summaryrefslogtreecommitdiffhomepage
path: root/pkg/dhcp/dhcp.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/dhcp/dhcp.go')
-rw-r--r--pkg/dhcp/dhcp.go263
1 files changed, 263 insertions, 0 deletions
diff --git a/pkg/dhcp/dhcp.go b/pkg/dhcp/dhcp.go
new file mode 100644
index 000000000..762086853
--- /dev/null
+++ b/pkg/dhcp/dhcp.go
@@ -0,0 +1,263 @@
+// Copyright 2016 The Netstack Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package dhcp implements a DHCP client and server as described in RFC 2131.
+package dhcp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "time"
+
+ "gvisor.googlesource.com/gvisor/pkg/tcpip"
+)
+
+// Config is standard DHCP configuration.
+type Config struct {
+ ServerAddress tcpip.Address // address of the server
+ SubnetMask tcpip.AddressMask // client address subnet mask
+ Gateway tcpip.Address // client default gateway
+ DomainNameServer tcpip.Address // client domain name server
+ LeaseLength time.Duration // length of the address lease
+}
+
+func (cfg *Config) decode(opts []option) error {
+ *cfg = Config{}
+ for _, opt := range opts {
+ b := opt.body
+ if l := opt.code.len(); l != -1 && l != len(b) {
+ return fmt.Errorf("%s bad length: %d", opt.code, len(b))
+ }
+ switch opt.code {
+ case optLeaseTime:
+ t := binary.BigEndian.Uint32(b)
+ cfg.LeaseLength = time.Duration(t) * time.Second
+ case optSubnetMask:
+ cfg.SubnetMask = tcpip.AddressMask(b)
+ case optDHCPServer:
+ cfg.ServerAddress = tcpip.Address(b)
+ case optDefaultGateway:
+ cfg.Gateway = tcpip.Address(b)
+ case optDomainNameServer:
+ cfg.DomainNameServer = tcpip.Address(b)
+ }
+ }
+ return nil
+}
+
+func (cfg Config) encode() (opts []option) {
+ if cfg.ServerAddress != "" {
+ opts = append(opts, option{optDHCPServer, []byte(cfg.ServerAddress)})
+ }
+ if cfg.SubnetMask != "" {
+ opts = append(opts, option{optSubnetMask, []byte(cfg.SubnetMask)})
+ }
+ if cfg.Gateway != "" {
+ opts = append(opts, option{optDefaultGateway, []byte(cfg.Gateway)})
+ }
+ if cfg.DomainNameServer != "" {
+ opts = append(opts, option{optDomainNameServer, []byte(cfg.DomainNameServer)})
+ }
+ if l := cfg.LeaseLength / time.Second; l != 0 {
+ v := make([]byte, 4)
+ v[0] = byte(l >> 24)
+ v[1] = byte(l >> 16)
+ v[2] = byte(l >> 8)
+ v[3] = byte(l >> 0)
+ opts = append(opts, option{optLeaseTime, v})
+ }
+ return opts
+}
+
+const (
+ serverPort = 67
+ clientPort = 68
+)
+
+var magicCookie = []byte{99, 130, 83, 99} // RFC 1497
+
+type xid uint32
+
+type header []byte
+
+func (h header) init() {
+ h[1] = 0x01 // htype
+ h[2] = 0x06 // hlen
+ h[3] = 0x00 // hops
+ h[8], h[9] = 0, 0 // secs
+ copy(h[236:240], magicCookie)
+}
+
+func (h header) isValid() bool {
+ if len(h) < 241 {
+ return false
+ }
+ if o := h.op(); o != opRequest && o != opReply {
+ return false
+ }
+ if h[1] != 0x01 || h[2] != 0x06 || h[3] != 0x00 {
+ return false
+ }
+ return bytes.Equal(h[236:240], magicCookie) && h[len(h)-1] == 0
+}
+
+func (h header) op() op { return op(h[0]) }
+func (h header) setOp(o op) { h[0] = byte(o) }
+func (h header) xidbytes() []byte { return h[4:8] }
+func (h header) xid() xid { return xid(h[4])<<24 | xid(h[5])<<16 | xid(h[6])<<8 | xid(h[7]) }
+func (h header) setBroadcast() { h[10], h[11] = 0x80, 0x00 } // flags top bit
+func (h header) ciaddr() []byte { return h[12:16] }
+func (h header) yiaddr() []byte { return h[16:20] }
+func (h header) siaddr() []byte { return h[20:24] }
+func (h header) giaddr() []byte { return h[24:28] }
+func (h header) chaddr() []byte { return h[28:44] }
+func (h header) sname() []byte { return h[44:108] }
+func (h header) file() []byte { return h[108:236] }
+
+func (h header) options() (opts options, err error) {
+ i := headerBaseSize
+ for i < len(h) {
+ if h[i] == 0 {
+ i++
+ continue
+ }
+ if h[i] == 255 {
+ break
+ }
+ if len(h) <= i+1 {
+ return nil, fmt.Errorf("option missing length")
+ }
+ optlen := int(h[i+1])
+ if len(h) < i+2+optlen {
+ return nil, fmt.Errorf("option too long")
+ }
+ opts = append(opts, option{
+ code: optionCode(h[i]),
+ body: h[i+2 : i+2+optlen],
+ })
+ i += 2 + optlen
+ }
+ return opts, nil
+}
+
+func (h header) setOptions(opts []option) {
+ i := headerBaseSize
+ for _, opt := range opts {
+ h[i] = byte(opt.code)
+ h[i+1] = byte(len(opt.body))
+ copy(h[i+2:i+2+len(opt.body)], opt.body)
+ i += 2 + len(opt.body)
+ }
+ for ; i < len(h); i++ {
+ h[i] = 0
+ }
+}
+
+// headerBaseSize is the size of a DHCP packet, including the magic cookie.
+//
+// Note that a DHCP packet is required to have an 'end' option that takes
+// up an extra byte, so the minimum DHCP packet size is headerBaseSize + 1.
+const headerBaseSize = 240
+
+type option struct {
+ code optionCode
+ body []byte
+}
+
+type optionCode byte
+
+const (
+ optSubnetMask optionCode = 1
+ optDefaultGateway optionCode = 3
+ optDomainNameServer optionCode = 6
+ optReqIPAddr optionCode = 50
+ optLeaseTime optionCode = 51
+ optDHCPMsgType optionCode = 53 // dhcpMsgType
+ optDHCPServer optionCode = 54
+ optParamReq optionCode = 55
+)
+
+func (code optionCode) len() int {
+ switch code {
+ case optSubnetMask, optDefaultGateway, optDomainNameServer,
+ optReqIPAddr, optLeaseTime, optDHCPServer:
+ return 4
+ case optDHCPMsgType:
+ return 1
+ case optParamReq:
+ return -1 // no fixed length
+ default:
+ return -1
+ }
+}
+
+func (code optionCode) String() string {
+ switch code {
+ case optSubnetMask:
+ return "option(subnet-mask)"
+ case optDefaultGateway:
+ return "option(default-gateway)"
+ case optDomainNameServer:
+ return "option(dns)"
+ case optReqIPAddr:
+ return "option(request-ip-address)"
+ case optLeaseTime:
+ return "option(least-time)"
+ case optDHCPMsgType:
+ return "option(message-type)"
+ case optDHCPServer:
+ return "option(server)"
+ case optParamReq:
+ return "option(parameter-request)"
+ default:
+ return fmt.Sprintf("option(%d)", code)
+ }
+}
+
+type options []option
+
+func (opts options) dhcpMsgType() (dhcpMsgType, error) {
+ for _, opt := range opts {
+ if opt.code == optDHCPMsgType {
+ if len(opt.body) != 1 {
+ return 0, fmt.Errorf("%s: bad length: %d", optDHCPMsgType, len(opt.body))
+ }
+ v := opt.body[0]
+ if v <= 0 || v >= 8 {
+ return 0, fmt.Errorf("%s: unknown value: %d", optDHCPMsgType, v)
+ }
+ return dhcpMsgType(v), nil
+ }
+ }
+ return 0, nil
+}
+
+func (opts options) len() int {
+ l := 0
+ for _, opt := range opts {
+ l += 1 + 1 + len(opt.body) // code + len + body
+ }
+ return l + 1 // extra byte for 'pad' option
+}
+
+type op byte
+
+const (
+ opRequest op = 0x01
+ opReply op = 0x02
+)
+
+// dhcpMsgType is the DHCP Message Type from RFC 1533, section 9.4.
+type dhcpMsgType byte
+
+const (
+ dhcpDISCOVER dhcpMsgType = 1
+ dhcpOFFER dhcpMsgType = 2
+ dhcpREQUEST dhcpMsgType = 3
+ dhcpDECLINE dhcpMsgType = 4
+ dhcpACK dhcpMsgType = 5
+ dhcpNAK dhcpMsgType = 6
+ dhcpRELEASE dhcpMsgType = 7
+)