packet6.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package dhcp
  15. import (
  16. "encoding/binary"
  17. "net"
  18. "strings"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. )
  22. // DHCPv6 https://datatracker.ietf.org/doc/html/rfc8415
  23. /*
  24. 0 1 2 3
  25. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  26. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  27. | msg-type | transaction-id |
  28. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  29. | |
  30. . options .
  31. . (variable number and length) .
  32. | |
  33. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  34. */
  35. /*
  36. 0 1 2 3
  37. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  38. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  39. | msg-type | hop-count | |
  40. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
  41. | |
  42. | link-address |
  43. | |
  44. | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
  45. | | |
  46. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
  47. | |
  48. | peer-address |
  49. | |
  50. | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
  51. | | |
  52. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
  53. . .
  54. . options (variable number and length) .... .
  55. | |
  56. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  57. */
  58. // DHCPv6 Message Type
  59. const (
  60. DHCPV6_SOLICIT MessageType = 1
  61. DHCPV6_ADVERTISE MessageType = 2
  62. DHCPV6_REQUEST MessageType = 3
  63. DHCPV6_CONFIRM MessageType = 4
  64. DHCPV6_RENEW MessageType = 5
  65. DHCPV6_REBIND MessageType = 6
  66. DHCPV6_REPLY MessageType = 7
  67. DHCPV6_RELEASE MessageType = 8
  68. DHCPV6_DECLINE MessageType = 9
  69. DHCPV6_RECONFIGURE MessageType = 10
  70. DHCPV6_INFORMATION_REQUEST MessageType = 11
  71. DHCPV6_RELAY_FORW MessageType = 12
  72. DHCPV6_RELAY_REPL MessageType = 13
  73. )
  74. type OptionCode6 uint16
  75. const (
  76. DHCPV6_OPTION_CLIENTID OptionCode6 = 1
  77. DHCPV6_OPTION_SERVERID OptionCode6 = 2
  78. DHCPV6_OPTION_IA_NA OptionCode6 = 3
  79. DHCPV6_OPTION_IA_TA OptionCode6 = 4
  80. DHCPV6_OPTION_IAADDR OptionCode6 = 5
  81. DHCPV6_OPTION_ORO OptionCode6 = 6
  82. DHCPV6_OPTION_PREFERENCE OptionCode6 = 7
  83. DHCPV6_OPTION_ELAPSED_TIME OptionCode6 = 8
  84. DHCPV6_OPTION_RELAY_MSG OptionCode6 = 9
  85. DHCPV6_OPTION_AUTH OptionCode6 = 11
  86. DHCPV6_OPTION_UNICAST OptionCode6 = 12
  87. DHCPV6_OPTION_STATUS_CODE OptionCode6 = 13
  88. DHCPV6_OPTION_RAPID_COMMIT OptionCode6 = 14
  89. DHCPV6_OPTION_USER_CLASS OptionCode6 = 15
  90. DHCPV6_OPTION_VENDOR_CLASS OptionCode6 = 16
  91. DHCPV6_OPTION_VENDOR_OPTS OptionCode6 = 17
  92. DHCPV6_OPTION_INTERFACE_ID OptionCode6 = 18
  93. DHCPV6_OPTION_RECONF_MSG OptionCode6 = 19
  94. DHCPV6_OPTION_RECONF_ACCEPT OptionCode6 = 20
  95. DHCPV6_OPTION_IA_PD OptionCode6 = 25
  96. DHCPV6_OPTION_IAPREFIX OptionCode6 = 26
  97. DHCPV6_OPTION_INFORMATION_REFRESH_TIME OptionCode6 = 32
  98. DHCPV6_OPTION_SOL_MAX_RT OptionCode6 = 82
  99. DHCPV6_OPTION_INF_MAX_RT OptionCode6 = 83
  100. // https://www.rfc-editor.org/rfc/rfc3646
  101. OPTION_DNS_SERVERS OptionCode6 = 23
  102. OPTION_DOMAIN_LIST OptionCode6 = 24
  103. // https://www.rfc-editor.org/rfc/rfc4075
  104. OPTION_SNTP_SERVERS OptionCode6 = 31
  105. //https://www.rfc-editor.org/rfc/rfc5908
  106. OPTION_NTP_SERVERS6 OptionCode6 = 56
  107. // https://www.rfc-editor.org/rfc/rfc4833
  108. OPTION_NEW_POSIX_TIMEZONE OptionCode6 = 41
  109. OPTION_NEW_TZDB_TIMEZONE OptionCode6 = 42
  110. )
  111. func (opt OptionCode6) String() string {
  112. switch opt {
  113. case DHCPV6_OPTION_CLIENTID:
  114. return "DHCPV6_OPTION_CLIENTID"
  115. case DHCPV6_OPTION_SERVERID:
  116. return "DHCPV6_OPTION_SERVERID"
  117. case DHCPV6_OPTION_IA_NA:
  118. return "DHCPV6_OPTION_IA_NA"
  119. case DHCPV6_OPTION_IA_TA:
  120. return "DHCPV6_OPTION_IA_TA"
  121. case DHCPV6_OPTION_IAADDR:
  122. return "DHCPV6_OPTION_IAADDR"
  123. case DHCPV6_OPTION_ORO:
  124. return "DHCPV6_OPTION_ORO"
  125. case DHCPV6_OPTION_PREFERENCE:
  126. return "DHCPV6_OPTION_PREFERENCE"
  127. case DHCPV6_OPTION_ELAPSED_TIME:
  128. return "DHCPV6_OPTION_ELAPSED_TIME"
  129. case DHCPV6_OPTION_RELAY_MSG:
  130. return "DHCPV6_OPTION_RELAY_MSG"
  131. case DHCPV6_OPTION_AUTH:
  132. return "DHCPV6_OPTION_AUTH"
  133. case DHCPV6_OPTION_UNICAST:
  134. return "DHCPV6_OPTION_UNICAST"
  135. case DHCPV6_OPTION_STATUS_CODE:
  136. return "DHCPV6_OPTION_STATUS_CODE"
  137. case DHCPV6_OPTION_RAPID_COMMIT:
  138. return "DHCPV6_OPTION_RAPID_COMMIT"
  139. case DHCPV6_OPTION_USER_CLASS:
  140. return "DHCPV6_OPTION_USER_CLASS"
  141. case DHCPV6_OPTION_VENDOR_CLASS:
  142. return "DHCPV6_OPTION_VENDOR_CLASS"
  143. case DHCPV6_OPTION_VENDOR_OPTS:
  144. return "DHCPV6_OPTION_VENDOR_OPTS"
  145. case DHCPV6_OPTION_INTERFACE_ID:
  146. return "DHCPV6_OPTION_INTERFACE_ID"
  147. case DHCPV6_OPTION_RECONF_MSG:
  148. return "DHCPV6_OPTION_RECONF_MSG"
  149. case DHCPV6_OPTION_RECONF_ACCEPT:
  150. return "DHCPV6_OPTION_RECONF_ACCEPT"
  151. case DHCPV6_OPTION_IA_PD:
  152. return "DHCPV6_OPTION_IA_PD"
  153. case DHCPV6_OPTION_IAPREFIX:
  154. return "DHCPV6_OPTION_IAPREFIX"
  155. case DHCPV6_OPTION_INFORMATION_REFRESH_TIME:
  156. return "DHCPV6_OPTION_INFORMATION_REFRESH_TIME"
  157. case DHCPV6_OPTION_SOL_MAX_RT:
  158. return "DHCPV6_OPTION_SOL_MAX_RT"
  159. case DHCPV6_OPTION_INF_MAX_RT:
  160. return "DHCPV6_OPTION_INF_MAX_RT"
  161. case OPTION_DNS_SERVERS:
  162. return "OPTION_DNS_SERVERS"
  163. case OPTION_DOMAIN_LIST:
  164. return "OPTION_DOMAIN_LIST"
  165. case OPTION_SNTP_SERVERS:
  166. return "OPTION_SNTP_SERVERS"
  167. case OPTION_NTP_SERVERS6:
  168. return "OPTION_NTP_SERVERS6"
  169. case OPTION_NEW_POSIX_TIMEZONE:
  170. return "OPTION_NEW_POSIX_TIMEZONE"
  171. case OPTION_NEW_TZDB_TIMEZONE:
  172. return "OPTION_NEW_TZDB_TIMEZONE"
  173. }
  174. return "DHCPV6_OPTION_UNKNOWN"
  175. }
  176. // DHCPv6 message type
  177. func (p Packet) Type6() MessageType {
  178. return MessageType(p[0])
  179. }
  180. // DHCPv6 transaction ID
  181. func (p Packet) TID6() (uint32, error) {
  182. if !p.IsRelayMsg() {
  183. if len(p) < 4 {
  184. return 0, errors.Wrapf(errors.ErrInvalidFormat, "packet too short")
  185. }
  186. return binary.BigEndian.Uint32([]byte{0, p[1], p[2], p[3]}), nil
  187. }
  188. options := p.GetOption6s()
  189. for _, o := range options {
  190. if o.Code == DHCPV6_OPTION_RELAY_MSG {
  191. return Packet(o.Value).TID6()
  192. }
  193. }
  194. return 0, errors.Wrapf(errors.ErrInvalidFormat, "not a valid relay message")
  195. }
  196. func (p Packet) ClientID() ([]byte, error) {
  197. if !p.IsRelayMsg() {
  198. options := p.GetOption6s()
  199. for _, o := range options {
  200. if o.Code == DHCPV6_OPTION_CLIENTID {
  201. return o.Value, nil
  202. }
  203. }
  204. return nil, errors.Wrapf(errors.ErrInvalidFormat, "clientID option not found")
  205. }
  206. options := p.GetOption6s()
  207. for _, o := range options {
  208. if o.Code == DHCPV6_OPTION_RELAY_MSG {
  209. return Packet(o.Value).ClientID()
  210. }
  211. }
  212. return nil, errors.Wrapf(errors.ErrInvalidFormat, "not a valid relay message")
  213. }
  214. // DHCPv6 hop Count for relay message
  215. func (p Packet) HopCount() byte {
  216. return p[1]
  217. }
  218. // DHCPv6 link address for relay message
  219. func (p Packet) LinkAddr() net.IP {
  220. return net.IP(p[2:18])
  221. }
  222. // DHCPv6 peer address for relay message
  223. func (p Packet) PeerAddr() net.IP {
  224. return net.IP(p[18:34])
  225. }
  226. func (p Packet) IsRelayMsg() bool {
  227. return p.Type6() == DHCPV6_RELAY_FORW || p.Type6() == DHCPV6_RELAY_REPL
  228. }
  229. func (p *Packet) SetType6(hType MessageType) {
  230. (*p)[0] = byte(hType)
  231. }
  232. func (p *Packet) SetTID(tid uint32) {
  233. tidBytes := make([]byte, 4)
  234. binary.BigEndian.PutUint32(tidBytes, tid)
  235. (*p)[1] = tidBytes[1]
  236. (*p)[2] = tidBytes[2]
  237. (*p)[3] = tidBytes[3]
  238. }
  239. func (p *Packet) SetHopCount(hops byte) {
  240. (*p)[1] = hops
  241. }
  242. func (p *Packet) SetLinkAddr(linkAddr net.IP) {
  243. copy((*p)[2:18], linkAddr)
  244. }
  245. func (p *Packet) SetPeerAddr(peerAddr net.IP) {
  246. copy((*p)[18:34], peerAddr)
  247. }
  248. func NewPacket6(opCode MessageType, tid uint32) Packet {
  249. p := make(Packet, 4)
  250. p.SetType6(opCode)
  251. p.SetTID(tid)
  252. return p
  253. }
  254. func NewRelayPacket6() Packet {
  255. p := make(Packet, 34)
  256. p.SetType6(DHCPV6_RELAY_FORW)
  257. return p
  258. }
  259. type Option6 struct {
  260. Code OptionCode6
  261. Value []byte
  262. }
  263. // Appends a DHCP option to the end of a packet
  264. /*
  265. 0 1 2 3
  266. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  267. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  268. | option-code | option-len |
  269. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  270. | option-data |
  271. | (option-len octets) |
  272. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  273. */
  274. func optionToBytes(o Option6) []byte {
  275. buf := make([]byte, 4+len(o.Value))
  276. binary.BigEndian.PutUint16(buf[0:2], uint16(o.Code))
  277. binary.BigEndian.PutUint16(buf[2:4], uint16(len(o.Value)))
  278. copy(buf[4:], o.Value)
  279. return buf
  280. }
  281. func optionsToBytes(opts []Option6) []byte {
  282. buf := make([]byte, 0)
  283. for i := range opts {
  284. buf = append(buf, optionToBytes(opts[i])...)
  285. }
  286. return buf
  287. }
  288. func (p Packet) GetOption6s() []Option6 {
  289. offset := 4
  290. if p.IsRelayMsg() {
  291. offset = 16*2 + 2
  292. }
  293. return decodeDHCP6Options(p, offset)
  294. }
  295. func decodeDHCP6Options(p []byte, offset int) []Option6 {
  296. options := make([]Option6, 0)
  297. i := offset
  298. for i < len(p) {
  299. code := binary.BigEndian.Uint16(p[i : i+2])
  300. length := binary.BigEndian.Uint16(p[i+2 : i+4])
  301. value := make([]byte, length)
  302. copy(value, p[i+4:i+4+int(length)])
  303. options = append(options, Option6{
  304. Code: OptionCode6(code),
  305. Value: value,
  306. })
  307. i += 4 + int(length)
  308. }
  309. return options
  310. }
  311. func MakeDHCP6Reply(pkt Packet, conf *ResponseConfig) (Packet, error) {
  312. var msgType MessageType
  313. pktType := pkt.Type6()
  314. switch pktType {
  315. case DHCPV6_SOLICIT:
  316. msgType = DHCPV6_ADVERTISE
  317. case DHCPV6_REQUEST:
  318. msgType = DHCPV6_REPLY
  319. case DHCPV6_CONFIRM:
  320. msgType = DHCPV6_REPLY
  321. case DHCPV6_RENEW:
  322. msgType = DHCPV6_REPLY
  323. case DHCPV6_REBIND:
  324. msgType = DHCPV6_REPLY
  325. case DHCPV6_INFORMATION_REQUEST:
  326. msgType = DHCPV6_REPLY
  327. default:
  328. return nil, errors.Wrapf(errors.ErrNotSupported, "unsupported message type %d", pktType)
  329. }
  330. return makeDHCPReplyPacket6(pkt, conf, msgType)
  331. }
  332. const (
  333. DUID_TYPE_LINK_LAYER_ADDRESS = 3
  334. DUID_HARDWARE_TYPE_ETHERNET = 1
  335. )
  336. func makeServerId(serverMac net.HardwareAddr) []byte {
  337. buf := make([]byte, 4)
  338. binary.BigEndian.PutUint16(buf[0:2], uint16(DUID_TYPE_LINK_LAYER_ADDRESS))
  339. binary.BigEndian.PutUint16(buf[2:4], uint16(DUID_HARDWARE_TYPE_ETHERNET))
  340. buf = append(buf, serverMac[:6]...)
  341. return buf
  342. }
  343. func makeIAAddr(ip net.IP, preferLT, validLT uint32, opts []Option6) []byte {
  344. buf := make([]byte, 24)
  345. copy(buf[0:16], ip.To16())
  346. binary.BigEndian.PutUint32(buf[16:20], preferLT)
  347. binary.BigEndian.PutUint32(buf[20:24], validLT)
  348. buf = append(buf, optionsToBytes(opts)...)
  349. return buf
  350. }
  351. func decodeIAAddr(buf []byte) (net.IP, uint32, uint32, []Option6) {
  352. ipBuf := make([]byte, 16)
  353. copy(ipBuf, buf[0:16])
  354. ip := net.IP(ipBuf)
  355. preferLT := binary.BigEndian.Uint32(buf[16:20])
  356. validLT := binary.BigEndian.Uint32(buf[20:24])
  357. opts := decodeDHCP6Options(buf, 24)
  358. return ip, preferLT, validLT, opts
  359. }
  360. /*
  361. +---------------+------+--------------------------------------------+
  362. | Name | Code | Description |
  363. +---------------+------+--------------------------------------------+
  364. | Success | 0 | Success. |
  365. | | | |
  366. | UnspecFail | 1 | Failure, reason unspecified; this status |
  367. | | | code is sent by either a client or a |
  368. | | | server to indicate a failure not |
  369. | | | explicitly specified in this document. |
  370. | | | |
  371. | NoAddrsAvail | 2 | The server has no addresses available to |
  372. | | | assign to the IA(s). |
  373. | | | |
  374. | NoBinding | 3 | Client record (binding) unavailable. |
  375. | | | |
  376. | NotOnLink | 4 | The prefix for the address is not |
  377. | | | appropriate for the link to which the |
  378. | | | client is attached. |
  379. | | | |
  380. | UseMulticast | 5 | Sent by a server to a client to force the |
  381. | | | client to send messages to the server |
  382. | | | using the |
  383. | | | All_DHCP_Relay_Agents_and_Servers |
  384. | | | multicast address. |
  385. | | | |
  386. | NoPrefixAvail | 6 | The server has no prefixes available to |
  387. | | | assign to the IA_PD(s). |
  388. +---------------+------+--------------------------------------------+
  389. */
  390. type DHCP6StatusCode uint16
  391. const (
  392. Dhcp6StatusSuccess DHCP6StatusCode = 0
  393. Dhcp6StatusUnspecFail DHCP6StatusCode = 1
  394. Dhcp6StatusNoAddrsAvail DHCP6StatusCode = 2
  395. Dhcp6StatusNoBinding DHCP6StatusCode = 3
  396. Dhcp6StatusNotOnLink DHCP6StatusCode = 4
  397. Dhcp6StatusUseMulticast DHCP6StatusCode = 5
  398. Dhcp6StatusNoPrefixAvail DHCP6StatusCode = 6
  399. )
  400. func (code DHCP6StatusCode) Encode() []byte {
  401. var msg string
  402. switch code {
  403. case Dhcp6StatusSuccess:
  404. msg = "Success"
  405. case Dhcp6StatusUnspecFail:
  406. msg = "UnspecFail"
  407. case Dhcp6StatusNoAddrsAvail:
  408. msg = "NoAddrsAvail"
  409. case Dhcp6StatusNoBinding:
  410. msg = "NoBinding"
  411. case Dhcp6StatusNotOnLink:
  412. msg = "NotOnLink"
  413. case Dhcp6StatusUseMulticast:
  414. msg = "UseMulticast"
  415. case Dhcp6StatusNoPrefixAvail:
  416. msg = "NoPrefixAvail"
  417. }
  418. buf := make([]byte, 2)
  419. binary.BigEndian.PutUint16(buf, uint16(code))
  420. buf = append(buf, []byte(msg)...)
  421. return buf
  422. }
  423. func responseIANA(buf []byte, ip net.IP, preferLT, validLT uint32) ([]byte, DHCP6StatusCode) {
  424. // IA_NA structure: IAID (4 bytes) + T1 (4 bytes) + T2 (4 bytes)
  425. // Preserve the original IAID, T1, and T2 values from the client request
  426. opts := make([]Option6, 0)
  427. status := Dhcp6StatusSuccess
  428. resp := make([]byte, 12)
  429. if len(buf) < 12 {
  430. copy(resp, buf)
  431. // If buffer is too short, pad with zeros
  432. // padding := make([]byte, 12-len(buf))
  433. // copy(resp[len(buf):], padding)
  434. } else {
  435. // Log the IA_NA parameters for debugging
  436. iaID := binary.BigEndian.Uint32(buf[0:4])
  437. t1 := binary.BigEndian.Uint32(buf[4:8])
  438. t2 := binary.BigEndian.Uint32(buf[8:12])
  439. log.Debugf("responseIANA IA_NA IAID %d t1 %d t2 %d", iaID, t1, t2)
  440. if len(buf) > 12 {
  441. iaOpts := decodeDHCP6Options(buf, 12)
  442. for i := range iaOpts {
  443. if iaOpts[i].Code == DHCPV6_OPTION_IAADDR {
  444. oldIp, oldPreferLT, oldValidLT, _ := decodeIAAddr(iaOpts[i].Value)
  445. if !oldIp.Equal(ip) {
  446. // send NotOnLink
  447. opts = append(opts, Option6{
  448. Code: DHCPV6_OPTION_IAADDR,
  449. Value: makeIAAddr(oldIp, oldPreferLT, oldValidLT, []Option6{
  450. {
  451. Code: DHCPV6_OPTION_STATUS_CODE,
  452. Value: Dhcp6StatusNotOnLink.Encode(),
  453. },
  454. }),
  455. })
  456. if status == Dhcp6StatusSuccess {
  457. status = Dhcp6StatusNotOnLink
  458. }
  459. }
  460. }
  461. }
  462. }
  463. copy(resp, buf[:12])
  464. }
  465. opts = append(opts, Option6{
  466. Code: DHCPV6_OPTION_IAADDR,
  467. Value: makeIAAddr(ip, preferLT, validLT, []Option6{
  468. {
  469. Code: DHCPV6_OPTION_STATUS_CODE,
  470. Value: Dhcp6StatusSuccess.Encode(),
  471. },
  472. }),
  473. })
  474. resp = append(resp, optionsToBytes(opts)...)
  475. return resp, status
  476. }
  477. func makeIPv6s(ips []net.IP) []byte {
  478. buf := make([]byte, 0)
  479. for _, ip := range ips {
  480. ip6 := ip.To16()
  481. if ip6 == nil {
  482. continue
  483. }
  484. buf = append(buf, ip6...)
  485. }
  486. return buf
  487. }
  488. func decodeRequestOptions(value []byte) []OptionCode6 {
  489. var optCodes []OptionCode6
  490. for i := 0; i < len(value); i += 2 {
  491. optCodes = append(optCodes, OptionCode6(binary.BigEndian.Uint16(value[i:i+2])))
  492. }
  493. return optCodes
  494. }
  495. func makeDHCPReplyPacket6(pkt Packet, conf *ResponseConfig, msgType MessageType) (Packet, error) {
  496. tid, err := pkt.TID6()
  497. if err != nil {
  498. return nil, errors.Wrapf(err, "TID6")
  499. }
  500. originOpts := pkt.GetOption6s()
  501. getOption := func(opts []Option6, code OptionCode6) *Option6 {
  502. for _, o := range opts {
  503. if o.Code == code {
  504. return &o
  505. }
  506. }
  507. return nil
  508. }
  509. reqInfo := getOption(originOpts, DHCPV6_OPTION_ORO)
  510. if reqInfo != nil && len(reqInfo.Value) > 0 {
  511. reqOpts := decodeRequestOptions(reqInfo.Value)
  512. reqOptsStr := make([]string, len(reqOpts))
  513. for i, opt := range reqOpts {
  514. reqOptsStr[i] = opt.String()
  515. }
  516. log.Debugf("request options: %s", strings.Join(reqOptsStr, ","))
  517. }
  518. options := make([]Option6, 0)
  519. reqCliID := getOption(originOpts, DHCPV6_OPTION_CLIENTID)
  520. if reqCliID == nil {
  521. return nil, errors.Wrapf(errors.ErrInvalidFormat, "clientID option not found")
  522. }
  523. // copy clientID
  524. options = append(options, Option6{
  525. Code: DHCPV6_OPTION_CLIENTID,
  526. Value: reqCliID.Value,
  527. })
  528. // serverID
  529. options = append(options, Option6{
  530. Code: DHCPV6_OPTION_SERVERID,
  531. Value: makeServerId(conf.InterfaceMac),
  532. })
  533. // Identity Association for Non-temporary Addresses Option
  534. ianaOpt := getOption(originOpts, DHCPV6_OPTION_IA_NA)
  535. if ianaOpt == nil {
  536. return nil, errors.Wrapf(errors.ErrInvalidFormat, "IA_NA option not found")
  537. }
  538. // Calculate proper timing values for IA_NA
  539. validLifetime := uint32(conf.LeaseTime.Seconds()) // Valid lifetime should be longer than preferred
  540. preferredLifetime := validLifetime / 2
  541. ianaResp, status := responseIANA(ianaOpt.Value, conf.ClientIP6, preferredLifetime, validLifetime)
  542. options = append(options, Option6{
  543. Code: DHCPV6_OPTION_IA_NA,
  544. Value: ianaResp,
  545. })
  546. if len(conf.DNSServers6) > 0 {
  547. options = append(options, Option6{
  548. Code: OPTION_DNS_SERVERS,
  549. Value: makeIPv6s(conf.DNSServers6),
  550. })
  551. }
  552. if len(conf.NTPServers6) > 0 {
  553. options = append(options, Option6{
  554. Code: OPTION_SNTP_SERVERS,
  555. Value: makeIPv6s(conf.NTPServers6),
  556. })
  557. }
  558. // Handle rapid commit option for SOLICIT messages
  559. if pkt.Type6() == DHCPV6_SOLICIT {
  560. rapidCmtOpt := getOption(originOpts, DHCPV6_OPTION_RAPID_COMMIT)
  561. if rapidCmtOpt != nil {
  562. // Client requested rapid commit, respond with REPLY instead of ADVERTISE
  563. msgType = DHCPV6_REPLY
  564. options = append(options, Option6{
  565. Code: DHCPV6_OPTION_RAPID_COMMIT,
  566. })
  567. }
  568. }
  569. options = append(options, Option6{
  570. Code: DHCPV6_OPTION_STATUS_CODE,
  571. Value: status.Encode(),
  572. })
  573. resp := NewPacket6(msgType, tid)
  574. resp = append(resp, optionsToBytes(options)...)
  575. return resp, nil
  576. }
  577. func EncapDHCP6RelayMsg(pkt Packet) Packet {
  578. relayMsg := NewRelayPacket6()
  579. relayOpt := Option6{
  580. Code: DHCPV6_OPTION_RELAY_MSG,
  581. Value: pkt,
  582. }
  583. relayMsg = append(relayMsg, optionsToBytes([]Option6{relayOpt})...)
  584. return relayMsg
  585. }
  586. func DecapDHCP6RelayMsg(pkt Packet) (Packet, error) {
  587. options := pkt.GetOption6s()
  588. for _, o := range options {
  589. if o.Code == DHCPV6_OPTION_RELAY_MSG {
  590. return Packet(o.Value), nil
  591. }
  592. }
  593. return nil, errors.Wrapf(errors.ErrInvalidFormat, "relay message not found")
  594. }