Added more docs

This commit is contained in:
Dmitri Dolguikh 2017-11-03 11:10:27 -07:00 committed by Dave Anderson
parent 5d5adcf2b9
commit 13846a9809
5 changed files with 88 additions and 30 deletions

View File

@ -5,7 +5,7 @@ import (
"time" "time"
) )
// Associates an ip address with an individual network interface of a client // IdentityAssociation associates an ip address with a network interface of a client
type IdentityAssociation struct { type IdentityAssociation struct {
IPAddress net.IP IPAddress net.IP
ClientID []byte ClientID []byte
@ -13,7 +13,7 @@ type IdentityAssociation struct {
CreatedAt time.Time CreatedAt time.Time
} }
// Keeps track of assigned and available ip address in an address pool // AddressPool keeps track of assigned and available ip address in an address pool
type AddressPool interface { type AddressPool interface {
ReserveAddresses(clientID []byte, interfaceIds [][]byte) ([]*IdentityAssociation, error) ReserveAddresses(clientID []byte, interfaceIds [][]byte) ([]*IdentityAssociation, error)
ReleaseAddresses(clientID []byte, interfaceIds [][]byte) ReleaseAddresses(clientID []byte, interfaceIds [][]byte)

View File

@ -66,8 +66,8 @@ type APIBootConfiguration struct {
UsePreference bool UsePreference bool
} }
// MakeApiBootConfiguration creates a new APIBootConfiguration initialized with provided values // MakeAPIBootConfiguration creates a new APIBootConfiguration initialized with provided values
func MakeApiBootConfiguration(url string, timeout time.Duration, preference uint8, usePreference bool, func MakeAPIBootConfiguration(url string, timeout time.Duration, preference uint8, usePreference bool,
dnsServerAddresses []net.IP) *APIBootConfiguration { dnsServerAddresses []net.IP) *APIBootConfiguration {
if !strings.HasSuffix(url, "/") { if !strings.HasSuffix(url, "/") {
url += "/" url += "/"

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
) )
// Conn is dhcpv6-specific socket
type Conn struct { type Conn struct {
conn *ipv6.PacketConn conn *ipv6.PacketConn
group net.IP group net.IP
@ -14,6 +15,7 @@ type Conn struct {
listenPort string listenPort string
} }
// NewConn creates a new Conn bound to specified address and port
func NewConn(addr, port string) (*Conn, error) { func NewConn(addr, port string) (*Conn, error) {
ifi, err := InterfaceByAddress(addr) ifi, err := InterfaceByAddress(addr)
if err != nil { if err != nil {
@ -45,10 +47,12 @@ func NewConn(addr, port string) (*Conn, error) {
}, nil }, nil
} }
// Close closes Conn
func (c *Conn) Close() error { func (c *Conn) Close() error {
return c.conn.Close() return c.conn.Close()
} }
// InterfaceByAddress finds the interface bound to an ip address, or returns an error if none were found
func InterfaceByAddress(ifAddr string) (*net.Interface, error) { func InterfaceByAddress(ifAddr string) (*net.Interface, error) {
allIfis, err := net.Interfaces() allIfis, err := net.Interfaces()
if err != nil { if err != nil {
@ -80,6 +84,7 @@ func addrToIP(a net.Addr) net.IP {
return ip return ip
} }
// RecvDHCP reads next available dhcp packet from Conn
func (c *Conn) RecvDHCP() (*Packet, net.IP, error) { func (c *Conn) RecvDHCP() (*Packet, net.IP, error) {
b := make([]byte, 1500) b := make([]byte, 1500)
for { for {
@ -102,6 +107,7 @@ func (c *Conn) RecvDHCP() (*Packet, net.IP, error) {
} }
} }
// SendDHCP sends a dhcp packet to the specified ip address using Conn
func (c *Conn) SendDHCP(dst net.IP, p []byte) error { func (c *Conn) SendDHCP(dst net.IP, p []byte) error {
dstAddr, err := net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%s", dst.String() + "%en0", "546")) dstAddr, err := net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%s", dst.String() + "%en0", "546"))
if err != nil { if err != nil {
@ -114,6 +120,7 @@ func (c *Conn) SendDHCP(dst net.IP, p []byte) error {
return nil return nil
} }
// SourceHardwareAddress returns hardware address of the interface used by Conn
func (c *Conn) SourceHardwareAddress() net.HardwareAddr { func (c *Conn) SourceHardwareAddress() net.HardwareAddr {
return c.ifi.HardwareAddr return c.ifi.HardwareAddr
} }

View File

@ -7,43 +7,69 @@ import (
"net" "net"
) )
// DHCPv6 option IDs
const ( const (
OptClientID uint16 = 1 // IPMask // Client ID Option
OptServerID = 2 // int32 OptClientID uint16 = 1
OptIaNa = 3 // IPs // Server ID Option
OptIaTa = 4 // IPs OptServerID = 2
OptIaAddr = 5 // string // Identity Association for Non-temporary Addresses Option
OptOro = 6 // uint16 OptIaNa = 3
OptPreference = 7 // string // Identity Association for Temporary Addresses Option
OptElapsedTime = 8 // IP OptIaTa = 4
OptRelayMessage = 9 // IP // IA Address Option
OptAuth = 11 // []byte OptIaAddr = 5
OptUnicast = 12 // IP // Option Request Option
OptStatusCode = 13 // uint32 OptOro = 6
OptRapidCommit = 14 // byte // Preference Option
OptUserClass = 15 // IP OptPreference = 7
OptVendorClass = 16 // []byte // Elapsed Time Option
OptVendorOpts = 17 // string OptElapsedTime = 8
OptInterfaceID = 18 // uint16 // Relay Message Option
OptReconfMsg = 19 // uint32 OptRelayMessage = 9
OptReconfAccept = 20 // uint32 // Authentication Option
OptRecursiveDNS = 23 // []byte OptAuth = 11
// Server Unicast Option
OptUnicast = 12
// Status Code Option
OptStatusCode = 13
// Rapid Commit Option
OptRapidCommit = 14
// User Class Option
OptUserClass = 15
// Vendor Class Option
OptVendorClass = 16
// Vendor-specific Information Option
OptVendorOpts = 17
// Interface-Id Option
OptInterfaceID = 18
// Reconfigure Message Option
OptReconfMsg = 19
// Reconfigure Accept Option
OptReconfAccept = 20
// Recursive DNS name servers Option
OptRecursiveDNS = 23
// Boot File URL Option
OptBootfileURL = 59 OptBootfileURL = 59
OptBootfileParam = 60 //[][]byte // Boot File Parameters Option
OptClientArchType = 61 //[][]byte, sent by the Client OptBootfileParam = 60
// 24? Domain search list // Client Architecture Type Option
OptClientArchType = 61
) )
// Option represents a DHCPv6 Option
type Option struct { type Option struct {
ID uint16 ID uint16
Length uint16 Length uint16
Value []byte Value []byte
} }
// MakeOption creates an Option with given ID and value
func MakeOption(id uint16, value []byte) *Option { func MakeOption(id uint16, value []byte) *Option {
return &Option{ ID: id, Length: uint16(len(value)), Value: value} return &Option{ ID: id, Length: uint16(len(value)), Value: value}
} }
// Options contains all options of a DHCPv6 packet
type Options map[uint16][]*Option type Options map[uint16][]*Option
func MakeOptions(bs []byte) (Options, error) { func MakeOptions(bs []byte) (Options, error) {
@ -59,6 +85,7 @@ func MakeOptions(bs []byte) (Options, error) {
return ret, nil return ret, nil
} }
// UnmarshalOption de-serializes an Option
func UnmarshalOption(bs []byte) (*Option, error) { func UnmarshalOption(bs []byte) (*Option, error) {
optionLength := uint16(binary.BigEndian.Uint16(bs[2:4])) optionLength := uint16(binary.BigEndian.Uint16(bs[2:4]))
optionId := uint16(binary.BigEndian.Uint16(bs[0:2])) optionId := uint16(binary.BigEndian.Uint16(bs[0:2]))
@ -79,13 +106,14 @@ func UnmarshalOption(bs []byte) (*Option, error) {
return &Option{ ID: optionId, Length: optionLength, Value: bs[4 : 4+optionLength]}, nil return &Option{ ID: optionId, Length: optionLength, Value: bs[4 : 4+optionLength]}, nil
} }
// HumanReadable presents DHCPv6 options in a human-readable form
func (o Options) HumanReadable() []string { func (o Options) HumanReadable() []string {
to_ret := make([]string, 0, len(o)) to_ret := make([]string, 0, len(o))
for _, multipleOptions := range(o) { for _, multipleOptions := range(o) {
for _, option := range(multipleOptions) { for _, option := range(multipleOptions) {
switch option.ID { switch option.ID {
case 3: case 3:
to_ret = append(to_ret, o.HumanReadableIaNa(*option)...) to_ret = append(to_ret, o.humanReadableIaNa(*option)...)
default: default:
to_ret = append(to_ret, fmt.Sprintf("Option: %d | %d | %d | %s\n", option.ID, option.Length, option.Value, option.Value)) to_ret = append(to_ret, fmt.Sprintf("Option: %d | %d | %d | %s\n", option.ID, option.Length, option.Value, option.Value))
} }
@ -94,7 +122,7 @@ func (o Options) HumanReadable() []string {
return to_ret return to_ret
} }
func (o Options) HumanReadableIaNa(opt Option) []string { func (o Options) humanReadableIaNa(opt Option) []string {
to_ret := make([]string, 0) to_ret := make([]string, 0)
to_ret = append(to_ret, fmt.Sprintf("Option: OptIaNa | len %d | iaid %x | t1 %d | t2 %d\n", to_ret = append(to_ret, fmt.Sprintf("Option: OptIaNa | len %d | iaid %x | t1 %d | t2 %d\n",
opt.Length, opt.Value[0:4], binary.BigEndian.Uint32(opt.Value[4:8]), binary.BigEndian.Uint32(opt.Value[8:12]))) opt.Length, opt.Value[0:4], binary.BigEndian.Uint32(opt.Value[4:8]), binary.BigEndian.Uint32(opt.Value[8:12])))
@ -126,6 +154,7 @@ func (o Options) HumanReadableIaNa(opt Option) []string {
return to_ret return to_ret
} }
// AddOption adds an option to Options
func (o Options) AddOption(option *Option) { func (o Options) AddOption(option *Option) {
_, present := o[option.ID]; if !present { _, present := o[option.ID]; if !present {
o[option.ID] = make([]*Option, 0) o[option.ID] = make([]*Option, 0)
@ -133,6 +162,9 @@ func (o Options) AddOption(option *Option) {
o[option.ID] = append(o[option.ID], option) o[option.ID] = append(o[option.ID], option)
} }
// MakeIaNaOption creates a Identity Association for Non-temporary Addresses Option
// with specified interface ID, t1 and t2 times, and an interface-specific option
// (an IA Address Option or a Status Option)
func MakeIaNaOption(iaid []byte, t1, t2 uint32, iaOption *Option) *Option { func MakeIaNaOption(iaid []byte, t1, t2 uint32, iaOption *Option) *Option {
serializedIaOption, _ := iaOption.Marshal() serializedIaOption, _ := iaOption.Marshal()
value := make([]byte, 12 + len(serializedIaOption)) value := make([]byte, 12 + len(serializedIaOption))
@ -143,6 +175,8 @@ func MakeIaNaOption(iaid []byte, t1, t2 uint32, iaOption *Option) *Option {
return MakeOption(OptIaNa, value) return MakeOption(OptIaNa, value)
} }
// MakeIaAddrOption creates an IA Address Option using IP address,
// preferred and valid lifetimes
func MakeIaAddrOption(addr net.IP, preferredLifetime, validLifetime uint32) *Option { func MakeIaAddrOption(addr net.IP, preferredLifetime, validLifetime uint32) *Option {
value := make([]byte, 24) value := make([]byte, 24)
copy(value[0:], addr) copy(value[0:], addr)
@ -151,6 +185,7 @@ func MakeIaAddrOption(addr net.IP, preferredLifetime, validLifetime uint32) *Opt
return MakeOption(OptIaAddr, value) return MakeOption(OptIaAddr, value)
} }
// MakeStatusOption creates a Status Option with given status code and message
func MakeStatusOption(statusCode uint16, message string) *Option { func MakeStatusOption(statusCode uint16, message string) *Option {
value := make([]byte, 2 + len(message)) value := make([]byte, 2 + len(message))
binary.BigEndian.PutUint16(value[0:], statusCode) binary.BigEndian.PutUint16(value[0:], statusCode)
@ -158,6 +193,7 @@ func MakeStatusOption(statusCode uint16, message string) *Option {
return MakeOption(OptStatusCode, value) return MakeOption(OptStatusCode, value)
} }
// MakeDNSServersOption creates a Recursive DNS servers Option with the specified list of IP addresses
func MakeDNSServersOption(addresses []net.IP) *Option { func MakeDNSServersOption(addresses []net.IP) *Option {
value := make([]byte, 16*len(addresses)) value := make([]byte, 16*len(addresses))
for i, dnsAddress := range addresses { for i, dnsAddress := range addresses {
@ -166,6 +202,7 @@ func MakeDNSServersOption(addresses []net.IP) *Option {
return MakeOption(OptRecursiveDNS, value) return MakeOption(OptRecursiveDNS, value)
} }
// Marshal serializes Options
func (o Options) Marshal() ([]byte, error) { func (o Options) Marshal() ([]byte, error) {
buffer := bytes.NewBuffer(make([]byte, 0, 1446)) buffer := bytes.NewBuffer(make([]byte, 0, 1446))
for _, multipleOptions := range(o) { for _, multipleOptions := range(o) {
@ -182,6 +219,7 @@ func (o Options) Marshal() ([]byte, error) {
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }
// Marshal serializes the Option
func (o *Option) Marshal() ([]byte, error) { func (o *Option) Marshal() ([]byte, error) {
buffer := bytes.NewBuffer(make([]byte, 0, o.Length + 2)) buffer := bytes.NewBuffer(make([]byte, 0, o.Length + 2))
@ -200,6 +238,7 @@ func (o *Option) Marshal() ([]byte, error) {
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }
// UnmarshalOptionRequestOption de-serializes Option Request Option
func (o Options) UnmarshalOptionRequestOption() map[uint16]bool { func (o Options) UnmarshalOptionRequestOption() map[uint16]bool {
ret := make(map[uint16]bool) ret := make(map[uint16]bool)
@ -214,37 +253,44 @@ func (o Options) UnmarshalOptionRequestOption() map[uint16]bool {
return ret return ret
} }
// HasBootFileURLOption returns true if Options contains Boot File URL Option
func (o Options) HasBootFileURLOption() bool { func (o Options) HasBootFileURLOption() bool {
requestedOptions := o.UnmarshalOptionRequestOption() requestedOptions := o.UnmarshalOptionRequestOption()
_, present := requestedOptions[OptBootfileURL] _, present := requestedOptions[OptBootfileURL]
return present return present
} }
// HasClientID returns true if Options contains Client ID Option
func (o Options) HasClientID() bool { func (o Options) HasClientID() bool {
_, present := o[OptClientID] _, present := o[OptClientID]
return present return present
} }
// HasServerID returns true if Options contains Server ID Option
func (o Options) HasServerID() bool { func (o Options) HasServerID() bool {
_, present := o[OptServerID] _, present := o[OptServerID]
return present return present
} }
// HasIaNa returns true oif Options contains Identity Association for Non-Temporary Addresses Option
func (o Options) HasIaNa() bool { func (o Options) HasIaNa() bool {
_, present := o[OptIaNa] _, present := o[OptIaNa]
return present return present
} }
// HasIaTa returns true if Options contains Identity Association for Temporary Addresses Option
func (o Options) HasIaTa() bool { func (o Options) HasIaTa() bool {
_, present := o[OptIaTa] _, present := o[OptIaTa]
return present return present
} }
// HasClientArchType returns true if Options contains Client Architecture Type Option
func (o Options) HasClientArchType() bool { func (o Options) HasClientArchType() bool {
_, present := o[OptClientArchType] _, present := o[OptClientArchType]
return present return present
} }
// ClientID returns the value in the Client ID Option or nil if the option doesn't exist
func (o Options) ClientID() []byte { func (o Options) ClientID() []byte {
opt, exists := o[OptClientID] opt, exists := o[OptClientID]
if exists { if exists {
@ -253,6 +299,7 @@ func (o Options) ClientID() []byte {
return nil return nil
} }
// ServerID returns the value in the Server ID Option or nil if the option doesn't exist
func (o Options) ServerID() []byte { func (o Options) ServerID() []byte {
opt, exists := o[OptServerID] opt, exists := o[OptServerID]
if exists { if exists {
@ -261,6 +308,8 @@ func (o Options) ServerID() []byte {
return nil return nil
} }
// IaNaIDs returns a list of interface IDs in all Identity Association for Non-Temporary Addresses Options,
// or an empty list if none exist
func (o Options) IaNaIDs() [][]byte { func (o Options) IaNaIDs() [][]byte {
options, exists := o[OptIaNa] options, exists := o[OptIaNa]
ret := make([][]byte, 0) ret := make([][]byte, 0)
@ -273,6 +322,7 @@ func (o Options) IaNaIDs() [][]byte {
return ret return ret
} }
// ClientArchType returns the value in the Client Architecture Type Option, or 0 if the option doesn't exist
func (o Options) ClientArchType() uint16 { func (o Options) ClientArchType() uint16 {
opt, exists := o[OptClientArchType] opt, exists := o[OptClientArchType]
if exists { if exists {
@ -281,6 +331,7 @@ func (o Options) ClientArchType() uint16 {
return 0 return 0
} }
// BootFileURL returns the value in the Boot File URL Option, or nil if the option doesn't exist
func (o Options) BootFileURL() []byte { func (o Options) BootFileURL() []byte {
opt, exists := o[OptBootfileURL] opt, exists := o[OptBootfileURL]
if exists { if exists {

View File

@ -59,7 +59,7 @@ var ipv6ApiCmd = &cobra.Command{
dnsServerAddresses = append(dnsServerAddresses, net.ParseIP(dnsServerAddress)) dnsServerAddresses = append(dnsServerAddresses, net.ParseIP(dnsServerAddress))
} }
} }
s.BootConfig = dhcp6.MakeApiBootConfiguration(apiURL, apiTimeout, preference, s.BootConfig = dhcp6.MakeAPIBootConfiguration(apiURL, apiTimeout, preference,
cmd.Flags().Changed("preference"), dnsServerAddresses) cmd.Flags().Changed("preference"), dnsServerAddresses)
addressPoolStart, err := cmd.Flags().GetString("address-pool-start") addressPoolStart, err := cmd.Flags().GetString("address-pool-start")