diff options
author | Andrea Barberio <insomniac@slackware.it> | 2017-12-06 20:29:55 +0000 |
---|---|---|
committer | Andrea Barberio <insomniac@slackware.it> | 2017-12-06 20:29:55 +0000 |
commit | 3874bd73f93d2abdfba04aa5821d62c37acd1f2f (patch) | |
tree | bbed5dd3ec2aaa5ebb28e076bc387487f39e4c6f /dhcpv6 | |
parent | 70a90992790d76f3a5d27fbbef985cdf1ef46a80 (diff) |
Added support for DHCP relay and refactored into multiple files
Diffstat (limited to 'dhcpv6')
-rw-r--r-- | dhcpv6/dhcpv6.go | 197 | ||||
-rw-r--r-- | dhcpv6/dhcpv6message.go | 180 | ||||
-rw-r--r-- | dhcpv6/dhcpv6relay.go | 50 | ||||
-rw-r--r-- | dhcpv6/types.go | 9 |
4 files changed, 247 insertions, 189 deletions
diff --git a/dhcpv6/dhcpv6.go b/dhcpv6/dhcpv6.go index dcd1ff0..7667821 100644 --- a/dhcpv6/dhcpv6.go +++ b/dhcpv6/dhcpv6.go @@ -1,76 +1,16 @@ package dhcpv6 import ( - "crypto/rand" - "encoding/binary" "fmt" "github.com/insomniacslk/dhcp/dhcpv6/options" - "github.com/insomniacslk/dhcp/iana" - "log" - "net" - "time" ) -const MessageHeaderSize = 4 -const RelayMessageHeaderSize = 34 - type DHCPv6 interface { Type() MessageType ToBytes() []byte Summary() string } -type DHCPv6Message struct { - messageType MessageType - transactionID uint32 // only 24 bits are used though - options []options.Option -} - -type DHCPv6RelayMessage struct { - messageType MessageType - hopCount uint8 - linkAddr net.IP - peerAddr net.IP -} - -func BytesToTransactionID(data []byte) (*uint32, error) { - // return a uint32 from a sequence of bytes, representing a transaction ID. - // Transaction IDs are three-bytes long. If the provided data is shorter than - // 3 bytes, it return an error. If longer, will use the first three bytes - // only. - if len(data) < 3 { - return nil, fmt.Errorf("Invalid transaction ID: less than 3 bytes") - } - buf := make([]byte, 4) - copy(buf[1:4], data[:3]) - tid := binary.BigEndian.Uint32(buf) - return &tid, nil -} - -func GenerateTransactionID() (*uint32, error) { - var tid *uint32 - for { - tidBytes := make([]byte, 4) - n, err := rand.Read(tidBytes) - if n != 4 { - return nil, fmt.Errorf("Invalid random sequence: shorter than 4 bytes") - } - tid, err = BytesToTransactionID(tidBytes) - if err != nil { - return nil, err - } - if tid == nil { - return nil, fmt.Errorf("Error: got a nil Transaction ID") - } - // retry until != 0 - // TODO add retry limit - if *tid != 0 { - break - } - } - return tid, nil -} - func FromBytes(data []byte) (DHCPv6, error) { var ( isRelay = false @@ -81,7 +21,7 @@ func FromBytes(data []byte) (DHCPv6, error) { isRelay = true } if isRelay { - headerSize = RelayMessageHeaderSize + headerSize = RelayHeaderSize } else { headerSize = MessageHeaderSize } @@ -89,14 +29,20 @@ func FromBytes(data []byte) (DHCPv6, error) { return nil, fmt.Errorf("Invalid header size: shorter than %v bytes", headerSize) } if isRelay { - return nil, fmt.Errorf("Relay messages not implemented yet") + d := DHCPv6Relay{ + messageType: messageType, + hopCount: uint8(data[1]), + linkAddr: append(data[2:18]), + peerAddr: append(data[18:34]), + } + return &d, nil } else { tid, err := BytesToTransactionID(data[1:4]) if err != nil { return nil, err } d := DHCPv6Message{ - messageType: MessageType(data[0]), + messageType: messageType, transactionID: *tid, } options, err := options.FromBytes(data[4:]) @@ -119,128 +65,3 @@ func NewMessage() (*DHCPv6Message, error) { } return &d, nil } - -// Return a time integer suitable for DUID-LLT, i.e. the current time counted in -// seconds since January 1st, 2000, midnight UTC, modulo 2^32 -func GetTime() uint32 { - now := time.Since(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)) - return uint32((now.Nanoseconds() / 1000000000) % 0xffffffff) -} - -// Create a new SOLICIT message with DUID-LLT, using the given network -// interface's hardware address and current time -func NewSolicitForInterface(ifname string) (*DHCPv6Message, error) { - d, err := NewMessage() - if err != nil { - return nil, err - } - d.SetMessage(SOLICIT) - iface, err := net.InterfaceByName(ifname) - if err != nil { - return nil, err - } - cid := options.OptClientId{} - cid.SetClientID(options.Duid{ - Type: options.DUID_LLT, - HwType: iana.HwTypeEthernet, - Time: GetTime(), - LinkLayerAddr: iface.HardwareAddr, - }) - - d.AddOption(&cid) - oro := options.OptRequestedOption{} - oro.SetRequestedOptions([]options.OptionCode{ - options.DNS_RECURSIVE_NAME_SERVER, - options.DOMAIN_SEARCH_LIST, - }) - d.AddOption(&oro) - d.AddOption(&options.OptElapsedTime{}) - // FIXME use real values for IA_NA - iaNa := options.OptIANA{} - iaNa.SetIAID([4]byte{0x27, 0xfe, 0x8f, 0x95}) - iaNa.SetT1(0xe10) - iaNa.SetT2(0x1518) - d.AddOption(&iaNa) - return d, nil -} - -func (d *DHCPv6Message) Type() MessageType { - return d.messageType -} - -func (d *DHCPv6Message) SetMessage(messageType MessageType) { - if MessageToString[messageType] == "" { - log.Printf("Warning: unknown DHCPv6 message type: %v", messageType) - } - d.messageType = messageType -} - -func (d *DHCPv6Message) MessageToString() string { - if m := MessageToString[d.messageType]; m != "" { - return m - } - return "Invalid" -} - -func (d *DHCPv6Message) TransactionID() uint32 { - return d.transactionID -} - -func (d *DHCPv6Message) SetTransactionID(tid uint32) { - ttid := tid & 0x00ffffff - if ttid != tid { - log.Printf("Warning: truncating transaction ID that is longer than 24 bits: %v", tid) - } - d.transactionID = ttid -} - -func (d *DHCPv6Message) Options() []options.Option { - return d.options -} - -func (d *DHCPv6Message) SetOptions(options []options.Option) { - d.options = options -} - -func (d *DHCPv6Message) AddOption(option options.Option) { - d.options = append(d.options, option) -} - -func (d *DHCPv6Message) String() string { - return fmt.Sprintf("DHCPv6Message(messageType=%v transactionID=0x%06x, %d options)", - d.MessageToString(), d.TransactionID(), len(d.options), - ) -} - -func (d *DHCPv6Message) Summary() string { - ret := fmt.Sprintf( - "DHCPv6Message\n"+ - " messageType=%v\n"+ - " transactionid=0x%06x\n", - d.MessageToString(), - d.TransactionID(), - ) - ret += " options=[" - if len(d.options) > 0 { - ret += "\n" - } - for _, opt := range d.options { - ret += fmt.Sprintf(" %v\n", opt.String()) - } - ret += " ]\n" - return ret -} - -// Convert a DHCPv6Message structure into its binary representation, suitable for being -// sent over the network -func (d *DHCPv6Message) ToBytes() []byte { - var ret []byte - ret = append(ret, byte(d.messageType)) - tidBytes := make([]byte, 4) - binary.BigEndian.PutUint32(tidBytes, d.transactionID) - ret = append(ret, tidBytes[1:4]...) // discard the first byte - for _, opt := range d.options { - ret = append(ret, opt.ToBytes()...) - } - return ret -} diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go new file mode 100644 index 0000000..4461c50 --- /dev/null +++ b/dhcpv6/dhcpv6message.go @@ -0,0 +1,180 @@ +package dhcpv6 + +import ( + "crypto/rand" + "encoding/binary" + "fmt" + "github.com/insomniacslk/dhcp/dhcpv6/options" + "github.com/insomniacslk/dhcp/iana" + "log" + "net" + "time" +) + +const MessageHeaderSize = 4 + +type DHCPv6Message struct { + messageType MessageType + transactionID uint32 // only 24 bits are used though + options []options.Option +} + +func BytesToTransactionID(data []byte) (*uint32, error) { + // return a uint32 from a sequence of bytes, representing a transaction ID. + // Transaction IDs are three-bytes long. If the provided data is shorter than + // 3 bytes, it return an error. If longer, will use the first three bytes + // only. + if len(data) < 3 { + return nil, fmt.Errorf("Invalid transaction ID: less than 3 bytes") + } + buf := make([]byte, 4) + copy(buf[1:4], data[:3]) + tid := binary.BigEndian.Uint32(buf) + return &tid, nil +} + +func GenerateTransactionID() (*uint32, error) { + var tid *uint32 + for { + tidBytes := make([]byte, 4) + n, err := rand.Read(tidBytes) + if n != 4 { + return nil, fmt.Errorf("Invalid random sequence: shorter than 4 bytes") + } + tid, err = BytesToTransactionID(tidBytes) + if err != nil { + return nil, err + } + if tid == nil { + return nil, fmt.Errorf("Error: got a nil Transaction ID") + } + // retry until != 0 + // TODO add retry limit + if *tid != 0 { + break + } + } + return tid, nil +} + +// Return a time integer suitable for DUID-LLT, i.e. the current time counted in +// seconds since January 1st, 2000, midnight UTC, modulo 2^32 +func GetTime() uint32 { + now := time.Since(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)) + return uint32((now.Nanoseconds() / 1000000000) % 0xffffffff) +} + +// Create a new SOLICIT message with DUID-LLT, using the given network +// interface's hardware address and current time +func NewSolicitForInterface(ifname string) (*DHCPv6Message, error) { + d, err := NewMessage() + if err != nil { + return nil, err + } + d.SetMessage(SOLICIT) + iface, err := net.InterfaceByName(ifname) + if err != nil { + return nil, err + } + cid := options.OptClientId{} + cid.SetClientID(options.Duid{ + Type: options.DUID_LLT, + HwType: iana.HwTypeEthernet, + Time: GetTime(), + LinkLayerAddr: iface.HardwareAddr, + }) + + d.AddOption(&cid) + oro := options.OptRequestedOption{} + oro.SetRequestedOptions([]options.OptionCode{ + options.DNS_RECURSIVE_NAME_SERVER, + options.DOMAIN_SEARCH_LIST, + }) + d.AddOption(&oro) + d.AddOption(&options.OptElapsedTime{}) + // FIXME use real values for IA_NA + iaNa := options.OptIANA{} + iaNa.SetIAID([4]byte{0x27, 0xfe, 0x8f, 0x95}) + iaNa.SetT1(0xe10) + iaNa.SetT2(0x1518) + d.AddOption(&iaNa) + return d, nil +} + +func (d *DHCPv6Message) Type() MessageType { + return d.messageType +} + +func (d *DHCPv6Message) SetMessage(messageType MessageType) { + if d.MessageTypeToString() == "" { + log.Printf("Warning: unknown DHCPv6 message type: %v", messageType) + } + d.messageType = messageType +} + +func (d *DHCPv6Message) MessageTypeToString() string { + return MessageTypeToString(d.messageType) +} + +func (d *DHCPv6Message) TransactionID() uint32 { + return d.transactionID +} + +func (d *DHCPv6Message) SetTransactionID(tid uint32) { + ttid := tid & 0x00ffffff + if ttid != tid { + log.Printf("Warning: truncating transaction ID that is longer than 24 bits: %v", tid) + } + d.transactionID = ttid +} + +func (d *DHCPv6Message) Options() []options.Option { + return d.options +} + +func (d *DHCPv6Message) SetOptions(options []options.Option) { + d.options = options +} + +func (d *DHCPv6Message) AddOption(option options.Option) { + d.options = append(d.options, option) +} + +func (d *DHCPv6Message) String() string { + return fmt.Sprintf("DHCPv6Message(messageType=%v transactionID=0x%06x, %d options)", + d.MessageTypeToString(), d.TransactionID(), len(d.options), + ) +} + +func (d *DHCPv6Message) Summary() string { + ret := fmt.Sprintf( + "DHCPv6Message\n"+ + " messageType=%v\n"+ + " transactionid=0x%06x\n", + d.MessageTypeToString(), + d.TransactionID(), + ) + ret += " options=[" + if len(d.options) > 0 { + ret += "\n" + } + for _, opt := range d.options { + ret += fmt.Sprintf(" %v\n", opt.String()) + } + ret += " ]\n" + return ret +} + +// Convert a DHCPv6Message structure into its binary representation, suitable for being +// sent over the network +func (d *DHCPv6Message) ToBytes() []byte { + var ret []byte + ret = append(ret, byte(d.messageType)) + tidBytes := make([]byte, 4) + binary.BigEndian.PutUint32(tidBytes, d.transactionID) + ret = append(ret, tidBytes[1:4]...) // discard the first byte + for _, opt := range d.options { + ret = append(ret, opt.ToBytes()...) + } + return ret +} diff --git a/dhcpv6/dhcpv6relay.go b/dhcpv6/dhcpv6relay.go new file mode 100644 index 0000000..25cf658 --- /dev/null +++ b/dhcpv6/dhcpv6relay.go @@ -0,0 +1,50 @@ +package dhcpv6 + +import ( + "fmt" + "net" +) + +const RelayHeaderSize = 34 + +type DHCPv6Relay struct { + messageType MessageType + hopCount uint8 + linkAddr net.IP + peerAddr net.IP + payload []byte // TODO implement relay payload +} + +func (r *DHCPv6Relay) Type() MessageType { + return r.messageType +} + +func (r *DHCPv6Relay) MessageTypeToString() string { + return MessageTypeToString(r.messageType) +} + +func (r *DHCPv6Relay) Summary() string { + ret := fmt.Sprintf( + "DHCPv6Relay\n"+ + " messageType=%v\n"+ + " hopcount=%v\n"+ + " linkaddr=%v\n"+ + " peeraddr=%v\n", + r.MessageTypeToString(), + r.hopCount, + r.linkAddr, + r.peerAddr, + ) + return ret +} + +func (r *DHCPv6Relay) ToBytes() []byte { + ret := make([]byte, RelayHeaderSize) + ret[0] = byte(r.messageType) + ret[1] = byte(r.hopCount) + copy(ret[2:18], r.peerAddr) + copy(ret[18:34], r.linkAddr) + ret = append(ret, r.payload...) + + return ret +} diff --git a/dhcpv6/types.go b/dhcpv6/types.go index 370243e..e0ea6f0 100644 --- a/dhcpv6/types.go +++ b/dhcpv6/types.go @@ -25,7 +25,14 @@ const ( LEASEQUERY_DATA ) -var MessageToString = map[MessageType]string{ +func MessageTypeToString(t MessageType) string { + if m := MessageTypeToStringMap[t]; m != "" { + return m + } + return "Unknown" +} + +var MessageTypeToStringMap = map[MessageType]string{ SOLICIT: "SOLICIT", ADVERTISE: "ADVERTISE", REQUEST: "REQUEST", |