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 )