github.com/jspc/eggos@v0.5.1-0.20221028160421-556c75c878a5/inet/dhcp/dhcp.go (about)

     1  // Copyright 2016 The Netstack Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package dhcp implements a DHCP client and server as described in RFC 2131.
     6  package dhcp
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"time"
    13  
    14  	"gvisor.dev/gvisor/pkg/tcpip"
    15  )
    16  
    17  // Config is standard DHCP configuration.
    18  type Config struct {
    19  	ServerAddress    tcpip.Address     // address of the server
    20  	SubnetMask       tcpip.AddressMask // client address subnet mask
    21  	Gateway          tcpip.Address     // client default gateway
    22  	DomainNameServer tcpip.Address     // client domain name server
    23  	LeaseLength      time.Duration     // length of the address lease
    24  }
    25  
    26  func (cfg *Config) decode(opts []option) error {
    27  	*cfg = Config{}
    28  	for _, opt := range opts {
    29  		b := opt.body
    30  		if l := opt.code.len(); l != -1 && l != len(b) {
    31  			return fmt.Errorf("%s bad length: %d", opt.code, len(b))
    32  		}
    33  		switch opt.code {
    34  		case optLeaseTime:
    35  			t := binary.BigEndian.Uint32(b)
    36  			cfg.LeaseLength = time.Duration(t) * time.Second
    37  		case optSubnetMask:
    38  			cfg.SubnetMask = tcpip.AddressMask(b)
    39  		case optDHCPServer:
    40  			cfg.ServerAddress = tcpip.Address(b)
    41  		case optDefaultGateway:
    42  			cfg.Gateway = tcpip.Address(b)
    43  		case optDomainNameServer:
    44  			cfg.DomainNameServer = tcpip.Address(b)
    45  		}
    46  	}
    47  	return nil
    48  }
    49  
    50  func (cfg Config) encode() (opts []option) {
    51  	if cfg.ServerAddress != "" {
    52  		opts = append(opts, option{optDHCPServer, []byte(cfg.ServerAddress)})
    53  	}
    54  	if cfg.SubnetMask != "" {
    55  		opts = append(opts, option{optSubnetMask, []byte(cfg.SubnetMask)})
    56  	}
    57  	if cfg.Gateway != "" {
    58  		opts = append(opts, option{optDefaultGateway, []byte(cfg.Gateway)})
    59  	}
    60  	if cfg.DomainNameServer != "" {
    61  		opts = append(opts, option{optDomainNameServer, []byte(cfg.DomainNameServer)})
    62  	}
    63  	if l := cfg.LeaseLength / time.Second; l != 0 {
    64  		v := make([]byte, 4)
    65  		v[0] = byte(l >> 24)
    66  		v[1] = byte(l >> 16)
    67  		v[2] = byte(l >> 8)
    68  		v[3] = byte(l >> 0)
    69  		opts = append(opts, option{optLeaseTime, v})
    70  	}
    71  	return opts
    72  }
    73  
    74  const (
    75  	serverPort = 67
    76  	clientPort = 68
    77  )
    78  
    79  var magicCookie = []byte{99, 130, 83, 99} // RFC 1497
    80  
    81  type xid uint32
    82  
    83  type header []byte
    84  
    85  func (h header) init() {
    86  	h[1] = 0x01       // htype
    87  	h[2] = 0x06       // hlen
    88  	h[3] = 0x00       // hops
    89  	h[8], h[9] = 0, 0 // secs
    90  	copy(h[236:240], magicCookie)
    91  }
    92  
    93  func (h header) isValid() bool {
    94  	if len(h) < 241 {
    95  		return false
    96  	}
    97  	if o := h.op(); o != opRequest && o != opReply {
    98  		return false
    99  	}
   100  	if h[1] != 0x01 || h[2] != 0x06 || h[3] != 0x00 {
   101  		return false
   102  	}
   103  	return bytes.Equal(h[236:240], magicCookie)
   104  }
   105  
   106  func (h header) op() op           { return op(h[0]) }
   107  func (h header) setOp(o op)       { h[0] = byte(o) }
   108  func (h header) xidbytes() []byte { return h[4:8] }
   109  func (h header) xid() xid         { return xid(h[4])<<24 | xid(h[5])<<16 | xid(h[6])<<8 | xid(h[7]) }
   110  func (h header) setBroadcast()    { h[10], h[11] = 0x80, 0x00 } // flags top bit
   111  func (h header) ciaddr() []byte   { return h[12:16] }
   112  func (h header) yiaddr() []byte   { return h[16:20] }
   113  func (h header) siaddr() []byte   { return h[20:24] }
   114  func (h header) giaddr() []byte   { return h[24:28] }
   115  func (h header) chaddr() []byte   { return h[28:44] }
   116  func (h header) sname() []byte    { return h[44:108] }
   117  func (h header) file() []byte     { return h[108:236] }
   118  
   119  func (h header) options() (opts options, err error) {
   120  	i := headerBaseSize
   121  	for i < len(h) {
   122  		if h[i] == 0 {
   123  			i++
   124  			continue
   125  		}
   126  		if h[i] == 255 {
   127  			break
   128  		}
   129  		if len(h) <= i+1 {
   130  			return nil, fmt.Errorf("option missing length")
   131  		}
   132  		optlen := int(h[i+1])
   133  		if len(h) < i+2+optlen {
   134  			return nil, fmt.Errorf("option too long")
   135  		}
   136  		opts = append(opts, option{
   137  			code: optionCode(h[i]),
   138  			body: h[i+2 : i+2+optlen],
   139  		})
   140  		i += 2 + optlen
   141  	}
   142  	return opts, nil
   143  }
   144  
   145  func (h header) setOptions(opts []option) {
   146  	i := headerBaseSize
   147  	for _, opt := range opts {
   148  		h[i] = byte(opt.code)
   149  		h[i+1] = byte(len(opt.body))
   150  		copy(h[i+2:i+2+len(opt.body)], opt.body)
   151  		i += 2 + len(opt.body)
   152  	}
   153  	for ; i < len(h); i++ {
   154  		h[i] = 0
   155  	}
   156  }
   157  
   158  // headerBaseSize is the size of a DHCP packet, including the magic cookie.
   159  //
   160  // Note that a DHCP packet is required to have an 'end' option that takes
   161  // up an extra byte, so the minimum DHCP packet size is headerBaseSize + 1.
   162  const headerBaseSize = 240
   163  
   164  type option struct {
   165  	code optionCode
   166  	body []byte
   167  }
   168  
   169  type optionCode byte
   170  
   171  const (
   172  	optSubnetMask       optionCode = 1
   173  	optDefaultGateway   optionCode = 3
   174  	optDomainNameServer optionCode = 6
   175  	optReqIPAddr        optionCode = 50
   176  	optLeaseTime        optionCode = 51
   177  	optDHCPMsgType      optionCode = 53 // dhcpMsgType
   178  	optDHCPServer       optionCode = 54
   179  	optParamReq         optionCode = 55
   180  )
   181  
   182  func (code optionCode) len() int {
   183  	switch code {
   184  	case optSubnetMask, optDefaultGateway, optDomainNameServer,
   185  		optReqIPAddr, optLeaseTime, optDHCPServer:
   186  		return 4
   187  	case optDHCPMsgType:
   188  		return 1
   189  	case optParamReq:
   190  		return -1 // no fixed length
   191  	default:
   192  		return -1
   193  	}
   194  }
   195  
   196  func (code optionCode) String() string {
   197  	switch code {
   198  	case optSubnetMask:
   199  		return "option(subnet-mask)"
   200  	case optDefaultGateway:
   201  		return "option(default-gateway)"
   202  	case optDomainNameServer:
   203  		return "option(dns)"
   204  	case optReqIPAddr:
   205  		return "option(request-ip-address)"
   206  	case optLeaseTime:
   207  		return "option(least-time)"
   208  	case optDHCPMsgType:
   209  		return "option(message-type)"
   210  	case optDHCPServer:
   211  		return "option(server)"
   212  	case optParamReq:
   213  		return "option(parameter-request)"
   214  	default:
   215  		return fmt.Sprintf("option(%d)", code)
   216  	}
   217  }
   218  
   219  type options []option
   220  
   221  func (opts options) dhcpMsgType() (dhcpMsgType, error) {
   222  	for _, opt := range opts {
   223  		if opt.code == optDHCPMsgType {
   224  			if len(opt.body) != 1 {
   225  				return 0, fmt.Errorf("%s: bad length: %d", optDHCPMsgType, len(opt.body))
   226  			}
   227  			v := opt.body[0]
   228  			if v <= 0 || v >= 8 {
   229  				return 0, fmt.Errorf("%s: unknown value: %d", optDHCPMsgType, v)
   230  			}
   231  			return dhcpMsgType(v), nil
   232  		}
   233  	}
   234  	return 0, nil
   235  }
   236  
   237  func (opts options) len() int {
   238  	l := 0
   239  	for _, opt := range opts {
   240  		l += 1 + 1 + len(opt.body) // code + len + body
   241  	}
   242  	return l + 1 // extra byte for 'pad' option
   243  }
   244  
   245  type op byte
   246  
   247  const (
   248  	opRequest op = 0x01
   249  	opReply   op = 0x02
   250  )
   251  
   252  // dhcpMsgType is the DHCP Message Type from RFC 1533, section 9.4.
   253  type dhcpMsgType byte
   254  
   255  const (
   256  	dhcpDISCOVER dhcpMsgType = 1
   257  	dhcpOFFER    dhcpMsgType = 2
   258  	dhcpREQUEST  dhcpMsgType = 3
   259  	dhcpDECLINE  dhcpMsgType = 4
   260  	dhcpACK      dhcpMsgType = 5
   261  	dhcpNAK      dhcpMsgType = 6
   262  	dhcpRELEASE  dhcpMsgType = 7
   263  )