diff --git a/layers/geneve.go b/layers/geneve.go index e9a142880..f47bc6302 100644 --- a/layers/geneve.go +++ b/layers/geneve.go @@ -9,6 +9,7 @@ package layers import ( "encoding/binary" "errors" + "fmt" "github.com/google/gopacket" ) @@ -119,3 +120,59 @@ func decodeGeneve(data []byte, p gopacket.PacketBuilder) error { gn := &Geneve{} return decodingLayerDecoder(gn, data, p) } + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (gn *Geneve) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + plen := int(gn.OptionsLength + 8) + bytes, err := b.PrependBytes(plen) + if err != nil { + return err + } + + // PrependBytes does not guarantee that bytes are zeroed. Setting flags via OR requires that they start off at zero + bytes[0] = 0 + bytes[1] = 0 + + // Construct Geneve + + bytes[0] |= gn.Version << 6 + bytes[0] |= ((gn.OptionsLength >> 2) & 0x3f) + + if gn.OAMPacket { + bytes[1] |= 0x80 + } + + if gn.CriticalOption { + bytes[1] |= 0x40 + } + + binary.BigEndian.PutUint16(bytes[2:4], uint16(gn.Protocol)) + + if gn.VNI >= 1<<24 { + return fmt.Errorf("Virtual Network Identifier = %x exceeds max for 24-bit uint", gn.VNI) + } + binary.BigEndian.PutUint32(bytes[4:8], gn.VNI<<8) + + // Construct Options + + offset, _ := uint8(8), int32(gn.OptionsLength) + for _, o := range gn.Options { + binary.BigEndian.PutUint16(bytes[offset:(offset+2)], uint16(o.Class)) + + offset += 2 + bytes[offset] = o.Type + + offset += 1 + bytes[offset] |= o.Flags << 5 + bytes[offset] |= ((o.Length - 4) >> 2) & 0x1f + + offset += 1 + copy(bytes[offset:(offset+o.Length-4)], o.Data) + + offset += o.Length - 4 + } + + return nil +} diff --git a/layers/geneve_test.go b/layers/geneve_test.go index f1a420bc6..f07d2b1be 100644 --- a/layers/geneve_test.go +++ b/layers/geneve_test.go @@ -136,7 +136,7 @@ func TestDecodeGeneve3(t *testing.T) { Protocol: EthernetTypeTransparentEthernetBridging, VNI: 0xa, Options: []*GeneveOption{ - &GeneveOption{ + { Class: 0x0, Type: 0x80, Length: 8, @@ -155,3 +155,39 @@ func BenchmarkDecodeGeneve1(b *testing.B) { gopacket.NewPacket(testPacketGeneve1, LinkTypeEthernet, gopacket.NoCopy) } } + +func TestIsomorphicPacketGeneve(t *testing.T) { + gn := &Geneve{ + Version: 0x0, + OptionsLength: 0x14, + OAMPacket: false, + CriticalOption: true, + Protocol: EthernetTypeTransparentEthernetBridging, + VNI: 0xa, + Options: []*GeneveOption{ + { + Class: 0x0, + Type: 0x80, + Length: 12, + Data: []byte{0, 0, 0, 0, 0, 0, 0, 0xc}, + }, + { + Class: 0x0, + Type: 0x80, + Length: 8, + Data: []byte{0, 0, 0, 0xc}, + }, + }, + } + + b := gopacket.NewSerializeBuffer() + gn.SerializeTo(b, gopacket.SerializeOptions{}) + + p := gopacket.NewPacket(b.Bytes(), gopacket.DecodeFunc(decodeGeneve), gopacket.Default) + gnTranslated := p.Layer(LayerTypeGeneve).(*Geneve) + gnTranslated.BaseLayer = BaseLayer{} + + if !reflect.DeepEqual(gn, gnTranslated) { + t.Errorf("VXLAN isomorph mismatch, \nwant %#v\ngot %#v\n", gn, gnTranslated) + } +}