diff --git a/protocol/icmp.go b/protocol/icmp.go index 5b37d63..fc3ae67 100644 --- a/protocol/icmp.go +++ b/protocol/icmp.go @@ -33,14 +33,13 @@ func (i *ICMP) MarshalBinary() (data []byte, err error) { func (i *ICMP) UnmarshalBinary(data []byte) error { if len(data) < 4 { - return errors.New("The []byte is too short to unmarshal a full ARP message.") + return errors.New("The []byte is too short to unmarshal a full ICMP message.") } i.Type = data[0] i.Code = data[1] i.Checksum = binary.BigEndian.Uint16(data[2:4]) - for n := range data[4:] { - i.Data = append(i.Data, data[n]) - } + i.Data = make([]byte, len(data)-4) + copy(i.Data, data[4:]) return nil } diff --git a/protocol/ip.go b/protocol/ip.go index 925d478..aecf309 100644 --- a/protocol/ip.go +++ b/protocol/ip.go @@ -135,7 +135,10 @@ func (i *IPv4) UnmarshalBinary(data []byte) error { i.NWDst = data[n : n+4] n += 4 - i.Options.UnmarshalBinary(data[n:int(i.IHL*4)]) + err := i.Options.UnmarshalBinary(data[n:int(i.IHL*4)]) + if err != nil { + return err + } n += int(i.IHL*4) - n switch i.Protocol { diff --git a/protocol/ipv6.go b/protocol/ipv6.go new file mode 100644 index 0000000..ad37cd1 --- /dev/null +++ b/protocol/ipv6.go @@ -0,0 +1,372 @@ +package protocol + +import ( + "encoding/binary" + "errors" + "net" + + "github.com/contiv/libOpenflow/util" +) + +const ( + Type_HBH = 0x0 + Type_Routing = 0x2b + Type_Fragment = 0x2c +) + +type IPv6 struct { + Version uint8 //4-bits + TrafficClass uint8 + FlowLabel uint32 //20-bits + Length uint16 + NextHeader uint8 + HopLimit uint8 + NWSrc net.IP + NWDst net.IP + HbhHeader *HopByHopHeader + RoutingHeader *RoutingHeader + FragmentHeader *FragmentHeader + Data util.Message +} + +func (i *IPv6) Len() (n uint16) { + length := uint16(40) + if i.HbhHeader != nil { + length += i.HbhHeader.Len() + } + if i.RoutingHeader != nil { + length += i.RoutingHeader.Len() + } + if i.FragmentHeader != nil { + length += i.FragmentHeader.Len() + } + length += i.Data.Len() + return length +} + +func (i *IPv6) MarshalBinary() (data []byte, err error) { + data = make([]byte, int(i.Len())) + b := make([]byte, 0) + n := 0 + + ihl := (i.Version << 4) | (i.TrafficClass>>4)&0x0f + data[n] = ihl + n += 1 + data[n] = (i.TrafficClass<<4)&0xf0 | uint8(i.FlowLabel>>16) + n += 1 + binary.BigEndian.PutUint16(data[n:], uint16(i.FlowLabel)) + n += 2 + binary.BigEndian.PutUint16(data[n:], i.Length) + n += 2 + data[n] = i.NextHeader + n += 1 + data[n] = i.HopLimit + n += 1 + copy(data[n:], i.NWSrc) + n += 16 + copy(data[n:], i.NWDst) + n += 16 + + checkExtHeader := true + nxtHeader := i.NextHeader + for checkExtHeader { + var err error + var hBytes []byte + switch nxtHeader { + case Type_HBH: + checkExtHeader = true + nxtHeader = i.HbhHeader.NextHeader + hBytes, err = i.HbhHeader.MarshalBinary() + case Type_Routing: + checkExtHeader = true + nxtHeader = i.RoutingHeader.NextHeader + hBytes, err = i.RoutingHeader.MarshalBinary() + case Type_Fragment: + checkExtHeader = true + nxtHeader = i.FragmentHeader.NextHeader + hBytes, err = i.FragmentHeader.MarshalBinary() + default: + checkExtHeader = false + break + } + if err != nil { + return nil, err + } + copy(data[n:], hBytes) + n += len(hBytes) + } + + if i.Data != nil { + b, err = i.Data.MarshalBinary() + if err != nil { + return + } + copy(data[n:], b) + n += len(b) + } + return +} + +func (i *IPv6) UnmarshalBinary(data []byte) error { + if len(data) < 40 { + return errors.New("The []byte is too short to unmarshal a full IPv6 message.") + } + n := 0 + + var ihl uint8 + ihl = data[n] + i.Version = ihl >> 4 + tcLeft := (ihl & 0x0f) << 4 + n += 1 + tc := data[n] + i.TrafficClass = tcLeft | (tc >> 4) + n += 1 + i.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF + n += 2 + i.Length = binary.BigEndian.Uint16(data[n:]) + n += 2 + i.NextHeader = data[n] + n += 1 + i.HopLimit = data[n] + n += 1 + i.NWSrc = data[n : n+16] + n += 16 + i.NWDst = data[n : n+16] + n += 16 + + checkExtHeader := true + nxtHeader := i.NextHeader +checkXHeader: + for checkExtHeader { + switch nxtHeader { + case Type_HBH: + checkExtHeader = true + i.HbhHeader = NewHopByHopHeader() + err := i.HbhHeader.UnmarshalBinary(data[n:]) + if err != nil { + return err + } + nxtHeader = i.HbhHeader.NextHeader + n += int(i.HbhHeader.Len()) + case Type_Routing: + checkExtHeader = true + i.RoutingHeader = NewRoutingHeader() + err := i.RoutingHeader.UnmarshalBinary(data[n:]) + if err != nil { + return err + } + nxtHeader = i.RoutingHeader.NextHeader + n += int(i.RoutingHeader.Len()) + case Type_Fragment: + checkExtHeader = true + i.FragmentHeader = NewFragmentHeader() + err := i.FragmentHeader.UnmarshalBinary(data[n:]) + if err != nil { + return err + } + nxtHeader = i.FragmentHeader.NextHeader + n += int(i.FragmentHeader.Len()) + case Type_IPv6ICMP: + i.Data = NewICMP() + break checkXHeader + case Type_UDP: + i.Data = NewUDP() + break checkXHeader + default: + i.Data = new(util.Buffer) + break checkXHeader + } + } + return i.Data.UnmarshalBinary(data[n:]) +} + +type Option struct { + Type uint8 + Length uint8 + Data []byte +} + +func (o *Option) Len() uint16 { + return uint16(o.Length + 2) +} + +func (o *Option) MarshalBinary() (data []byte, err error) { + data = make([]byte, int(o.Len())) + n := 0 + data[n] = o.Type + n += 1 + data[n] = o.Length + n += 1 + copy(data[n:], o.Data) + return data, nil +} + +func (o *Option) UnmarshalBinary(data []byte) error { + n := 0 + o.Type = data[n] + n += 1 + o.Length = data[n] + n += 1 + if (len(data) - 2) < int(o.Length) { + return errors.New("The []byte is too short to unmarshal a full Option message.") + } + o.Data = make([]byte, o.Length) + copy(o.Data, data[n:n+int(o.Length)]) + return nil +} + +type HopByHopHeader struct { + NextHeader uint8 + HEL uint8 + Options []*Option +} + +func (h *HopByHopHeader) Len() uint16 { + return 8 * uint16(h.HEL+1) +} + +func (h *HopByHopHeader) MarshalBinary() (data []byte, err error) { + data = make([]byte, int(h.Len())) + n := 0 + data[n] = h.NextHeader + n += 1 + data[n] = h.HEL + n += 1 + for _, o := range h.Options { + ob, err := o.MarshalBinary() + if err != nil { + return data, err + } + copy(data[n:], ob) + n += int(o.Len()) + } + return data, nil +} + +func (h *HopByHopHeader) UnmarshalBinary(data []byte) error { + n := 0 + h.NextHeader = data[n] + n += 1 + h.HEL = data[n] + if len(data) < 8*int(h.HEL+1) { + return errors.New("The []byte is too short to unmarshal a full HopByHopHeader message.") + } + n += 1 + for n < int(h.Len()) { + o := new(Option) + err := o.UnmarshalBinary(data[n:]) + if err != nil { + return err + } + n += int(o.Len()) + h.Options = append(h.Options, o) + } + return nil +} + +func NewHopByHopHeader() *HopByHopHeader { + return new(HopByHopHeader) +} + +type RoutingHeader struct { + NextHeader uint8 + HEL uint8 + RoutingType uint8 + SegmentsLeft uint8 + Data *util.Buffer +} + +func (h *RoutingHeader) Len() uint16 { + return 8 * uint16(h.HEL+1) +} + +func (h *RoutingHeader) MarshalBinary() (data []byte, err error) { + data = make([]byte, int(h.Len())) + n := 0 + data[n] = h.NextHeader + n += 1 + data[n] = h.HEL + n += 1 + data[n] = h.RoutingType + n += 1 + data[n] = h.SegmentsLeft + n += 1 + copy(data[n:], h.Data.Bytes()) + return data, nil +} + +func (h *RoutingHeader) UnmarshalBinary(data []byte) error { + n := 0 + h.NextHeader = data[n] + n += 1 + h.HEL = data[n] + if len(data) < 8*int(h.HEL+1) { + return errors.New("The []byte is too short to unmarshal a full RoutingHeader message.") + } + n += 1 + h.RoutingType = data[n] + n += 1 + h.SegmentsLeft = data[n] + n += 1 + h.Data = new(util.Buffer) + err := h.Data.UnmarshalBinary(data[n:h.Len()]) + if err != nil { + return err + } + return nil +} + +func NewRoutingHeader() *RoutingHeader { + return new(RoutingHeader) +} + +type FragmentHeader struct { + NextHeader uint8 + Reserved uint8 + FragmentOffset uint16 + MoreFragments bool + Identification uint32 +} + +func (h *FragmentHeader) Len() uint16 { + return uint16(8) +} + +func (h *FragmentHeader) MarshalBinary() (data []byte, err error) { + data = make([]byte, int(h.Len())) + n := 0 + data[n] = h.NextHeader + n += 1 + data[n] = h.Reserved + n += 1 + fragment := h.FragmentOffset << 3 + if h.MoreFragments { + fragment |= uint16(1) + } + binary.BigEndian.PutUint16(data[n:], fragment) + n += 2 + binary.BigEndian.PutUint32(data[n:], h.Identification) + return data, nil +} + +func (h *FragmentHeader) UnmarshalBinary(data []byte) error { + if len(data) < int(h.Len()) { + return errors.New("The []byte is too short to unmarshal a full FragmentHeader message.") + } + n := 0 + h.NextHeader = data[n] + n += 1 + h.Reserved = data[n] + n += 1 + fragment := binary.BigEndian.Uint16(data[n:]) + n += 2 + h.FragmentOffset = fragment >> 3 + h.MoreFragments = (fragment & uint16(1)) == uint16(1) + h.Identification = binary.BigEndian.Uint32(data[n:]) + n += 4 + return nil +} + +func NewFragmentHeader() *FragmentHeader { + return new(FragmentHeader) +} diff --git a/protocol/ipv6_test.go b/protocol/ipv6_test.go new file mode 100644 index 0000000..5e52a31 --- /dev/null +++ b/protocol/ipv6_test.go @@ -0,0 +1,313 @@ +package protocol + +import ( + "bytes" + "encoding/binary" + "fmt" + "net" + "testing" + + "github.com/contiv/libOpenflow/util" +) + +func TestIPv6Option(t *testing.T) { + testFunc := func(oriMessage *Option) { + data, err := oriMessage.MarshalBinary() + if err != nil { + t.Fatalf("Failed to Marshal message: %v", err) + } + newMessage := new(Option) + err = newMessage.UnmarshalBinary(data) + if err != nil { + t.Fatalf("Failed to UnMarshal message: %v", err) + } + if err := testOptionEqual(oriMessage, newMessage); err != nil { + t.Error(err.Error()) + } + } + option := &Option{ + Type: 1, + Length: 4, + Data: []byte{0x00, 0x00, 0x00, 0x00}, + } + testFunc(option) +} + +func testOptionEqual(oriMessage, newMessage *Option) error { + if oriMessage.Type != newMessage.Type { + return fmt.Errorf("Option Type not equal") + } + if oriMessage.Length != newMessage.Length { + return fmt.Errorf("Option Length not equal") + } + if !bytes.Equal(oriMessage.Data, newMessage.Data) { + return fmt.Errorf("Option Data not equal") + } + return nil +} + +func TestHopByHopHeader(t *testing.T) { + testFunc := func(oriMessage *HopByHopHeader) { + data, err := oriMessage.MarshalBinary() + if err != nil { + t.Fatalf("Failed to Marshal message: %v", err) + } + newMessage := new(HopByHopHeader) + err = newMessage.UnmarshalBinary(data) + if err != nil { + t.Fatalf("Failed to UnMarshal message: %v", err) + } + if err := testHopByHopHeaderEqual(oriMessage, newMessage); err != nil { + t.Errorf(err.Error()) + } + } + msg := &HopByHopHeader{ + NextHeader: 59, + HEL: 0, + Options: []*Option{ + { + Type: 1, + Length: 4, + Data: []byte{0x00, 0x00, 0x00, 0x00}, + }, + }, + } + + testFunc(msg) +} + +func testHopByHopHeaderEqual(oriMessage, newMessage *HopByHopHeader) error { + if oriMessage.NextHeader != newMessage.NextHeader { + return fmt.Errorf("HopByHopHeader NextHeader not equal") + } + if oriMessage.HEL != newMessage.HEL { + return fmt.Errorf("HopByHopHeader HEL not equal") + } + if len(oriMessage.Options) != len(newMessage.Options) { + return fmt.Errorf("HopByHopHeader Options count not equal") + } + for i := range oriMessage.Options { + if err := testOptionEqual(oriMessage.Options[i], newMessage.Options[i]); err != nil { + return err + } + } + return nil +} + +func TestRoutingHeader(t *testing.T) { + testFunc := func(oriMessage *RoutingHeader) { + data, err := oriMessage.MarshalBinary() + if err != nil { + t.Fatalf("Failed to Marshal message: %v", err) + } + newMessage := new(RoutingHeader) + err = newMessage.UnmarshalBinary(data) + if err != nil { + t.Fatalf("Failed to UnMarshal message: %v", err) + } + if err := testRoutingHeaderEqual(oriMessage, newMessage); err != nil { + t.Errorf(err.Error()) + } + } + data := make([]byte, 20) + binary.BigEndian.PutUint32(data, uint32(0)) + nxtIP := net.ParseIP("2001:db8::3") + copy(data[4:], nxtIP) + msg := &RoutingHeader{ + NextHeader: 59, + HEL: 2, + RoutingType: 0, + SegmentsLeft: 1, + Data: util.NewBuffer(data), + } + testFunc(msg) +} + +func testRoutingHeaderEqual(oriMessage *RoutingHeader, newMessage *RoutingHeader) error { + if oriMessage.NextHeader != newMessage.NextHeader { + return fmt.Errorf("RoutingHeader NextHeader not equal") + } + if oriMessage.HEL != newMessage.HEL { + return fmt.Errorf("RoutingHeader HEL not equal") + } + if oriMessage.RoutingType != newMessage.RoutingType { + return fmt.Errorf("RoutingHeader RoutingType not equal") + } + if oriMessage.SegmentsLeft != newMessage.SegmentsLeft { + return fmt.Errorf("RoutingHeader SegmentsLeft not equal") + } + if (oriMessage.Data != nil && newMessage.Data == nil) || (oriMessage.Data == nil && newMessage.Data != nil) { + return fmt.Errorf("RoutingHeader Data not equal") + } + if (oriMessage.Data != nil && newMessage.Data != nil) && !bytes.Equal(oriMessage.Data.Bytes(), newMessage.Data.Bytes()) { + return fmt.Errorf("RoutingHeader Data not equal") + } + return nil +} + +func TestFragmentHeader(t *testing.T) { + testFunc := func(oriMessage *FragmentHeader) { + data, err := oriMessage.MarshalBinary() + if err != nil { + t.Fatalf("Failed to Marshal message: %v", err) + } + newMessage := new(FragmentHeader) + err = newMessage.UnmarshalBinary(data) + if err != nil { + t.Fatalf("Failed to UnMarshal message: %v", err) + } + if err := testFragmentHeaderEqual(oriMessage, newMessage); err != nil { + t.Errorf(err.Error()) + } + } + + msg := &FragmentHeader{ + NextHeader: 59, + Reserved: 0, + FragmentOffset: 0x1234, + MoreFragments: true, + Identification: 0xabcd, + } + testFunc(msg) +} + +func testFragmentHeaderEqual(oriMessage *FragmentHeader, newMessage *FragmentHeader) error { + if oriMessage.NextHeader != newMessage.NextHeader { + return fmt.Errorf("FragmentHeader NextHeader not equal") + } + if oriMessage.Reserved != newMessage.Reserved { + return fmt.Errorf("FragmentHeader Reserved not equal") + } + if oriMessage.FragmentOffset != newMessage.FragmentOffset { + return fmt.Errorf("FragmentHeader FragmentOffset not equal") + } + if oriMessage.MoreFragments != newMessage.MoreFragments { + return fmt.Errorf("FragmentHeader MoreFragment not equal") + } + if oriMessage.Identification != newMessage.Identification { + return fmt.Errorf("FragmentHeader Identification not equal") + } + return nil +} + +func TestIPv6(t *testing.T) { + testFunc := func(oriMessage *IPv6) { + data, err := oriMessage.MarshalBinary() + if err != nil { + t.Fatalf("Failed to Marshal message: %v", err) + } + newMessage := new(IPv6) + err = newMessage.UnmarshalBinary(data) + if err != nil { + t.Fatalf("Failed to UnMarshal message: %v", err) + } + err = testIPv6Equals(oriMessage, newMessage) + if err != nil { + t.Error(err.Error()) + } + } + srcIP := net.ParseIP("2001:db8::1") + dstIP := net.ParseIP("2001:db8::2") + icmpData := make([]byte, 4) + binary.BigEndian.PutUint32(icmpData, 0x34567890) + uplayerData := &ICMP{ + Type: 128, + Code: 0, + Checksum: 0x2345, + Data: icmpData, + } + + msg1 := &IPv6{ + Version: 6, + TrafficClass: 1, + FlowLabel: 0x12345, + Length: 8, + NextHeader: Type_HBH, + HopLimit: 64, + NWSrc: srcIP, + NWDst: dstIP, + } + msg1.HbhHeader = &HopByHopHeader{ + NextHeader: Type_IPv6ICMP, + HEL: 0, + Options: []*Option{ + { + Type: 1, + Length: 4, + Data: []byte{0x00, 0x00, 0x00, 0x00}, + }, + }, + } + msg1.Data = uplayerData + testFunc(msg1) + + msg2 := &IPv6{ + Version: 6, + TrafficClass: 1, + FlowLabel: 0x12345, + Length: 8, + NextHeader: Type_Routing, + HopLimit: 64, + NWSrc: srcIP, + NWDst: dstIP, + Data: uplayerData, + } + data := make([]byte, 20) + binary.BigEndian.PutUint32(data, uint32(0)) + nxtIP := net.ParseIP("2001:db8::3") + copy(data[4:], nxtIP) + msg2.RoutingHeader = &RoutingHeader{ + NextHeader: Type_Fragment, + HEL: 2, + RoutingType: 0, + SegmentsLeft: 1, + Data: util.NewBuffer(data), + } + msg2.FragmentHeader = &FragmentHeader{ + NextHeader: Type_IPv6ICMP, + Reserved: 0, + FragmentOffset: 0x1234, + MoreFragments: true, + Identification: 0xabcd, + } + testFunc(msg2) +} + +func testIPv6Equals(oriMessage, newMessage *IPv6) error { + if oriMessage.Version != newMessage.Version { + return fmt.Errorf("IPv6 version not equal") + } + if oriMessage.TrafficClass != newMessage.TrafficClass { + return fmt.Errorf("IPv6 trafficClass not equal") + } + if oriMessage.FlowLabel != newMessage.FlowLabel { + return fmt.Errorf("IPv6 flowLable not equal") + } + if oriMessage.Length != newMessage.Length { + return fmt.Errorf("IPv6 Length not equal") + } + if oriMessage.NextHeader != newMessage.NextHeader { + return fmt.Errorf("IPv6 NextHeader not equal") + } + if oriMessage.HopLimit != newMessage.HopLimit { + return fmt.Errorf("IPv6 HotLimit not equal") + } + if !bytes.Equal(oriMessage.NWSrc, newMessage.NWSrc) { + return fmt.Errorf("IPv6 NWSrc not equal") + } + if !bytes.Equal(oriMessage.NWDst, newMessage.NWDst) { + return fmt.Errorf("IPv6 NWDst not equal") + } + oriData, err := oriMessage.Data.MarshalBinary() + if err != nil { + return err + } + newData, err := newMessage.Data.MarshalBinary() + if err != nil { + return err + } + if !bytes.Equal(oriData, newData) { + return fmt.Errorf("IPv6 Data not equal") + } + return nil +}