mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
2732cc3cf9
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
652 lines
20 KiB
Go
652 lines
20 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package dhcp
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"net"
|
|
"strings"
|
|
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
)
|
|
|
|
// DHCPv6 https://datatracker.ietf.org/doc/html/rfc8415
|
|
/*
|
|
0 1 2 3
|
|
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
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| msg-type | transaction-id |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| |
|
|
. options .
|
|
. (variable number and length) .
|
|
| |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
/*
|
|
0 1 2 3
|
|
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
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| msg-type | hop-count | |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
|
| |
|
|
| link-address |
|
|
| |
|
|
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|
|
| | |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
|
| |
|
|
| peer-address |
|
|
| |
|
|
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|
|
| | |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
|
. .
|
|
. options (variable number and length) .... .
|
|
| |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
|
|
// DHCPv6 Message Type
|
|
const (
|
|
DHCPV6_SOLICIT MessageType = 1
|
|
DHCPV6_ADVERTISE MessageType = 2
|
|
DHCPV6_REQUEST MessageType = 3
|
|
DHCPV6_CONFIRM MessageType = 4
|
|
DHCPV6_RENEW MessageType = 5
|
|
DHCPV6_REBIND MessageType = 6
|
|
DHCPV6_REPLY MessageType = 7
|
|
DHCPV6_RELEASE MessageType = 8
|
|
DHCPV6_DECLINE MessageType = 9
|
|
DHCPV6_RECONFIGURE MessageType = 10
|
|
DHCPV6_INFORMATION_REQUEST MessageType = 11
|
|
DHCPV6_RELAY_FORW MessageType = 12
|
|
DHCPV6_RELAY_REPL MessageType = 13
|
|
)
|
|
|
|
type OptionCode6 uint16
|
|
|
|
const (
|
|
DHCPV6_OPTION_CLIENTID OptionCode6 = 1
|
|
DHCPV6_OPTION_SERVERID OptionCode6 = 2
|
|
DHCPV6_OPTION_IA_NA OptionCode6 = 3
|
|
DHCPV6_OPTION_IA_TA OptionCode6 = 4
|
|
DHCPV6_OPTION_IAADDR OptionCode6 = 5
|
|
DHCPV6_OPTION_ORO OptionCode6 = 6
|
|
DHCPV6_OPTION_PREFERENCE OptionCode6 = 7
|
|
DHCPV6_OPTION_ELAPSED_TIME OptionCode6 = 8
|
|
DHCPV6_OPTION_RELAY_MSG OptionCode6 = 9
|
|
DHCPV6_OPTION_AUTH OptionCode6 = 11
|
|
DHCPV6_OPTION_UNICAST OptionCode6 = 12
|
|
DHCPV6_OPTION_STATUS_CODE OptionCode6 = 13
|
|
DHCPV6_OPTION_RAPID_COMMIT OptionCode6 = 14
|
|
DHCPV6_OPTION_USER_CLASS OptionCode6 = 15
|
|
DHCPV6_OPTION_VENDOR_CLASS OptionCode6 = 16
|
|
DHCPV6_OPTION_VENDOR_OPTS OptionCode6 = 17
|
|
DHCPV6_OPTION_INTERFACE_ID OptionCode6 = 18
|
|
DHCPV6_OPTION_RECONF_MSG OptionCode6 = 19
|
|
DHCPV6_OPTION_RECONF_ACCEPT OptionCode6 = 20
|
|
DHCPV6_OPTION_IA_PD OptionCode6 = 25
|
|
DHCPV6_OPTION_IAPREFIX OptionCode6 = 26
|
|
DHCPV6_OPTION_INFORMATION_REFRESH_TIME OptionCode6 = 32
|
|
DHCPV6_OPTION_SOL_MAX_RT OptionCode6 = 82
|
|
DHCPV6_OPTION_INF_MAX_RT OptionCode6 = 83
|
|
|
|
// https://www.rfc-editor.org/rfc/rfc3646
|
|
OPTION_DNS_SERVERS OptionCode6 = 23
|
|
OPTION_DOMAIN_LIST OptionCode6 = 24
|
|
// https://www.rfc-editor.org/rfc/rfc4075
|
|
OPTION_SNTP_SERVERS OptionCode6 = 31
|
|
//https://www.rfc-editor.org/rfc/rfc5908
|
|
OPTION_NTP_SERVERS6 OptionCode6 = 56
|
|
// https://www.rfc-editor.org/rfc/rfc4833
|
|
OPTION_NEW_POSIX_TIMEZONE OptionCode6 = 41
|
|
OPTION_NEW_TZDB_TIMEZONE OptionCode6 = 42
|
|
)
|
|
|
|
func (opt OptionCode6) String() string {
|
|
switch opt {
|
|
case DHCPV6_OPTION_CLIENTID:
|
|
return "DHCPV6_OPTION_CLIENTID"
|
|
case DHCPV6_OPTION_SERVERID:
|
|
return "DHCPV6_OPTION_SERVERID"
|
|
case DHCPV6_OPTION_IA_NA:
|
|
return "DHCPV6_OPTION_IA_NA"
|
|
case DHCPV6_OPTION_IA_TA:
|
|
return "DHCPV6_OPTION_IA_TA"
|
|
case DHCPV6_OPTION_IAADDR:
|
|
return "DHCPV6_OPTION_IAADDR"
|
|
case DHCPV6_OPTION_ORO:
|
|
return "DHCPV6_OPTION_ORO"
|
|
case DHCPV6_OPTION_PREFERENCE:
|
|
return "DHCPV6_OPTION_PREFERENCE"
|
|
case DHCPV6_OPTION_ELAPSED_TIME:
|
|
return "DHCPV6_OPTION_ELAPSED_TIME"
|
|
case DHCPV6_OPTION_RELAY_MSG:
|
|
return "DHCPV6_OPTION_RELAY_MSG"
|
|
case DHCPV6_OPTION_AUTH:
|
|
return "DHCPV6_OPTION_AUTH"
|
|
case DHCPV6_OPTION_UNICAST:
|
|
return "DHCPV6_OPTION_UNICAST"
|
|
case DHCPV6_OPTION_STATUS_CODE:
|
|
return "DHCPV6_OPTION_STATUS_CODE"
|
|
case DHCPV6_OPTION_RAPID_COMMIT:
|
|
return "DHCPV6_OPTION_RAPID_COMMIT"
|
|
case DHCPV6_OPTION_USER_CLASS:
|
|
return "DHCPV6_OPTION_USER_CLASS"
|
|
case DHCPV6_OPTION_VENDOR_CLASS:
|
|
return "DHCPV6_OPTION_VENDOR_CLASS"
|
|
case DHCPV6_OPTION_VENDOR_OPTS:
|
|
return "DHCPV6_OPTION_VENDOR_OPTS"
|
|
case DHCPV6_OPTION_INTERFACE_ID:
|
|
return "DHCPV6_OPTION_INTERFACE_ID"
|
|
case DHCPV6_OPTION_RECONF_MSG:
|
|
return "DHCPV6_OPTION_RECONF_MSG"
|
|
case DHCPV6_OPTION_RECONF_ACCEPT:
|
|
return "DHCPV6_OPTION_RECONF_ACCEPT"
|
|
case DHCPV6_OPTION_IA_PD:
|
|
return "DHCPV6_OPTION_IA_PD"
|
|
case DHCPV6_OPTION_IAPREFIX:
|
|
return "DHCPV6_OPTION_IAPREFIX"
|
|
case DHCPV6_OPTION_INFORMATION_REFRESH_TIME:
|
|
return "DHCPV6_OPTION_INFORMATION_REFRESH_TIME"
|
|
case DHCPV6_OPTION_SOL_MAX_RT:
|
|
return "DHCPV6_OPTION_SOL_MAX_RT"
|
|
case DHCPV6_OPTION_INF_MAX_RT:
|
|
return "DHCPV6_OPTION_INF_MAX_RT"
|
|
case OPTION_DNS_SERVERS:
|
|
return "OPTION_DNS_SERVERS"
|
|
case OPTION_DOMAIN_LIST:
|
|
return "OPTION_DOMAIN_LIST"
|
|
case OPTION_SNTP_SERVERS:
|
|
return "OPTION_SNTP_SERVERS"
|
|
case OPTION_NTP_SERVERS6:
|
|
return "OPTION_NTP_SERVERS6"
|
|
case OPTION_NEW_POSIX_TIMEZONE:
|
|
return "OPTION_NEW_POSIX_TIMEZONE"
|
|
case OPTION_NEW_TZDB_TIMEZONE:
|
|
return "OPTION_NEW_TZDB_TIMEZONE"
|
|
}
|
|
return "DHCPV6_OPTION_UNKNOWN"
|
|
}
|
|
|
|
// DHCPv6 message type
|
|
func (p Packet) Type6() MessageType {
|
|
return MessageType(p[0])
|
|
}
|
|
|
|
// DHCPv6 transaction ID
|
|
func (p Packet) TID6() (uint32, error) {
|
|
if !p.IsRelayMsg() {
|
|
if len(p) < 4 {
|
|
return 0, errors.Wrapf(errors.ErrInvalidFormat, "packet too short")
|
|
}
|
|
return binary.BigEndian.Uint32([]byte{0, p[1], p[2], p[3]}), nil
|
|
}
|
|
options := p.GetOption6s()
|
|
for _, o := range options {
|
|
if o.Code == DHCPV6_OPTION_RELAY_MSG {
|
|
return Packet(o.Value).TID6()
|
|
}
|
|
}
|
|
return 0, errors.Wrapf(errors.ErrInvalidFormat, "not a valid relay message")
|
|
}
|
|
|
|
func (p Packet) ClientID() ([]byte, error) {
|
|
if !p.IsRelayMsg() {
|
|
options := p.GetOption6s()
|
|
for _, o := range options {
|
|
if o.Code == DHCPV6_OPTION_CLIENTID {
|
|
return o.Value, nil
|
|
}
|
|
}
|
|
return nil, errors.Wrapf(errors.ErrInvalidFormat, "clientID option not found")
|
|
}
|
|
options := p.GetOption6s()
|
|
for _, o := range options {
|
|
if o.Code == DHCPV6_OPTION_RELAY_MSG {
|
|
return Packet(o.Value).ClientID()
|
|
}
|
|
}
|
|
return nil, errors.Wrapf(errors.ErrInvalidFormat, "not a valid relay message")
|
|
}
|
|
|
|
// DHCPv6 hop Count for relay message
|
|
func (p Packet) HopCount() byte {
|
|
return p[1]
|
|
}
|
|
|
|
// DHCPv6 link address for relay message
|
|
func (p Packet) LinkAddr() net.IP {
|
|
return net.IP(p[2:18])
|
|
}
|
|
|
|
// DHCPv6 peer address for relay message
|
|
func (p Packet) PeerAddr() net.IP {
|
|
return net.IP(p[18:34])
|
|
}
|
|
|
|
func (p Packet) IsRelayMsg() bool {
|
|
return p.Type6() == DHCPV6_RELAY_FORW || p.Type6() == DHCPV6_RELAY_REPL
|
|
}
|
|
|
|
func (p *Packet) SetType6(hType MessageType) {
|
|
(*p)[0] = byte(hType)
|
|
}
|
|
|
|
func (p *Packet) SetTID(tid uint32) {
|
|
tidBytes := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(tidBytes, tid)
|
|
(*p)[1] = tidBytes[1]
|
|
(*p)[2] = tidBytes[2]
|
|
(*p)[3] = tidBytes[3]
|
|
}
|
|
|
|
func (p *Packet) SetHopCount(hops byte) {
|
|
(*p)[1] = hops
|
|
}
|
|
|
|
func (p *Packet) SetLinkAddr(linkAddr net.IP) {
|
|
copy((*p)[2:18], linkAddr)
|
|
}
|
|
|
|
func (p *Packet) SetPeerAddr(peerAddr net.IP) {
|
|
copy((*p)[18:34], peerAddr)
|
|
}
|
|
|
|
func NewPacket6(opCode MessageType, tid uint32) Packet {
|
|
p := make(Packet, 4)
|
|
p.SetType6(opCode)
|
|
p.SetTID(tid)
|
|
return p
|
|
}
|
|
|
|
func NewRelayPacket6() Packet {
|
|
p := make(Packet, 34)
|
|
p.SetType6(DHCPV6_RELAY_FORW)
|
|
return p
|
|
}
|
|
|
|
type Option6 struct {
|
|
Code OptionCode6
|
|
Value []byte
|
|
}
|
|
|
|
// Appends a DHCP option to the end of a packet
|
|
/*
|
|
0 1 2 3
|
|
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
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| option-code | option-len |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| option-data |
|
|
| (option-len octets) |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
func optionToBytes(o Option6) []byte {
|
|
buf := make([]byte, 4+len(o.Value))
|
|
binary.BigEndian.PutUint16(buf[0:2], uint16(o.Code))
|
|
binary.BigEndian.PutUint16(buf[2:4], uint16(len(o.Value)))
|
|
copy(buf[4:], o.Value)
|
|
return buf
|
|
}
|
|
|
|
func optionsToBytes(opts []Option6) []byte {
|
|
buf := make([]byte, 0)
|
|
for i := range opts {
|
|
buf = append(buf, optionToBytes(opts[i])...)
|
|
}
|
|
return buf
|
|
}
|
|
|
|
func (p Packet) GetOption6s() []Option6 {
|
|
offset := 4
|
|
if p.IsRelayMsg() {
|
|
offset = 16*2 + 2
|
|
}
|
|
return decodeDHCP6Options(p, offset)
|
|
}
|
|
|
|
func decodeDHCP6Options(p []byte, offset int) []Option6 {
|
|
options := make([]Option6, 0)
|
|
i := offset
|
|
for i < len(p) {
|
|
code := binary.BigEndian.Uint16(p[i : i+2])
|
|
length := binary.BigEndian.Uint16(p[i+2 : i+4])
|
|
value := make([]byte, length)
|
|
copy(value, p[i+4:i+4+int(length)])
|
|
options = append(options, Option6{
|
|
Code: OptionCode6(code),
|
|
Value: value,
|
|
})
|
|
i += 4 + int(length)
|
|
}
|
|
return options
|
|
}
|
|
|
|
func MakeDHCP6Reply(pkt Packet, conf *ResponseConfig) (Packet, error) {
|
|
var msgType MessageType
|
|
pktType := pkt.Type6()
|
|
switch pktType {
|
|
case DHCPV6_SOLICIT:
|
|
msgType = DHCPV6_ADVERTISE
|
|
case DHCPV6_REQUEST:
|
|
msgType = DHCPV6_REPLY
|
|
case DHCPV6_CONFIRM:
|
|
msgType = DHCPV6_REPLY
|
|
case DHCPV6_RENEW:
|
|
msgType = DHCPV6_REPLY
|
|
case DHCPV6_REBIND:
|
|
msgType = DHCPV6_REPLY
|
|
case DHCPV6_INFORMATION_REQUEST:
|
|
msgType = DHCPV6_REPLY
|
|
default:
|
|
return nil, errors.Wrapf(errors.ErrNotSupported, "unsupported message type %d", pktType)
|
|
}
|
|
|
|
return makeDHCPReplyPacket6(pkt, conf, msgType)
|
|
}
|
|
|
|
const (
|
|
DUID_TYPE_LINK_LAYER_ADDRESS = 3
|
|
DUID_HARDWARE_TYPE_ETHERNET = 1
|
|
)
|
|
|
|
func makeServerId(serverMac net.HardwareAddr) []byte {
|
|
buf := make([]byte, 4)
|
|
binary.BigEndian.PutUint16(buf[0:2], uint16(DUID_TYPE_LINK_LAYER_ADDRESS))
|
|
binary.BigEndian.PutUint16(buf[2:4], uint16(DUID_HARDWARE_TYPE_ETHERNET))
|
|
buf = append(buf, serverMac[:6]...)
|
|
return buf
|
|
}
|
|
|
|
func makeIAAddr(ip net.IP, preferLT, validLT uint32, opts []Option6) []byte {
|
|
buf := make([]byte, 24)
|
|
copy(buf[0:16], ip.To16())
|
|
binary.BigEndian.PutUint32(buf[16:20], preferLT)
|
|
binary.BigEndian.PutUint32(buf[20:24], validLT)
|
|
buf = append(buf, optionsToBytes(opts)...)
|
|
return buf
|
|
}
|
|
|
|
func decodeIAAddr(buf []byte) (net.IP, uint32, uint32, []Option6) {
|
|
ipBuf := make([]byte, 16)
|
|
copy(ipBuf, buf[0:16])
|
|
ip := net.IP(ipBuf)
|
|
preferLT := binary.BigEndian.Uint32(buf[16:20])
|
|
validLT := binary.BigEndian.Uint32(buf[20:24])
|
|
opts := decodeDHCP6Options(buf, 24)
|
|
return ip, preferLT, validLT, opts
|
|
}
|
|
|
|
/*
|
|
+---------------+------+--------------------------------------------+
|
|
| Name | Code | Description |
|
|
+---------------+------+--------------------------------------------+
|
|
| Success | 0 | Success. |
|
|
| | | |
|
|
| UnspecFail | 1 | Failure, reason unspecified; this status |
|
|
| | | code is sent by either a client or a |
|
|
| | | server to indicate a failure not |
|
|
| | | explicitly specified in this document. |
|
|
| | | |
|
|
| NoAddrsAvail | 2 | The server has no addresses available to |
|
|
| | | assign to the IA(s). |
|
|
| | | |
|
|
| NoBinding | 3 | Client record (binding) unavailable. |
|
|
| | | |
|
|
| NotOnLink | 4 | The prefix for the address is not |
|
|
| | | appropriate for the link to which the |
|
|
| | | client is attached. |
|
|
| | | |
|
|
| UseMulticast | 5 | Sent by a server to a client to force the |
|
|
| | | client to send messages to the server |
|
|
| | | using the |
|
|
| | | All_DHCP_Relay_Agents_and_Servers |
|
|
| | | multicast address. |
|
|
| | | |
|
|
| NoPrefixAvail | 6 | The server has no prefixes available to |
|
|
| | | assign to the IA_PD(s). |
|
|
+---------------+------+--------------------------------------------+
|
|
*/
|
|
type DHCP6StatusCode uint16
|
|
|
|
const (
|
|
Dhcp6StatusSuccess DHCP6StatusCode = 0
|
|
Dhcp6StatusUnspecFail DHCP6StatusCode = 1
|
|
Dhcp6StatusNoAddrsAvail DHCP6StatusCode = 2
|
|
Dhcp6StatusNoBinding DHCP6StatusCode = 3
|
|
Dhcp6StatusNotOnLink DHCP6StatusCode = 4
|
|
Dhcp6StatusUseMulticast DHCP6StatusCode = 5
|
|
Dhcp6StatusNoPrefixAvail DHCP6StatusCode = 6
|
|
)
|
|
|
|
func (code DHCP6StatusCode) Encode() []byte {
|
|
var msg string
|
|
switch code {
|
|
case Dhcp6StatusSuccess:
|
|
msg = "Success"
|
|
case Dhcp6StatusUnspecFail:
|
|
msg = "UnspecFail"
|
|
case Dhcp6StatusNoAddrsAvail:
|
|
msg = "NoAddrsAvail"
|
|
case Dhcp6StatusNoBinding:
|
|
msg = "NoBinding"
|
|
case Dhcp6StatusNotOnLink:
|
|
msg = "NotOnLink"
|
|
case Dhcp6StatusUseMulticast:
|
|
msg = "UseMulticast"
|
|
case Dhcp6StatusNoPrefixAvail:
|
|
msg = "NoPrefixAvail"
|
|
}
|
|
buf := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(buf, uint16(code))
|
|
buf = append(buf, []byte(msg)...)
|
|
return buf
|
|
}
|
|
|
|
func responseIANA(buf []byte, ip net.IP, preferLT, validLT uint32) ([]byte, DHCP6StatusCode) {
|
|
// IA_NA structure: IAID (4 bytes) + T1 (4 bytes) + T2 (4 bytes)
|
|
// Preserve the original IAID, T1, and T2 values from the client request
|
|
opts := make([]Option6, 0)
|
|
status := Dhcp6StatusSuccess
|
|
resp := make([]byte, 12)
|
|
if len(buf) < 12 {
|
|
copy(resp, buf)
|
|
// If buffer is too short, pad with zeros
|
|
// padding := make([]byte, 12-len(buf))
|
|
// copy(resp[len(buf):], padding)
|
|
} else {
|
|
// Log the IA_NA parameters for debugging
|
|
iaID := binary.BigEndian.Uint32(buf[0:4])
|
|
t1 := binary.BigEndian.Uint32(buf[4:8])
|
|
t2 := binary.BigEndian.Uint32(buf[8:12])
|
|
log.Debugf("responseIANA IA_NA IAID %d t1 %d t2 %d", iaID, t1, t2)
|
|
if len(buf) > 12 {
|
|
iaOpts := decodeDHCP6Options(buf, 12)
|
|
for i := range iaOpts {
|
|
if iaOpts[i].Code == DHCPV6_OPTION_IAADDR {
|
|
oldIp, oldPreferLT, oldValidLT, _ := decodeIAAddr(iaOpts[i].Value)
|
|
if !oldIp.Equal(ip) {
|
|
// send NotOnLink
|
|
opts = append(opts, Option6{
|
|
Code: DHCPV6_OPTION_IAADDR,
|
|
Value: makeIAAddr(oldIp, oldPreferLT, oldValidLT, []Option6{
|
|
{
|
|
Code: DHCPV6_OPTION_STATUS_CODE,
|
|
Value: Dhcp6StatusNotOnLink.Encode(),
|
|
},
|
|
}),
|
|
})
|
|
if status == Dhcp6StatusSuccess {
|
|
status = Dhcp6StatusNotOnLink
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
copy(resp, buf[:12])
|
|
}
|
|
|
|
opts = append(opts, Option6{
|
|
Code: DHCPV6_OPTION_IAADDR,
|
|
Value: makeIAAddr(ip, preferLT, validLT, []Option6{
|
|
{
|
|
Code: DHCPV6_OPTION_STATUS_CODE,
|
|
Value: Dhcp6StatusSuccess.Encode(),
|
|
},
|
|
}),
|
|
})
|
|
|
|
resp = append(resp, optionsToBytes(opts)...)
|
|
return resp, status
|
|
}
|
|
|
|
func makeIPv6s(ips []net.IP) []byte {
|
|
buf := make([]byte, 0)
|
|
for _, ip := range ips {
|
|
ip6 := ip.To16()
|
|
if ip6 == nil {
|
|
continue
|
|
}
|
|
buf = append(buf, ip6...)
|
|
}
|
|
return buf
|
|
}
|
|
|
|
func decodeRequestOptions(value []byte) []OptionCode6 {
|
|
var optCodes []OptionCode6
|
|
for i := 0; i < len(value); i += 2 {
|
|
optCodes = append(optCodes, OptionCode6(binary.BigEndian.Uint16(value[i:i+2])))
|
|
}
|
|
return optCodes
|
|
}
|
|
|
|
func makeDHCPReplyPacket6(pkt Packet, conf *ResponseConfig, msgType MessageType) (Packet, error) {
|
|
tid, err := pkt.TID6()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "TID6")
|
|
}
|
|
|
|
originOpts := pkt.GetOption6s()
|
|
getOption := func(opts []Option6, code OptionCode6) *Option6 {
|
|
for _, o := range opts {
|
|
if o.Code == code {
|
|
return &o
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
reqInfo := getOption(originOpts, DHCPV6_OPTION_ORO)
|
|
if reqInfo != nil && len(reqInfo.Value) > 0 {
|
|
reqOpts := decodeRequestOptions(reqInfo.Value)
|
|
reqOptsStr := make([]string, len(reqOpts))
|
|
for i, opt := range reqOpts {
|
|
reqOptsStr[i] = opt.String()
|
|
}
|
|
log.Debugf("request options: %s", strings.Join(reqOptsStr, ","))
|
|
}
|
|
|
|
options := make([]Option6, 0)
|
|
|
|
reqCliID := getOption(originOpts, DHCPV6_OPTION_CLIENTID)
|
|
if reqCliID == nil {
|
|
return nil, errors.Wrapf(errors.ErrInvalidFormat, "clientID option not found")
|
|
}
|
|
// copy clientID
|
|
options = append(options, Option6{
|
|
Code: DHCPV6_OPTION_CLIENTID,
|
|
Value: reqCliID.Value,
|
|
})
|
|
// serverID
|
|
options = append(options, Option6{
|
|
Code: DHCPV6_OPTION_SERVERID,
|
|
Value: makeServerId(conf.InterfaceMac),
|
|
})
|
|
// Identity Association for Non-temporary Addresses Option
|
|
ianaOpt := getOption(originOpts, DHCPV6_OPTION_IA_NA)
|
|
if ianaOpt == nil {
|
|
return nil, errors.Wrapf(errors.ErrInvalidFormat, "IA_NA option not found")
|
|
}
|
|
// Calculate proper timing values for IA_NA
|
|
validLifetime := uint32(conf.LeaseTime.Seconds()) // Valid lifetime should be longer than preferred
|
|
preferredLifetime := validLifetime / 2
|
|
|
|
ianaResp, status := responseIANA(ianaOpt.Value, conf.ClientIP6, preferredLifetime, validLifetime)
|
|
|
|
options = append(options, Option6{
|
|
Code: DHCPV6_OPTION_IA_NA,
|
|
Value: ianaResp,
|
|
})
|
|
|
|
if len(conf.DNSServers6) > 0 {
|
|
options = append(options, Option6{
|
|
Code: OPTION_DNS_SERVERS,
|
|
Value: makeIPv6s(conf.DNSServers6),
|
|
})
|
|
}
|
|
|
|
if len(conf.NTPServers6) > 0 {
|
|
options = append(options, Option6{
|
|
Code: OPTION_SNTP_SERVERS,
|
|
Value: makeIPv6s(conf.NTPServers6),
|
|
})
|
|
}
|
|
|
|
// Handle rapid commit option for SOLICIT messages
|
|
if pkt.Type6() == DHCPV6_SOLICIT {
|
|
rapidCmtOpt := getOption(originOpts, DHCPV6_OPTION_RAPID_COMMIT)
|
|
if rapidCmtOpt != nil {
|
|
// Client requested rapid commit, respond with REPLY instead of ADVERTISE
|
|
msgType = DHCPV6_REPLY
|
|
options = append(options, Option6{
|
|
Code: DHCPV6_OPTION_RAPID_COMMIT,
|
|
})
|
|
}
|
|
}
|
|
|
|
options = append(options, Option6{
|
|
Code: DHCPV6_OPTION_STATUS_CODE,
|
|
Value: status.Encode(),
|
|
})
|
|
|
|
resp := NewPacket6(msgType, tid)
|
|
resp = append(resp, optionsToBytes(options)...)
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func EncapDHCP6RelayMsg(pkt Packet) Packet {
|
|
relayMsg := NewRelayPacket6()
|
|
relayOpt := Option6{
|
|
Code: DHCPV6_OPTION_RELAY_MSG,
|
|
Value: pkt,
|
|
}
|
|
relayMsg = append(relayMsg, optionsToBytes([]Option6{relayOpt})...)
|
|
return relayMsg
|
|
}
|
|
|
|
func DecapDHCP6RelayMsg(pkt Packet) (Packet, error) {
|
|
options := pkt.GetOption6s()
|
|
for _, o := range options {
|
|
if o.Code == DHCPV6_OPTION_RELAY_MSG {
|
|
return Packet(o.Value), nil
|
|
}
|
|
}
|
|
return nil, errors.Wrapf(errors.ErrInvalidFormat, "relay message not found")
|
|
}
|