| // Copyright 2012 Google, Inc. All rights reserved. |
| // Copyright 2009-2011 Andreas Krennmair. 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" |
| "fmt" |
| |
| "github.com/google/gopacket" |
| ) |
| |
| // UDP is the layer for UDP headers. |
| type UDP struct { |
| BaseLayer |
| SrcPort, DstPort UDPPort |
| Length uint16 |
| Checksum uint16 |
| sPort, dPort []byte |
| tcpipchecksum |
| } |
| |
| // LayerType returns gopacket.LayerTypeUDP |
| func (u *UDP) LayerType() gopacket.LayerType { return LayerTypeUDP } |
| |
| func (udp *UDP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { |
| if len(data) < 8 { |
| df.SetTruncated() |
| return fmt.Errorf("Invalid UDP header. Length %d less than 8", len(data)) |
| } |
| udp.SrcPort = UDPPort(binary.BigEndian.Uint16(data[0:2])) |
| udp.sPort = data[0:2] |
| udp.DstPort = UDPPort(binary.BigEndian.Uint16(data[2:4])) |
| udp.dPort = data[2:4] |
| udp.Length = binary.BigEndian.Uint16(data[4:6]) |
| udp.Checksum = binary.BigEndian.Uint16(data[6:8]) |
| udp.BaseLayer = BaseLayer{Contents: data[:8]} |
| switch { |
| case udp.Length >= 8: |
| hlen := int(udp.Length) |
| if hlen > len(data) { |
| df.SetTruncated() |
| hlen = len(data) |
| } |
| udp.Payload = data[8:hlen] |
| case udp.Length == 0: // Jumbogram, use entire rest of data |
| udp.Payload = data[8:] |
| default: |
| return fmt.Errorf("UDP packet too small: %d bytes", udp.Length) |
| } |
| return nil |
| } |
| |
| // SerializeTo writes the serialized form of this layer into the |
| // SerializationBuffer, implementing gopacket.SerializableLayer. |
| // See the docs for gopacket.SerializableLayer for more info. |
| func (u *UDP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { |
| var jumbo bool |
| |
| payload := b.Bytes() |
| if _, ok := u.pseudoheader.(*IPv6); ok { |
| if len(payload)+8 > 65535 { |
| jumbo = true |
| } |
| } |
| bytes, err := b.PrependBytes(8) |
| if err != nil { |
| return err |
| } |
| binary.BigEndian.PutUint16(bytes, uint16(u.SrcPort)) |
| binary.BigEndian.PutUint16(bytes[2:], uint16(u.DstPort)) |
| if opts.FixLengths { |
| if jumbo { |
| u.Length = 0 |
| } else { |
| u.Length = uint16(len(payload)) + 8 |
| } |
| } |
| binary.BigEndian.PutUint16(bytes[4:], u.Length) |
| if opts.ComputeChecksums { |
| // zero out checksum bytes |
| bytes[6] = 0 |
| bytes[7] = 0 |
| csum, err := u.computeChecksum(b.Bytes(), IPProtocolUDP) |
| if err != nil { |
| return err |
| } |
| u.Checksum = csum |
| } |
| binary.BigEndian.PutUint16(bytes[6:], u.Checksum) |
| return nil |
| } |
| |
| func (u *UDP) CanDecode() gopacket.LayerClass { |
| return LayerTypeUDP |
| } |
| |
| // NextLayerType use the destination port to select the |
| // right next decoder. It tries first to decode via the |
| // destination port, then the source port. |
| func (u *UDP) NextLayerType() gopacket.LayerType { |
| if lt := u.DstPort.LayerType(); lt != gopacket.LayerTypePayload { |
| return lt |
| } |
| return u.SrcPort.LayerType() |
| } |
| |
| func decodeUDP(data []byte, p gopacket.PacketBuilder) error { |
| udp := &UDP{} |
| err := udp.DecodeFromBytes(data, p) |
| p.AddLayer(udp) |
| p.SetTransportLayer(udp) |
| if err != nil { |
| return err |
| } |
| return p.NextDecoder(udp.NextLayerType()) |
| } |
| |
| func (u *UDP) TransportFlow() gopacket.Flow { |
| return gopacket.NewFlow(EndpointUDPPort, u.sPort, u.dPort) |
| } |
| |
| // For testing only |
| func (u *UDP) SetInternalPortsForTesting() { |
| u.sPort = make([]byte, 2) |
| u.dPort = make([]byte, 2) |
| binary.BigEndian.PutUint16(u.sPort, uint16(u.SrcPort)) |
| binary.BigEndian.PutUint16(u.dPort, uint16(u.DstPort)) |
| } |