github.com/gopacket/gopacket@v1.1.0/layers/dhcpv6.go (about)

     1  // Copyright 2018 Google, Inc. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style license
     4  // that can be found in the LICENSE file in the root of the source
     5  // tree.
     6  
     7  package layers
     8  
     9  import (
    10  	"encoding/binary"
    11  	"fmt"
    12  	"net"
    13  
    14  	"github.com/gopacket/gopacket"
    15  )
    16  
    17  // DHCPv6MsgType represents a DHCPv6 operation
    18  type DHCPv6MsgType byte
    19  
    20  // Constants that represent DHCP operations
    21  const (
    22  	DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota
    23  	DHCPv6MsgTypeSolicit
    24  	DHCPv6MsgTypeAdvertise
    25  	DHCPv6MsgTypeRequest
    26  	DHCPv6MsgTypeConfirm
    27  	DHCPv6MsgTypeRenew
    28  	DHCPv6MsgTypeRebind
    29  	DHCPv6MsgTypeReply
    30  	DHCPv6MsgTypeRelease
    31  	DHCPv6MsgTypeDecline
    32  	DHCPv6MsgTypeReconfigure
    33  	DHCPv6MsgTypeInformationRequest
    34  	DHCPv6MsgTypeRelayForward
    35  	DHCPv6MsgTypeRelayReply
    36  )
    37  
    38  // String returns a string version of a DHCPv6MsgType.
    39  func (o DHCPv6MsgType) String() string {
    40  	switch o {
    41  	case DHCPv6MsgTypeUnspecified:
    42  		return "Unspecified"
    43  	case DHCPv6MsgTypeSolicit:
    44  		return "Solicit"
    45  	case DHCPv6MsgTypeAdvertise:
    46  		return "Advertise"
    47  	case DHCPv6MsgTypeRequest:
    48  		return "Request"
    49  	case DHCPv6MsgTypeConfirm:
    50  		return "Confirm"
    51  	case DHCPv6MsgTypeRenew:
    52  		return "Renew"
    53  	case DHCPv6MsgTypeRebind:
    54  		return "Rebind"
    55  	case DHCPv6MsgTypeReply:
    56  		return "Reply"
    57  	case DHCPv6MsgTypeRelease:
    58  		return "Release"
    59  	case DHCPv6MsgTypeDecline:
    60  		return "Decline"
    61  	case DHCPv6MsgTypeReconfigure:
    62  		return "Reconfigure"
    63  	case DHCPv6MsgTypeInformationRequest:
    64  		return "InformationRequest"
    65  	case DHCPv6MsgTypeRelayForward:
    66  		return "RelayForward"
    67  	case DHCPv6MsgTypeRelayReply:
    68  		return "RelayReply"
    69  	default:
    70  		return "Unknown"
    71  	}
    72  }
    73  
    74  // DHCPv6 contains data for a single DHCP packet.
    75  type DHCPv6 struct {
    76  	BaseLayer
    77  	MsgType       DHCPv6MsgType
    78  	HopCount      uint8
    79  	LinkAddr      net.IP
    80  	PeerAddr      net.IP
    81  	TransactionID []byte
    82  	Options       DHCPv6Options
    83  }
    84  
    85  // LayerType returns gopacket.LayerTypeDHCPv6
    86  func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 }
    87  
    88  // DecodeFromBytes decodes the given bytes into this layer.
    89  func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
    90  	if len(data) < 4 {
    91  		df.SetTruncated()
    92  		return fmt.Errorf("DHCPv6 length %d too short", len(data))
    93  	}
    94  	d.BaseLayer = BaseLayer{Contents: data}
    95  	d.Options = d.Options[:0]
    96  	d.MsgType = DHCPv6MsgType(data[0])
    97  
    98  	offset := 0
    99  	if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
   100  		if len(data) < 34 {
   101  			df.SetTruncated()
   102  			return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType)
   103  		}
   104  		d.HopCount = data[1]
   105  		d.LinkAddr = net.IP(data[2:18])
   106  		d.PeerAddr = net.IP(data[18:34])
   107  		offset = 34
   108  	} else {
   109  		d.TransactionID = data[1:4]
   110  		offset = 4
   111  	}
   112  
   113  	stop := len(data)
   114  	for offset < stop {
   115  		o := DHCPv6Option{}
   116  		if err := o.decode(data[offset:]); err != nil {
   117  			return err
   118  		}
   119  		d.Options = append(d.Options, o)
   120  		offset += int(o.Length) + 4 // 2 from option code, 2 from option length
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  // Len returns the length of a DHCPv6 packet.
   127  func (d *DHCPv6) Len() int {
   128  	n := 1
   129  	if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
   130  		n += 33
   131  	} else {
   132  		n += 3
   133  	}
   134  
   135  	for _, o := range d.Options {
   136  		n += int(o.Length) + 4 // 2 from option code, 2 from option length
   137  	}
   138  
   139  	return n
   140  }
   141  
   142  // SerializeTo writes the serialized form of this layer into the
   143  // SerializationBuffer, implementing gopacket.SerializableLayer.
   144  // See the docs for gopacket.SerializableLayer for more info.
   145  func (d *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
   146  	plen := int(d.Len())
   147  
   148  	data, err := b.PrependBytes(plen)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	offset := 0
   154  	data[0] = byte(d.MsgType)
   155  	if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
   156  		data[1] = byte(d.HopCount)
   157  		copy(data[2:18], d.LinkAddr.To16())
   158  		copy(data[18:34], d.PeerAddr.To16())
   159  		offset = 34
   160  	} else {
   161  		copy(data[1:4], d.TransactionID)
   162  		offset = 4
   163  	}
   164  
   165  	if len(d.Options) > 0 {
   166  		for _, o := range d.Options {
   167  			if err := o.encode(data[offset:], opts); err != nil {
   168  				return err
   169  			}
   170  			offset += int(o.Length) + 4 // 2 from option code, 2 from option length
   171  		}
   172  	}
   173  	return nil
   174  }
   175  
   176  // CanDecode returns the set of layer types that this DecodingLayer can decode.
   177  func (d *DHCPv6) CanDecode() gopacket.LayerClass {
   178  	return LayerTypeDHCPv6
   179  }
   180  
   181  // NextLayerType returns the layer type contained by this DecodingLayer.
   182  func (d *DHCPv6) NextLayerType() gopacket.LayerType {
   183  	return gopacket.LayerTypePayload
   184  }
   185  
   186  func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error {
   187  	dhcp := &DHCPv6{}
   188  	err := dhcp.DecodeFromBytes(data, p)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	p.AddLayer(dhcp)
   193  	return p.NextDecoder(gopacket.LayerTypePayload)
   194  }
   195  
   196  // DHCPv6StatusCode represents a DHCP status code - RFC-3315
   197  type DHCPv6StatusCode uint16
   198  
   199  // Constants for the DHCPv6StatusCode.
   200  const (
   201  	DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota
   202  	DHCPv6StatusCodeUnspecFail
   203  	DHCPv6StatusCodeNoAddrsAvail
   204  	DHCPv6StatusCodeNoBinding
   205  	DHCPv6StatusCodeNotOnLink
   206  	DHCPv6StatusCodeUseMulticast
   207  )
   208  
   209  // String returns a string version of a DHCPv6StatusCode.
   210  func (o DHCPv6StatusCode) String() string {
   211  	switch o {
   212  	case DHCPv6StatusCodeSuccess:
   213  		return "Success"
   214  	case DHCPv6StatusCodeUnspecFail:
   215  		return "UnspecifiedFailure"
   216  	case DHCPv6StatusCodeNoAddrsAvail:
   217  		return "NoAddressAvailable"
   218  	case DHCPv6StatusCodeNoBinding:
   219  		return "NoBinding"
   220  	case DHCPv6StatusCodeNotOnLink:
   221  		return "NotOnLink"
   222  	case DHCPv6StatusCodeUseMulticast:
   223  		return "UseMulticast"
   224  	default:
   225  		return "Unknown"
   226  	}
   227  }
   228  
   229  // DHCPv6DUIDType represents a DHCP DUID - RFC-3315
   230  type DHCPv6DUIDType uint16
   231  
   232  // Constants for the DHCPv6DUIDType.
   233  const (
   234  	DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1
   235  	DHCPv6DUIDTypeEN
   236  	DHCPv6DUIDTypeLL
   237  )
   238  
   239  // String returns a string version of a DHCPv6DUIDType.
   240  func (o DHCPv6DUIDType) String() string {
   241  	switch o {
   242  	case DHCPv6DUIDTypeLLT:
   243  		return "LLT"
   244  	case DHCPv6DUIDTypeEN:
   245  		return "EN"
   246  	case DHCPv6DUIDTypeLL:
   247  		return "LL"
   248  	default:
   249  		return "Unknown"
   250  	}
   251  }
   252  
   253  // DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19)
   254  type DHCPv6DUID struct {
   255  	Type DHCPv6DUIDType
   256  	// LLT, LL
   257  	HardwareType []byte
   258  	// EN
   259  	EnterpriseNumber []byte
   260  	// LLT
   261  	Time []byte
   262  	// LLT, LL
   263  	LinkLayerAddress net.HardwareAddr
   264  	// EN
   265  	Identifier []byte
   266  }
   267  
   268  // DecodeFromBytes decodes the given bytes into a DHCPv6DUID
   269  func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
   270  	if len(data) < 2 {
   271  		return fmt.Errorf("Not enough bytes to decode: %d", len(data))
   272  	}
   273  
   274  	d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
   275  	if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
   276  		if len(data) < 4 {
   277  			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
   278  		}
   279  		d.HardwareType = data[2:4]
   280  	}
   281  
   282  	if d.Type == DHCPv6DUIDTypeLLT {
   283  		if len(data) < 8 {
   284  			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
   285  		}
   286  		d.Time = data[4:8]
   287  		d.LinkLayerAddress = net.HardwareAddr(data[8:])
   288  	} else if d.Type == DHCPv6DUIDTypeEN {
   289  		if len(data) < 6 {
   290  			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
   291  		}
   292  		d.EnterpriseNumber = data[2:6]
   293  		d.Identifier = data[6:]
   294  	} else { // DHCPv6DUIDTypeLL
   295  		if len(data) < 4 {
   296  			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
   297  		}
   298  		d.LinkLayerAddress = net.HardwareAddr(data[4:])
   299  	}
   300  
   301  	return nil
   302  }
   303  
   304  // Encode encodes the DHCPv6DUID in a slice of bytes
   305  func (d *DHCPv6DUID) Encode() []byte {
   306  	length := d.Len()
   307  	data := make([]byte, length)
   308  	binary.BigEndian.PutUint16(data[0:2], uint16(d.Type))
   309  
   310  	if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
   311  		copy(data[2:4], d.HardwareType)
   312  	}
   313  
   314  	if d.Type == DHCPv6DUIDTypeLLT {
   315  		copy(data[4:8], d.Time)
   316  		copy(data[8:], d.LinkLayerAddress)
   317  	} else if d.Type == DHCPv6DUIDTypeEN {
   318  		copy(data[2:6], d.EnterpriseNumber)
   319  		copy(data[6:], d.Identifier)
   320  	} else {
   321  		copy(data[4:], d.LinkLayerAddress)
   322  	}
   323  
   324  	return data
   325  }
   326  
   327  // Len returns the length of the DHCPv6DUID, respecting the type
   328  func (d *DHCPv6DUID) Len() int {
   329  	length := 2 // d.Type
   330  	if d.Type == DHCPv6DUIDTypeLLT {
   331  		length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress)
   332  	} else if d.Type == DHCPv6DUIDTypeEN {
   333  		length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier)
   334  	} else { // LL
   335  		length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress)
   336  	}
   337  
   338  	return length
   339  }
   340  
   341  func (d *DHCPv6DUID) String() string {
   342  	duid := "Type: " + d.Type.String() + ", "
   343  	if d.Type == DHCPv6DUIDTypeLLT {
   344  		duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress)
   345  	} else if d.Type == DHCPv6DUIDTypeEN {
   346  		duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier)
   347  	} else { // DHCPv6DUIDTypeLL
   348  		duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress)
   349  	}
   350  	return duid
   351  }
   352  
   353  func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) {
   354  	duid := &DHCPv6DUID{}
   355  	err := duid.DecodeFromBytes(data)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  	return duid, nil
   360  }