| // Copyright 2016 Google, Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style license |
| // that can be found in the LICENSE file in the root of the source |
| // tree. |
| |
| package layers |
| |
| import ( |
| "encoding/binary" |
| "errors" |
| "fmt" |
| |
| "github.com/google/gopacket" |
| ) |
| |
| // VXLAN is specifed in RFC 7348 https://tools.ietf.org/html/rfc7348 |
| // G, D, A, Group Policy ID from https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 |
| // 0 1 2 3 |
| // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| // 0 8 16 24 32 |
| // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| // |G|R|R|R|I|R|R|R|R|D|R|R|A|R|R|R| Group Policy ID | |
| // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| // | 24 bit VXLAN Network Identifier | Reserved | |
| // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| // VXLAN is a VXLAN packet header |
| type VXLAN struct { |
| BaseLayer |
| ValidIDFlag bool // 'I' bit per RFC 7348 |
| VNI uint32 // 'VXLAN Network Identifier' 24 bits per RFC 7348 |
| GBPExtension bool // 'G' bit per Group Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 |
| GBPDontLearn bool // 'D' bit per Group Policy |
| GBPApplied bool // 'A' bit per Group Policy |
| GBPGroupPolicyID uint16 // 'Group Policy ID' 16 bits per Group Policy |
| } |
| |
| // LayerType returns LayerTypeVXLAN |
| func (vx *VXLAN) LayerType() gopacket.LayerType { return LayerTypeVXLAN } |
| |
| // CanDecode returns the layer type this DecodingLayer can decode |
| func (vx *VXLAN) CanDecode() gopacket.LayerClass { |
| return LayerTypeVXLAN |
| } |
| |
| // NextLayerType retuns the next layer we should see after vxlan |
| func (vx *VXLAN) NextLayerType() gopacket.LayerType { |
| return LayerTypeEthernet |
| } |
| |
| // DecodeFromBytes takes a byte buffer and decodes |
| func (vx *VXLAN) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { |
| if len(data) < 8 { |
| return errors.New("vxlan packet too small") |
| } |
| // VNI is a 24bit number, Uint32 requires 32 bits |
| var buf [4]byte |
| copy(buf[1:], data[4:7]) |
| |
| // RFC 7348 https://tools.ietf.org/html/rfc7348 |
| vx.ValidIDFlag = data[0]&0x08 > 0 // 'I' bit per RFC7348 |
| vx.VNI = binary.BigEndian.Uint32(buf[:]) // VXLAN Network Identifier per RFC7348 |
| |
| // Group Based Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 |
| vx.GBPExtension = data[0]&0x80 > 0 // 'G' bit per the group policy draft |
| vx.GBPDontLearn = data[1]&0x40 > 0 // 'D' bit - the egress VTEP MUST NOT learn the source address of the encapsulated frame. |
| vx.GBPApplied = data[1]&0x80 > 0 // 'A' bit - indicates that the group policy has already been applied to this packet. |
| vx.GBPGroupPolicyID = binary.BigEndian.Uint16(data[2:4]) // Policy ID as per the group policy draft |
| |
| // Layer information |
| const vxlanLength = 8 |
| vx.Contents = data[:vxlanLength] |
| vx.Payload = data[vxlanLength:] |
| |
| return nil |
| |
| } |
| |
| func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error { |
| vx := &VXLAN{} |
| err := vx.DecodeFromBytes(data, p) |
| if err != nil { |
| return err |
| } |
| |
| p.AddLayer(vx) |
| return p.NextDecoder(LinkTypeEthernet) |
| } |
| |
| // SerializeTo writes the serialized form of this layer into the |
| // SerializationBuffer, implementing gopacket.SerializableLayer. |
| // See the docs for gopacket.SerializableLayer for more info. |
| func (vx *VXLAN) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { |
| bytes, err := b.PrependBytes(8) |
| 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 |
| |
| if vx.ValidIDFlag { |
| bytes[0] |= 0x08 |
| } |
| if vx.GBPExtension { |
| bytes[0] |= 0x80 |
| } |
| if vx.GBPDontLearn { |
| bytes[1] |= 0x40 |
| } |
| if vx.GBPApplied { |
| bytes[1] |= 0x80 |
| } |
| |
| binary.BigEndian.PutUint16(bytes[2:4], vx.GBPGroupPolicyID) |
| if vx.VNI >= 1<<24 { |
| return fmt.Errorf("Virtual Network Identifier = %x exceeds max for 24-bit uint", vx.VNI) |
| } |
| binary.BigEndian.PutUint32(bytes[4:8], vx.VNI<<8) |
| return nil |
| } |