diff --git a/dhcp6/conn.go b/dhcp6/conn.go index 2ca37fc..6f4ab9e 100644 --- a/dhcp6/conn.go +++ b/dhcp6/conn.go @@ -81,7 +81,7 @@ func InterfaceIndexByAddress(ifAddr string) (*net.Interface, error) { func (c *Conn) RecvDHCP() (*Packet, net.IP, error) { b := make([]byte, 1500) for { - packetSize, rcm, _, err := c.conn.ReadFrom(b) + _, rcm, _, err := c.conn.ReadFrom(b) if err != nil { return nil, nil, err } @@ -91,7 +91,10 @@ func (c *Conn) RecvDHCP() (*Packet, net.IP, error) { if !rcm.Dst.IsMulticast() || !rcm.Dst.Equal(c.group) { continue // unknown group, discard } - pkt := MakePacket(b, packetSize) + pkt, err := MakePacket(b) + if err != nil { + return nil, nil, err + } return pkt, rcm.Src, nil } diff --git a/dhcp6/options.go b/dhcp6/options.go index 5e278a3..94305fa 100644 --- a/dhcp6/options.go +++ b/dhcp6/options.go @@ -131,9 +131,13 @@ func (o *Option) Marshal() ([]byte, error) { } func (o Options) UnmarshalOptionRequestOption() map[uint16]bool { - oro_content := o[OptOro].Value to_ret := make(map[uint16]bool) + if o[OptOro] == nil { + return to_ret + } + + oro_content := o[OptOro].Value for i := 0; i < int(o[OptOro].Length)/2; i++ { to_ret[uint16(binary.BigEndian.Uint16(oro_content[i*2:(i+1)*2]))] = true } diff --git a/dhcp6/packet.go b/dhcp6/packet.go index 698be26..6f82d10 100644 --- a/dhcp6/packet.go +++ b/dhcp6/packet.go @@ -5,7 +5,6 @@ import ( "net" "encoding/binary" "bytes" - "golang.org/x/tools/go/gcimporter15/testdata" ) type MessageType uint8 @@ -32,7 +31,7 @@ type Packet struct { Options Options } -func MakePacket(bs []byte, len int) (*Packet, error) { +func MakePacket(bs []byte) (*Packet, error) { options, err := MakeOptions(bs[4:]) // 4:len? if err != nil { return nil, fmt.Errorf("packet has malformed options section: %s", err) @@ -58,9 +57,9 @@ func (p *Packet) Marshal() ([]byte, error) { func (p *Packet) BuildResponse(serverDuid []byte) *Packet { transactionId := p.TransactionID - clientId := in_options[OptClientId].Value - iaNaId := in_options[OptIaNa].Value[0:4] - clientArchType := in_options[OptClientArchType].Value + clientId := p.Options[OptClientId].Value + iaNaId := p.Options[OptIaNa].Value[0:4] + clientArchType := p.Options[OptClientArchType].Value switch p.Type { case MsgSolicit: diff --git a/dhcp6/packet_test.go b/dhcp6/packet_test.go new file mode 100644 index 0000000..f7302d4 --- /dev/null +++ b/dhcp6/packet_test.go @@ -0,0 +1,329 @@ +package dhcp6 + +import ( + "testing" + "encoding/binary" +) + +func TestMakeMsgAdvertise(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + msg := MakeMsgAdvertise(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte("11")) + + if msg.Type != MsgAdvertise { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options[OptClientId] + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption.Value) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption.Value) + } + if len(expectedClientId) != len(clientIdOption.Value) { + t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption.Value)) + } + + serverIdOption := msg.Options[OptServerId] + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption.Value) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption.Value) + } + if len(expectedServerId) != len(serverIdOption.Value) { + t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption.Value)) + } + + bootfileUrlOption := msg.Options[OptBootfileUrl] + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + + iaNaOption := msg.Options[OptIaNa] + if iaNaOption == nil { + t.Fatalf("interface non-temporary association option should be present") + } +} + +func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + msg := MakeMsgAdvertise(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte{0x0, 0x10}) + + vendorClassOption := msg.Options[OptVendorClass] + if vendorClassOption == nil { + t.Fatalf("Vendor class option should be present") + } + + bootfileUrlOption := msg.Options[OptBootfileUrl] + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } +} + +func TestMakeMsgReply(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + msg := MakeMsgReply(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte("11")) + + if msg.Type != MsgReply { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options[OptClientId] + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption.Value) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption.Value) + } + if len(expectedClientId) != len(clientIdOption.Value) { + t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption.Value)) + } + + serverIdOption := msg.Options[OptServerId] + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption.Value) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption.Value) + } + if len(expectedServerId) != len(serverIdOption.Value) { + t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption.Value)) + } + + bootfileUrlOption := msg.Options[OptBootfileUrl] + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + + iaNaOption := msg.Options[OptIaNa] + if iaNaOption == nil { + t.Fatalf("interface non-temporary association option should be present") + } +} + +func TestMakeMsgReplyWithHttpClientArch(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + msg := MakeMsgReply(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte{0x0, 0x10}) + + vendorClassOption := msg.Options[OptVendorClass] + if vendorClassOption == nil { + t.Fatalf("Vendor class option should be present") + } + + bootfileUrlOption := msg.Options[OptBootfileUrl] + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } +} + +func TestMakeMsgInformationRequestReply(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + msg := MakeMsgInformationRequestReply(transactionId, expectedServerId, expectedClientId, []byte("11")) + + + if msg.Type != MsgReply { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options[OptClientId] + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption.Value) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption.Value) + } + if len(expectedClientId) != len(clientIdOption.Value) { + t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption.Value)) + } + + serverIdOption := msg.Options[OptServerId] + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption.Value) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption.Value) + } + if len(expectedServerId) != len(serverIdOption.Value) { + t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption.Value)) + } + + bootfileUrlOption := msg.Options[OptBootfileUrl] + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } +} + +func TestMakeMsgInformationRequestReplyWithHttpClientArch(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + msg := MakeMsgInformationRequestReply(transactionId, expectedServerId, expectedClientId, []byte{0x0, 0x10}) + + vendorClassOption := msg.Options[OptVendorClass] + if vendorClassOption == nil { + t.Fatalf("Vendor class option should be present") + } + + bootfileUrlOption := msg.Options[OptBootfileUrl] + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } +} + +func TestMakeMsgReleaseReply(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + msg := MakeMsgReleaseReply(transactionId, expectedServerId, expectedClientId) + + if msg.Type != MsgReply { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options[OptClientId] + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption.Value) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption.Value) + } + if len(expectedClientId) != len(clientIdOption.Value) { + t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption.Value)) + } + + serverIdOption := msg.Options[OptServerId] + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption.Value) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption.Value) + } + if len(expectedServerId) != len(serverIdOption.Value) { + t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption.Value)) + } +} + +func TestShouldDiscardSolicitWithoutBootfileUrlOption(t *testing.T) { + clientId := []byte("clientid") + options := make(Options) + options.AddOption(&Option{Id: OptClientId, Length: uint16(len(clientId)), Value: clientId}) + solicit := &Packet{Type: MsgSolicit, TransactionID: [3]byte{'1', '2', '3'}, Options: options} + + if err := ShouldDiscardSolicit(solicit); err == nil { + t.Fatalf("Should discard solicit packet without bootfile url option, but didn't") + } +} + +func TestShouldDiscardSolicitWithoutClientIdOption(t *testing.T) { + options := make(Options) + options.AddOption(MakeOptionRequestOptions([]uint16{OptBootfileUrl})) + solicit := &Packet{Type: MsgSolicit, TransactionID: [3]byte{'1', '2', '3'}, Options: options} + + if err := ShouldDiscardSolicit(solicit); err == nil { + t.Fatalf("Should discard solicit packet without client id option, but didn't") + } +} + +func TestShouldDiscardSolicitWithServerIdOption(t *testing.T) { + serverId := []byte("serverid") + clientId := []byte("clientid") + options := make(Options) + options.AddOption(MakeOptionRequestOptions([]uint16{OptBootfileUrl})) + options.AddOption(&Option{Id: OptClientId, Length: uint16(len(clientId)), Value: clientId}) + options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverId)), Value: serverId}) + solicit := &Packet{Type: MsgSolicit, TransactionID: [3]byte{'1', '2', '3'}, Options: options} + + if err := ShouldDiscardSolicit(solicit); err == nil { + t.Fatalf("Should discard solicit packet with server id option, but didn't") + } +} + +func TestShouldDiscardRequestWithoutBootfileUrlOption(t *testing.T) { + serverId := []byte("serverid") + clientId := []byte("clientid") + options := make(Options) + options.AddOption(&Option{Id: OptClientId, Length: uint16(len(clientId)), Value: clientId}) + options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverId)), Value: serverId}) + request := &Packet{Type: MsgRequest, TransactionID: [3]byte{'1', '2', '3'}, Options: options} + + if err := ShouldDiscardRequest(request, serverId); err == nil { + t.Fatalf("Should discard request packet without bootfile url option, but didn't") + } +} + +func TestShouldDiscardRequestWithoutClientIdOption(t *testing.T) { + serverId := []byte("serverid") + options := make(Options) + options.AddOption(MakeOptionRequestOptions([]uint16{OptBootfileUrl})) + options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverId)), Value: serverId}) + request := &Packet{Type: MsgRequest, TransactionID: [3]byte{'1', '2', '3'}, Options: options} + + if err := ShouldDiscardRequest(request, serverId); err == nil { + t.Fatalf("Should discard request packet without client id option, but didn't") + } +} + +func TestShouldDiscardRequestWithoutServerIdOption(t *testing.T) { + clientId := []byte("clientid") + options := make(Options) + options.AddOption(MakeOptionRequestOptions([]uint16{OptBootfileUrl})) + options.AddOption(&Option{Id: OptClientId, Length: uint16(len(clientId)), Value: clientId}) + request := &Packet{Type: MsgRequest, TransactionID: [3]byte{'1', '2', '3'}, Options: options} + + if err := ShouldDiscardRequest(request, []byte("serverid")); err == nil { + t.Fatalf("Should discard request packet with server id option, but didn't") + } +} + +func TestShouldDiscardRequestWithWrongServerId(t *testing.T) { + clientId := []byte("clientid") + serverId := []byte("serverid") + options := make(Options) + options.AddOption(MakeOptionRequestOptions([]uint16{OptBootfileUrl})) + options.AddOption(&Option{Id: OptClientId, Length: uint16(len(clientId)), Value: clientId}) + options.AddOption(&Option{Id: OptServerId, Length: uint16(len(serverId)), Value: serverId}) + request := &Packet{Type: MsgRequest, TransactionID: [3]byte{'1', '2', '3'}, Options: options} + + if err := ShouldDiscardRequest(request, []byte("wrongid")); err == nil { + t.Fatalf("Should discard request packet with wrong server id option, but didn't") + } +} +func MakeOptionRequestOptions(options []uint16) *Option { + value := make([]byte, len(options)*2) + for i, option := range(options) { + binary.BigEndian.PutUint16(value[i*2:], option) + } + + return &Option{Id: OptOro, Length: uint16(len(options)*2), Value: value} +} +