mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
fix: dhcpv6 support phase 3 (#22881)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
+48
-17
@@ -27,11 +27,17 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Help bool `help:"Show help"`
|
||||
Help bool `help:"Show help"`
|
||||
|
||||
Interface string `help:"Listening interface, e.g. eth0"`
|
||||
Ip string `help:"Listening interface IP, e.g. 192.168.22.2"`
|
||||
Port int `help:"listening port" default:"67"`
|
||||
Relay string `help:"Relay server address, e.g. 192.168.22.23"`
|
||||
|
||||
Ip string `help:"Listening interface IP, e.g. 192.168.22.2"`
|
||||
Port int `help:"listening port" default:"67"`
|
||||
Relay string `help:"Relay server address, e.g. 192.168.22.23"`
|
||||
|
||||
Ip6 string `help:"Listening interface IP, e.g. 2001:db8::1"`
|
||||
Port6 int `help:"listening port" default:"547"`
|
||||
Relay6 string `help:"Relay server address, e.g. 2001:db8::23"`
|
||||
}
|
||||
|
||||
func relayMain() error {
|
||||
@@ -45,6 +51,10 @@ func relayMain() error {
|
||||
}
|
||||
|
||||
err = parse.ParseArgs(os.Args[1:], false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := parse.Options().(*Options)
|
||||
|
||||
if options.Help {
|
||||
@@ -55,24 +65,45 @@ func relayMain() error {
|
||||
if len(options.Interface) == 0 {
|
||||
return errors.Error("Missing interface")
|
||||
}
|
||||
if len(options.Ip) == 0 {
|
||||
return errors.Error("Missing interface IP")
|
||||
}
|
||||
if len(options.Relay) == 0 {
|
||||
return errors.Error("Missing DHCP relay server")
|
||||
if len(options.Ip) == 0 && len(options.Ip6) == 0 {
|
||||
return errors.Error("Missing interface IP or IP6")
|
||||
}
|
||||
|
||||
relayConfig := &hostdhcp.SDHCPRelayUpstream{}
|
||||
relayConfig.IP = options.Relay
|
||||
relayConfig.Port = 67
|
||||
srv, err := hostdhcp.NewGuestDHCPServer(options.Interface, options.Port, relayConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewGuestDHCPServer")
|
||||
if len(options.Ip) > 0 {
|
||||
if len(options.Relay) == 0 {
|
||||
return errors.Error("Missing DHCP relay server or relay server6")
|
||||
}
|
||||
|
||||
relayConfig := &hostdhcp.SDHCPRelayUpstream{}
|
||||
relayConfig.IP = options.Relay
|
||||
relayConfig.Port = 67
|
||||
srv, err := hostdhcp.NewGuestDHCPServer(options.Interface, options.Port, relayConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewGuestDHCPServer")
|
||||
}
|
||||
|
||||
srv.Start(false)
|
||||
|
||||
srv.RelaySetup(options.Ip)
|
||||
}
|
||||
|
||||
srv.Start(false)
|
||||
if len(options.Ip6) > 0 {
|
||||
if len(options.Relay6) == 0 {
|
||||
return errors.Error("Missing DHCP relay server or relay server6")
|
||||
}
|
||||
|
||||
srv.RelaySetup(options.Ip)
|
||||
relayConfig6 := &hostdhcp.SDHCPRelayUpstream{}
|
||||
relayConfig6.IP = options.Relay6
|
||||
relayConfig6.Port = options.Port6
|
||||
srv6, err := hostdhcp.NewGuestDHCP6Server(options.Interface, options.Port6, relayConfig6)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewGuestDHCP6Server")
|
||||
}
|
||||
|
||||
srv6.Start(false)
|
||||
|
||||
srv6.RelaySetup(options.Ip6)
|
||||
}
|
||||
|
||||
for {
|
||||
time.Sleep(time.Hour)
|
||||
|
||||
@@ -61,7 +61,10 @@ func GetNicDHCPConfig(
|
||||
}
|
||||
}
|
||||
|
||||
isDefaultGW := false
|
||||
if n.IsDefault == nil || *n.IsDefault {
|
||||
isDefaultGW = true
|
||||
|
||||
if n.Gateway != "" && !strings.HasPrefix(strings.ToLower(osName), "win") {
|
||||
routes4 = append(routes4, netutils2.SRouteInfo{
|
||||
SPrefixInfo: netutils2.SPrefixInfo{
|
||||
@@ -84,6 +87,7 @@ func GetNicDHCPConfig(
|
||||
|
||||
conf := &dhcp.ResponseConfig{
|
||||
InterfaceMac: serverMac,
|
||||
VlanId: uint16(n.VlanId),
|
||||
|
||||
ServerIP: serverIP,
|
||||
Domain: n.Domain,
|
||||
@@ -93,6 +97,8 @@ func GetNicDHCPConfig(
|
||||
Routes6: routes6,
|
||||
LeaseTime: time.Duration(o.Options.DhcpLeaseTime) * time.Second,
|
||||
RenewalTime: time.Duration(o.Options.DhcpRenewalTime) * time.Second,
|
||||
|
||||
IsDefaultGW: isDefaultGW,
|
||||
}
|
||||
|
||||
if n.IpAddr != "" {
|
||||
|
||||
@@ -1528,7 +1528,7 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
// TODO
|
||||
cmds.WriteString("IPV6INIT=yes\n")
|
||||
cmds.WriteString("DHCPV6C=yes\n")
|
||||
cmds.WriteString("IPV6_AUTOCONF=no\n")
|
||||
cmds.WriteString("IPV6_AUTOCONF=yes\n")
|
||||
// cmds.WriteString(fmt.Sprintf("IPV6ADDR=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
|
||||
// if len(nicDesc.Gateway6) > 0 {
|
||||
// cmds.WriteString("IPV6_DEFROUTE=yes\n", nicDesc.Gateway6))
|
||||
|
||||
@@ -21,10 +21,21 @@ import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/dhcp"
|
||||
)
|
||||
|
||||
type SRelayCache6 struct {
|
||||
peerMac net.HardwareAddr
|
||||
peerUdp *net.UDPAddr
|
||||
|
||||
linkAddr net.IP
|
||||
peerAddr net.IP
|
||||
|
||||
timer time.Time
|
||||
}
|
||||
|
||||
type SDHCP6Relay struct {
|
||||
server *dhcp.DHCP6Server
|
||||
OnRecv recvFunc
|
||||
@@ -53,13 +64,13 @@ func NewDHCP6Relay(guestDHCPConn *dhcp.Conn, config *SDHCPRelayUpstream) (*SDHCP
|
||||
func (r *SDHCP6Relay) Setup(addr string) error {
|
||||
r.ipv6srcAddr = net.ParseIP(addr)
|
||||
if len(r.ipv6srcAddr) == 0 {
|
||||
return fmt.Errorf("Wrong ip address %s", addr)
|
||||
return fmt.Errorf("wrong ip address %s", addr)
|
||||
}
|
||||
log.Infof("DHCP6 Relay Setup on %s %d", addr, DEFAULT_DHCP6_RELAY_PORT)
|
||||
var err error
|
||||
r.server, err = dhcp.NewDHCP6Server3(addr, DEFAULT_DHCP6_RELAY_PORT)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "NewDHCP6Server3")
|
||||
}
|
||||
go r.server.ListenAndServe(r)
|
||||
return nil
|
||||
@@ -70,52 +81,101 @@ func (r *SDHCP6Relay) ServeDHCP(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *
|
||||
return pkg, nil, err
|
||||
}
|
||||
|
||||
func (r *SDHCP6Relay) OnRecvICMP6(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) error {
|
||||
func (r *SDHCP6Relay) OnRecvICMP6(pkt dhcp.Packet) error {
|
||||
// null operation
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSessionKey(tid uint32, clientID []byte) string {
|
||||
return fmt.Sprintf("%x-%x", tid, clientID)
|
||||
}
|
||||
|
||||
func (r *SDHCP6Relay) serveDHCPInternal(pkt dhcp.Packet, _ *net.UDPAddr) (dhcp.Packet, error) {
|
||||
log.Infof("DHCP Relay Reply TO %s", pkt.CHAddr())
|
||||
v, ok := r.cache.Load(pkt.TransactionID())
|
||||
if pkt.Type6() != dhcp.DHCPV6_RELAY_REPL {
|
||||
return nil, errors.Wrapf(errors.ErrInvalidFormat, "not a valid relay reply message")
|
||||
}
|
||||
|
||||
hopCount := pkt.HopCount()
|
||||
decapPkt, err := dhcp.DecapDHCP6RelayMsg(pkt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "DecapDHCP6RelayMsg")
|
||||
}
|
||||
tid, err := decapPkt.TID6()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "TID6")
|
||||
}
|
||||
cliID, err := decapPkt.ClientID()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "ClientID")
|
||||
}
|
||||
|
||||
key := getSessionKey(tid, cliID)
|
||||
v, ok := r.cache.Load(key)
|
||||
if ok {
|
||||
r.cache.Delete(pkt.TransactionID())
|
||||
val := v.(*SRelayCache)
|
||||
udpAddr := &net.UDPAddr{
|
||||
IP: pkt.CIAddr(),
|
||||
Port: val.srcPort,
|
||||
}
|
||||
if err := r.guestDHCPConn.SendDHCP(pkt, udpAddr, pkt.CHAddr()); err != nil {
|
||||
log.Errorln(err)
|
||||
r.cache.Delete(key)
|
||||
val := v.(*SRelayCache6)
|
||||
|
||||
if hopCount > 1 {
|
||||
pkt.SetHopCount(hopCount - 1)
|
||||
pkt.SetLinkAddr(val.linkAddr)
|
||||
pkt.SetPeerAddr(val.peerAddr)
|
||||
if err := r.server.GetConn().SendDHCP(pkt, val.peerUdp, val.peerMac); err != nil {
|
||||
log.Errorf("send relay packet to client %s %s failed: %s", val.peerUdp, val.peerMac, err)
|
||||
}
|
||||
} else {
|
||||
pkt = decapPkt
|
||||
if err := r.guestDHCPConn.SendDHCP(pkt, val.peerUdp, val.peerMac); err != nil {
|
||||
log.Errorf("last hop send dhcp packet to client %s %s failed: %s", val.peerUdp, val.peerMac, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *SDHCP6Relay) Relay(pkt dhcp.Packet, addr *net.UDPAddr) (dhcp.Packet, error) {
|
||||
if addr.IP.Equal(r.ipv6srcAddr) {
|
||||
func (r *SDHCP6Relay) Relay(pkt dhcp.Packet, cliMac net.HardwareAddr, cliAddr *net.UDPAddr) (dhcp.Packet, error) {
|
||||
if cliAddr.IP.Equal(r.ipv6srcAddr) {
|
||||
// come from local? ignore it
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Infof("Receive DHCP Relay Rquest FROM %s %s", addr.IP, pkt.CHAddr())
|
||||
// clean cache first
|
||||
var now = time.Now().Add(time.Second * -30)
|
||||
r.cache.Range(func(key, value interface{}) bool {
|
||||
v := value.(*SRelayCache)
|
||||
if v.timer.Before(now) {
|
||||
r.cache.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
log.Infof("Receive IPv6 DHCPRequest FROM %s, relay to upstream %s:%d", cliAddr.IP, r.destaddr, r.destport)
|
||||
|
||||
// cache pkt info
|
||||
r.cache.Store(pkt.TransactionID(), &SRelayCache{
|
||||
mac: pkt.CHAddr(),
|
||||
srcPort: addr.Port,
|
||||
if pkt.Type6() == dhcp.DHCPV6_RELAY_REPL {
|
||||
return nil, errors.Wrapf(errors.ErrInvalidFormat, "cannot relay a reply message")
|
||||
}
|
||||
|
||||
session := &SRelayCache6{
|
||||
peerMac: cliMac,
|
||||
peerUdp: cliAddr,
|
||||
timer: time.Now(),
|
||||
})
|
||||
}
|
||||
hopCount := uint8(0)
|
||||
if pkt.Type6() == dhcp.DHCPV6_RELAY_FORW {
|
||||
hopCount = pkt.HopCount()
|
||||
session.linkAddr = pkt.LinkAddr()
|
||||
session.peerAddr = pkt.PeerAddr()
|
||||
} else {
|
||||
pkt = dhcp.EncapDHCP6RelayMsg(pkt)
|
||||
}
|
||||
pkt.SetHopCount(hopCount + 1)
|
||||
pkt.SetLinkAddr(r.ipv6srcAddr)
|
||||
pkt.SetPeerAddr(cliAddr.IP)
|
||||
|
||||
pkt.SetGIAddr(r.ipv6srcAddr)
|
||||
tid, err := pkt.TID6()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "TID6")
|
||||
}
|
||||
cliID, err := pkt.ClientID()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "ClientID")
|
||||
}
|
||||
sessionKey := getSessionKey(tid, cliID)
|
||||
r.cache.Store(sessionKey, session)
|
||||
|
||||
err := r.server.GetConn().SendDHCP(pkt, &net.UDPAddr{IP: r.destaddr, Port: r.destport}, nil)
|
||||
return nil, err
|
||||
err = r.server.GetConn().SendDHCP(pkt, &net.UDPAddr{IP: r.destaddr, Port: r.destport}, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SendDHCP to upstream %s:%d", r.destaddr, r.destport)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -186,15 +186,23 @@ func getGuestConfig(
|
||||
var conf = new(dhcp.ResponseConfig)
|
||||
|
||||
conf.InterfaceMac = serverMac
|
||||
conf.VlanId = uint16(nicdesc.Vlan)
|
||||
|
||||
nicIp := nicdesc.Ip
|
||||
v4Ip, _ := netutils.NewIPV4Addr(nicIp)
|
||||
conf.ClientIP = net.ParseIP(nicdesc.Ip)
|
||||
if len(nicdesc.Ip) > 0 {
|
||||
nicIp := nicdesc.Ip
|
||||
v4Ip, _ := netutils.NewIPV4Addr(nicIp)
|
||||
conf.ClientIP = net.ParseIP(nicdesc.Ip)
|
||||
|
||||
masklen := nicdesc.Masklen
|
||||
conf.ServerIP = net.ParseIP(v4Ip.NetAddr(int8(masklen)).String())
|
||||
conf.SubnetMask = net.ParseIP(netutils2.Netlen2Mask(int(masklen)))
|
||||
conf.BroadcastAddr = v4Ip.BroadcastAddr(int8(masklen)).ToBytes()
|
||||
|
||||
if nicdesc.Gateway != "" {
|
||||
conf.Gateway = net.ParseIP(nicdesc.Gateway)
|
||||
}
|
||||
}
|
||||
|
||||
masklen := nicdesc.Masklen
|
||||
conf.ServerIP = net.ParseIP(v4Ip.NetAddr(int8(masklen)).String())
|
||||
conf.SubnetMask = net.ParseIP(netutils2.Netlen2Mask(int(masklen)))
|
||||
conf.BroadcastAddr = v4Ip.BroadcastAddr(int8(masklen)).ToBytes()
|
||||
if len(guestDesc.Hostname) > 0 {
|
||||
conf.Hostname = guestDesc.Hostname
|
||||
} else {
|
||||
@@ -206,6 +214,10 @@ func getGuestConfig(
|
||||
if len(nicdesc.Ip6) > 0 {
|
||||
// ipv6
|
||||
conf.ClientIP6 = net.ParseIP(nicdesc.Ip6)
|
||||
conf.PrefixLen6 = uint8(nicdesc.Masklen6)
|
||||
if nicdesc.Gateway6 != "" {
|
||||
conf.Gateway6 = net.ParseIP(nicdesc.Gateway6)
|
||||
}
|
||||
}
|
||||
|
||||
// get main ip
|
||||
@@ -224,36 +236,34 @@ func getGuestConfig(
|
||||
route4 := make([]netutils2.SRouteInfo, 0)
|
||||
route6 := make([]netutils2.SRouteInfo, 0)
|
||||
if nicdesc.IsDefault {
|
||||
conf.IsDefaultGW = true
|
||||
|
||||
osName := guestDesc.OsName
|
||||
if len(osName) == 0 {
|
||||
osName = "Linux"
|
||||
}
|
||||
|
||||
if nicdesc.Gateway != "" {
|
||||
conf.Gateway = net.ParseIP(nicdesc.Gateway)
|
||||
|
||||
if conf.Gateway != nil {
|
||||
if !strings.HasPrefix(strings.ToLower(osName), "win") {
|
||||
route4 = append(route4, netutils2.SRouteInfo{
|
||||
SPrefixInfo: netutils2.SPrefixInfo{
|
||||
Prefix: net.ParseIP("0.0.0.0"),
|
||||
PrefixLen: 0,
|
||||
},
|
||||
Gateway: net.ParseIP(nicdesc.Gateway),
|
||||
Gateway: conf.Gateway,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(nicdesc.Gateway6) > 0 {
|
||||
conf.Gateway6 = net.ParseIP(nicdesc.Gateway6)
|
||||
conf.PrefixLen6 = uint8(nicdesc.Masklen6)
|
||||
route6 = append(route6, netutils2.SRouteInfo{
|
||||
SPrefixInfo: netutils2.SPrefixInfo{
|
||||
Prefix: net.ParseIP("::"),
|
||||
PrefixLen: 0,
|
||||
},
|
||||
Gateway: net.ParseIP(nicdesc.Gateway6),
|
||||
})
|
||||
}
|
||||
//if conf.Gateway6 != nil {
|
||||
/*route6 = append(route6, netutils2.SRouteInfo{
|
||||
SPrefixInfo: netutils2.SPrefixInfo{
|
||||
Prefix: net.ParseIP("::"),
|
||||
PrefixLen: 0,
|
||||
},
|
||||
Gateway: conf.Gateway6,
|
||||
})*/
|
||||
//}
|
||||
}
|
||||
route4, route6 = netutils2.AddNicRoutes(route4, route6, nicdesc, mainIp, mainIp6, len(guestNics))
|
||||
|
||||
@@ -317,7 +327,7 @@ func (s *SGuestDHCPServer) getConfig(pkt dhcp.Packet) *dhcp.ResponseConfig {
|
||||
if guestNic == nil {
|
||||
guestDesc, guestNic = guestman.GuestDescGetter.GetGuestNicDesc(mac, ip, port, s.ifaceDev.String(), !isCandidate)
|
||||
}
|
||||
if guestNic != nil && !guestNic.Virtual && len(guestNic.Ip6) > 0 {
|
||||
if guestNic != nil && !guestNic.Virtual {
|
||||
return getGuestConfig(guestDesc, guestNic, s.ifaceDev.GetHardwareAddr())
|
||||
}
|
||||
return nil
|
||||
@@ -338,7 +348,7 @@ func (s *SGuestDHCPServer) serveDHCPInternal(pkt dhcp.Packet, addr *net.UDPAddr)
|
||||
}
|
||||
var conf = s.getConfig(pkt)
|
||||
if conf != nil {
|
||||
log.Infof("Make DHCP Reply %s TO %s", conf.ClientIP, pkt.CHAddr())
|
||||
log.Infof("Make DHCP Reply %s TO %s %s", conf.ClientIP, pkt.CHAddr(), addr.String())
|
||||
// Guest request ip
|
||||
return dhcp.MakeReplyPacket(pkt, conf)
|
||||
} else if s.relay != nil && s.relay.server != nil {
|
||||
|
||||
@@ -16,12 +16,17 @@ package hostdhcp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
guestman "yunion.io/x/onecloud/pkg/hostman/guestman/types"
|
||||
"yunion.io/x/onecloud/pkg/util/dhcp"
|
||||
"yunion.io/x/onecloud/pkg/util/hashcache"
|
||||
"yunion.io/x/onecloud/pkg/util/icmp6"
|
||||
"yunion.io/x/onecloud/pkg/util/netutils2"
|
||||
)
|
||||
|
||||
@@ -37,6 +42,12 @@ type SGuestDHCP6Server struct {
|
||||
conn *dhcp.Conn
|
||||
|
||||
ifaceDev *netutils2.SNetInterface
|
||||
|
||||
raExitCh chan struct{}
|
||||
raReqCh chan *sRARequest
|
||||
raReqQueue []*sRARequest
|
||||
|
||||
gwMacCache *hashcache.Cache
|
||||
}
|
||||
|
||||
func NewGuestDHCP6Server(iface string, port int, relay *SDHCPRelayUpstream) (*SGuestDHCP6Server, error) {
|
||||
@@ -64,12 +75,20 @@ func NewGuestDHCP6Server(iface string, port int, relay *SDHCPRelayUpstream) (*SG
|
||||
}
|
||||
}
|
||||
|
||||
guestdhcp.raExitCh = make(chan struct{})
|
||||
guestdhcp.raReqCh = make(chan *sRARequest, 100)
|
||||
guestdhcp.raReqQueue = make([]*sRARequest, 0)
|
||||
guestdhcp.gwMacCache = hashcache.NewCache(1024, 5*time.Minute)
|
||||
|
||||
return guestdhcp, nil
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) Start(blocking bool) {
|
||||
log.Infof("SGuestDHCP6Server starting ...")
|
||||
serve := func() {
|
||||
defer s.stopRAServer()
|
||||
|
||||
go s.startRAServer()
|
||||
err := s.server.ListenAndServe(s)
|
||||
if err != nil {
|
||||
log.Errorf("DHCP serve error: %s", err)
|
||||
@@ -89,7 +108,7 @@ func (s *SGuestDHCP6Server) RelaySetup(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) getConfig(cliMac net.HardwareAddr, _ dhcp.Packet) *dhcp.ResponseConfig {
|
||||
func (s *SGuestDHCP6Server) getConfig(cliMac net.HardwareAddr) *dhcp.ResponseConfig {
|
||||
if guestman.GuestDescGetter == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -113,20 +132,40 @@ func (s *SGuestDHCP6Server) ServeDHCP(pkt dhcp.Packet, cliMac net.HardwareAddr,
|
||||
return pkg, nil, err
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) OnRecvICMP6(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) error {
|
||||
log.Infof("SGuestDHCP6Server ServeRA from %s", cliMac.String())
|
||||
return nil
|
||||
func (s *SGuestDHCP6Server) OnRecvICMP6(pkt dhcp.Packet) error {
|
||||
msg, err := icmp6.DecodePacket(pkt)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "icmp6.DecodePacket")
|
||||
}
|
||||
// log.Infof("SGuestDHCP6Server recv ICMP6 message %s", msg.String())
|
||||
switch msg.ICMP6TypeCode().Type() {
|
||||
case layers.ICMPv6TypeRouterSolicitation:
|
||||
// Router Solicitation
|
||||
return s.handleRouterSolicitation(msg.(*icmp6.SRouterSolicitation))
|
||||
case layers.ICMPv6TypeRouterAdvertisement:
|
||||
// Router Advertisement
|
||||
return s.handleRouterAdvertisement(msg.(*icmp6.SRouterAdvertisement))
|
||||
case layers.ICMPv6TypeNeighborSolicitation:
|
||||
// Neighbor Solicitation
|
||||
return s.handleNeighborSolicitation(msg.(*icmp6.SNeighborSolicitation))
|
||||
case layers.ICMPv6TypeNeighborAdvertisement:
|
||||
// Neighbor Advertisement
|
||||
return s.handleNeighborAdvertisement(msg.(*icmp6.SNeighborAdvertisement))
|
||||
default:
|
||||
log.Errorf("SGuestDHCP6Server recv unknown ICMP6 message %s", msg.String())
|
||||
return errors.Wrapf(errors.ErrNotSupported, "unknown ICMP6 message %s", msg.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) serveDHCPInternal(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (dhcp.Packet, error) {
|
||||
var conf = s.getConfig(cliMac, pkt)
|
||||
var conf = s.getConfig(cliMac)
|
||||
if conf != nil {
|
||||
log.Infof("Make DHCPv6 Reply %s TO %s %s", conf.ClientIP6, cliMac.String(), addr.String())
|
||||
// Guest request ip
|
||||
return dhcp.MakeDHCP6Reply(pkt, conf)
|
||||
} else if s.relay != nil && s.relay.server != nil {
|
||||
// Host agent as dhcp relay, relay to baremetal
|
||||
return s.relay.Relay(pkt, addr)
|
||||
return s.relay.Relay(pkt, cliMac, addr)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
// 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 hostdhcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/util/icmp6"
|
||||
)
|
||||
|
||||
type sRARequest struct {
|
||||
solicitation *icmp6.SRouterSolicitation
|
||||
attempts int
|
||||
succAttempts int
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) handleRouterSolicitation(msg *icmp6.SRouterSolicitation) error {
|
||||
// solicitation request from guest
|
||||
var conf = s.getConfig(msg.SrcMac)
|
||||
if conf != nil && conf.ClientIP6 != nil && conf.Gateway6 != nil {
|
||||
req := &sRARequest{
|
||||
solicitation: msg,
|
||||
attempts: 1,
|
||||
}
|
||||
succ, err := s.sendRouterAdvertisement(msg)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "sendRouterAdvertisement")
|
||||
}
|
||||
if succ {
|
||||
req.succAttempts++
|
||||
}
|
||||
s.raReqCh <- req
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) handleRouterAdvertisement(msg *icmp6.SRouterAdvertisement) error {
|
||||
// periodic router advertisement from local router
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) handleNeighborSolicitation(msg *icmp6.SNeighborSolicitation) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) handleNeighborAdvertisement(msg *icmp6.SNeighborAdvertisement) error {
|
||||
if msg.IsSolicited {
|
||||
//log.Infof("Save mac %s for gw IP %s", msg.SrcMac, msg.TargetAddr.String())
|
||||
s.gwMacCache.Set(msg.TargetAddr.String(), msg.SrcMac)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) requestGatewayMac(gwIP net.IP, vlanId uint16) {
|
||||
// Solicited-Node Multicast Address, FF02::1:FF00:0/104, 33:33:ff:00:00:00
|
||||
destIP := net.IP{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, gwIP[13], gwIP[14], gwIP[15]}
|
||||
destMac := net.HardwareAddr{0x33, 0x33, 0xff, gwIP[13], gwIP[14], gwIP[15]}
|
||||
|
||||
ns := &icmp6.SNeighborSolicitation{
|
||||
SBaseICMP6Message: icmp6.SBaseICMP6Message{
|
||||
SrcMac: s.ifaceDev.GetHardwareAddr(),
|
||||
SrcIP: net.ParseIP(s.ifaceDev.Addr6LinkLocal),
|
||||
DstMac: destMac,
|
||||
DstIP: destIP,
|
||||
Vlan: vlanId,
|
||||
},
|
||||
TargetAddr: gwIP,
|
||||
}
|
||||
|
||||
bytes, err := icmp6.EncodePacket(ns)
|
||||
if err != nil {
|
||||
log.Errorf("Encode NeighborSolicitation error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.conn.SendRaw(bytes, destMac)
|
||||
if err != nil {
|
||||
log.Errorf("Send RouterAdvertisement error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) sendRouterAdvertisement(solicitation *icmp6.SRouterSolicitation) (bool, error) {
|
||||
var conf = s.getConfig(solicitation.SrcMac)
|
||||
if conf != nil && conf.ClientIP6 != nil && conf.Gateway6 != nil {
|
||||
gwMacObj := s.gwMacCache.AtomicGet(conf.Gateway6.String())
|
||||
if gwMacObj == nil {
|
||||
log.Debugf("No mac for gw IP %s, request it", conf.Gateway6.String())
|
||||
s.requestGatewayMac(conf.Gateway6, conf.VlanId)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
gwMac := gwMacObj.(net.HardwareAddr)
|
||||
|
||||
pref := icmp6.PreferenceMedium
|
||||
if conf.IsDefaultGW {
|
||||
pref = icmp6.PreferenceHigh
|
||||
}
|
||||
|
||||
_, ipnet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", conf.ClientIP6, conf.PrefixLen6))
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "ParseCIDR %s/%d", conf.ClientIP6, conf.PrefixLen6)
|
||||
}
|
||||
|
||||
srcIpAddr, _ := netutils.Mac2LinkLocal(gwMac.String())
|
||||
gwLinkLocalIP := srcIpAddr.ToIP()
|
||||
ra := &icmp6.SRouterAdvertisement{
|
||||
SBaseICMP6Message: icmp6.SBaseICMP6Message{
|
||||
SrcMac: gwMac,
|
||||
// https://www.rfc-editor.org/rfc/rfc4861.html#page-18
|
||||
// !!! MUST be the link-local address assigned to the interface from which this message is sent.
|
||||
SrcIP: gwLinkLocalIP,
|
||||
DstMac: solicitation.SrcMac,
|
||||
DstIP: net.ParseIP("ff02::1"),
|
||||
},
|
||||
CurHopLimit: 64,
|
||||
IsManaged: true,
|
||||
IsOther: true,
|
||||
IsHomeAgent: false,
|
||||
Preference: pref,
|
||||
RouterLifetime: 9000,
|
||||
ReachableTime: 0,
|
||||
RetransTimer: 0,
|
||||
MTU: uint32(conf.MTU),
|
||||
PrefixInfo: []icmp6.SPrefixInfoOption{
|
||||
{
|
||||
IsOnlink: true,
|
||||
IsAutoconf: true,
|
||||
Prefix: ipnet.IP,
|
||||
PrefixLen: conf.PrefixLen6,
|
||||
ValidLifetime: 4500,
|
||||
PreferredLifetime: 2250,
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range conf.Routes6 {
|
||||
route := conf.Routes6[i]
|
||||
if route.Gateway.String() == "::" {
|
||||
// on-link routes
|
||||
ra.PrefixInfo = append(ra.PrefixInfo, icmp6.SPrefixInfoOption{
|
||||
IsOnlink: true,
|
||||
IsAutoconf: false,
|
||||
Prefix: route.Prefix,
|
||||
PrefixLen: route.PrefixLen,
|
||||
ValidLifetime: 4500,
|
||||
PreferredLifetime: 2250,
|
||||
})
|
||||
} else if route.Gateway.String() != conf.Gateway6.String() {
|
||||
// routes forwarded by router
|
||||
ra.RouteInfo = append(ra.RouteInfo, icmp6.SRouteInfoOption{
|
||||
RouteLifetime: 9000,
|
||||
Prefix: route.Prefix,
|
||||
PrefixLen: route.PrefixLen,
|
||||
Preference: icmp6.PreferenceMedium,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
bytes, err := icmp6.EncodePacket(ra)
|
||||
if err != nil {
|
||||
log.Errorf("Encode RouterAdvertisement error: %v", err)
|
||||
return false, errors.Wrapf(err, "EncodePacket")
|
||||
}
|
||||
|
||||
err = s.conn.SendRaw(bytes, solicitation.SrcMac)
|
||||
if err != nil {
|
||||
log.Errorf("Send RouterAdvertisement error: %v", err)
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) stopRAServer() {
|
||||
close(s.raExitCh)
|
||||
close(s.raReqCh)
|
||||
}
|
||||
|
||||
func (s *SGuestDHCP6Server) startRAServer() {
|
||||
// a tiny RA server
|
||||
stop := false
|
||||
for !stop {
|
||||
select {
|
||||
case <-s.raExitCh:
|
||||
stop = true
|
||||
case raReq := <-s.raReqCh:
|
||||
// handle RA request
|
||||
s.raReqQueue = append(s.raReqQueue, raReq)
|
||||
case <-time.After(time.Second * time.Duration(options.HostOptions.Dhcp6RouterAdvertisementIntervalSecs)):
|
||||
// send RA
|
||||
// log.Infof("timeout, to announce RA %d requests", len(s.raReqQueue))
|
||||
if len(s.raReqQueue) > 0 {
|
||||
raReqQueue := s.raReqQueue
|
||||
s.raReqQueue = make([]*sRARequest, 0)
|
||||
|
||||
for i := range raReqQueue {
|
||||
raReq := raReqQueue[i]
|
||||
raReq.attempts++
|
||||
succ, err := s.sendRouterAdvertisement(raReq.solicitation)
|
||||
if err != nil {
|
||||
log.Errorf("sendRouterAdvertisement error: %v", err)
|
||||
continue
|
||||
}
|
||||
if succ {
|
||||
raReq.succAttempts++
|
||||
}
|
||||
if raReq.succAttempts < options.HostOptions.Dhcp6RouterAdvertisementAttempts && raReq.attempts < 2*options.HostOptions.Dhcp6RouterAdvertisementAttempts {
|
||||
s.raReqCh <- raReq
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,9 @@ type SHostBaseOptions struct {
|
||||
DhcpLeaseTime int `default:"100663296" help:"DHCP lease time in seconds"`
|
||||
DhcpRenewalTime int `default:"67108864" help:"DHCP renewal time in seconds"`
|
||||
|
||||
Dhcp6RouterAdvertisementIntervalSecs int `default:"3" help:"DHCPv6 router advertisement interval in seconds, default 3 seconds"`
|
||||
Dhcp6RouterAdvertisementAttempts int `default:"3" help:"DHCPv6 router advertisement attempts, default 3 attempts"`
|
||||
|
||||
Ext4LargefileSizeGb int `default:"4096" help:"Use largefile options when the ext4 fs greater than this size"`
|
||||
Ext4HugefileSizeGb int `default:"512" help:"Use huge options when the ext4 fs greater than this size"`
|
||||
|
||||
|
||||
+2
-10
@@ -196,16 +196,8 @@ func (c *Conn) RecvDHCP6() (Packet, *net.UDPAddr, net.HardwareAddr, error) {
|
||||
// which configuration is desired if acting as a client.
|
||||
func (c *Conn) SendDHCP(pkt Packet, addr *net.UDPAddr, mac net.HardwareAddr) error {
|
||||
b := pkt.Marshal()
|
||||
|
||||
switch len(addr.IP) {
|
||||
case net.IPv4len:
|
||||
if addr.IP.Equal(net.IPv4zero) || pkt.txType() == txBroadcast {
|
||||
addr = &net.UDPAddr{IP: net.IPv4bcast, Port: addr.Port}
|
||||
}
|
||||
case net.IPv6len:
|
||||
default:
|
||||
return errors.Wrapf(errors.ErrNotSupported, "unsupported length of IP address length %d", len(addr.IP))
|
||||
|
||||
if addr.IP.Equal(net.IPv4zero) || pkt.txType() == txBroadcast {
|
||||
addr = &net.UDPAddr{IP: net.IPv4bcast, Port: addr.Port}
|
||||
}
|
||||
return c.conn.Send(b, addr, mac)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,6 @@ func (s *rawSocketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "Read from errror")
|
||||
}
|
||||
log.Debugf("rawSocketConn Recv %d bytes", n)
|
||||
|
||||
b = b[:n]
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "Read from errror")
|
||||
}
|
||||
log.Debugf("rawSocketConn Recv6 %d bytes from %s", n, addr.String())
|
||||
|
||||
b = b[:n]
|
||||
|
||||
@@ -106,19 +105,7 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
|
||||
|
||||
icmpLayer := p.Layer(layers.LayerTypeICMPv6)
|
||||
if icmpLayer != nil {
|
||||
log.Infof("rawSocketConn Recv6 icmpLayer %s", icmpLayer.LayerType())
|
||||
/*raLayer := p.Layer(layers.LayerTypeICMPv6RouterSolicitation)
|
||||
if raLayer != nil {
|
||||
// icmp6, ra solitation
|
||||
raPkt := raLayer.(*layers.ICMPv6RouterSolicitation)
|
||||
sbf := gopacket.NewSerializeBuffer()
|
||||
if err := raPkt.SerializeTo(sbf, gopacket.SerializeOptions{}); err != nil {
|
||||
return nil, nil, nil, 0, errors.Wrap(err, "Serialize ICMPv6 ra solitation packet error")
|
||||
}
|
||||
return sbf.Bytes(), &net.UDPAddr{IP: srcIp, Port: icmpRAFakePort}, srcMac, 0, nil
|
||||
} else {
|
||||
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "expect an ICMPv6 RA solitation packet")
|
||||
}*/
|
||||
// receive icmp packet
|
||||
return b, &net.UDPAddr{IP: srcIp, Port: icmpRAFakePort}, srcMac, nil
|
||||
}
|
||||
|
||||
|
||||
+26
-10
@@ -36,6 +36,7 @@ const (
|
||||
// http://www.networksorcery.com/enp/rfc/rfc2132.txt
|
||||
type ResponseConfig struct {
|
||||
InterfaceMac net.HardwareAddr
|
||||
VlanId uint16
|
||||
|
||||
OsName string
|
||||
ServerIP net.IP // OptServerIdentifier 54
|
||||
@@ -59,6 +60,8 @@ type ResponseConfig struct {
|
||||
NTPServers6 []net.IP
|
||||
Routes6 []netutils2.SRouteInfo
|
||||
|
||||
IsDefaultGW bool
|
||||
|
||||
// Relay Info https://datatracker.ietf.org/doc/html/rfc3046
|
||||
RelayInfo []byte
|
||||
|
||||
@@ -78,6 +81,12 @@ func GetOptUint16(val uint16) []byte {
|
||||
return opts
|
||||
}
|
||||
|
||||
func GetOptUint32(val uint32) []byte {
|
||||
opts := []byte{0, 0, 0, 0}
|
||||
binary.BigEndian.PutUint32(opts, val)
|
||||
return opts
|
||||
}
|
||||
|
||||
func GetOptIP(ip net.IP) []byte {
|
||||
return []byte(ip.To4())
|
||||
}
|
||||
@@ -149,33 +158,40 @@ func makeDHCPReplyPacket(req Packet, conf *ResponseConfig, msgType MessageType)
|
||||
opts := make([]Option, 0)
|
||||
|
||||
if conf.SubnetMask != nil {
|
||||
opts = append(opts, Option{OptionSubnetMask, GetOptIP(conf.SubnetMask)})
|
||||
opts = append(opts, Option{Code: OptionSubnetMask, Value: GetOptIP(conf.SubnetMask)})
|
||||
}
|
||||
if conf.Gateway != nil {
|
||||
opts = append(opts, Option{OptionRouter, GetOptIP(conf.Gateway)})
|
||||
opts = append(opts, Option{Code: OptionRouter, Value: GetOptIP(conf.Gateway)})
|
||||
}
|
||||
if conf.Domain != "" {
|
||||
opts = append(opts, Option{OptionDomainName, []byte(conf.Domain)})
|
||||
opts = append(opts, Option{Code: OptionDomainName, Value: []byte(conf.Domain)})
|
||||
}
|
||||
if conf.BroadcastAddr != nil {
|
||||
opts = append(opts, Option{OptionBroadcastAddress, GetOptIP(conf.BroadcastAddr)})
|
||||
opts = append(opts, Option{Code: OptionBroadcastAddress, Value: GetOptIP(conf.BroadcastAddr)})
|
||||
}
|
||||
if conf.Hostname != "" {
|
||||
opts = append(opts, Option{OptionHostName, []byte(conf.GetHostname())})
|
||||
opts = append(opts, Option{Code: OptionHostName, Value: []byte(conf.GetHostname())})
|
||||
}
|
||||
if len(conf.DNSServers) > 0 {
|
||||
opts = append(opts, Option{OptionDomainNameServer, GetOptIPs(conf.DNSServers)})
|
||||
opts = append(opts, Option{Code: OptionDomainNameServer, Value: GetOptIPs(conf.DNSServers)})
|
||||
}
|
||||
if len(conf.NTPServers) > 0 {
|
||||
opts = append(opts, Option{OptionNetworkTimeProtocolServers, GetOptIPs(conf.NTPServers)})
|
||||
opts = append(opts, Option{Code: OptionNetworkTimeProtocolServers, Value: GetOptIPs(conf.NTPServers)})
|
||||
}
|
||||
if conf.MTU > 0 {
|
||||
opts = append(opts, Option{OptionInterfaceMTU, GetOptUint16(conf.MTU)})
|
||||
opts = append(opts, Option{Code: OptionInterfaceMTU, Value: GetOptUint16(conf.MTU)})
|
||||
}
|
||||
if conf.RelayInfo != nil {
|
||||
opts = append(opts, Option{OptionRelayAgentInformation, conf.RelayInfo})
|
||||
opts = append(opts, Option{Code: OptionRelayAgentInformation, Value: conf.RelayInfo})
|
||||
}
|
||||
resp := ReplyPacket(req, msgType, conf.ServerIP, conf.ClientIP, conf.LeaseTime, opts)
|
||||
var clientIP net.IP
|
||||
if conf.ClientIP != nil {
|
||||
clientIP = conf.ClientIP
|
||||
} else {
|
||||
clientIP = net.ParseIP("0.0.0.0")
|
||||
opts = append(opts, Option{Code: OptionIPv6Only, Value: GetOptUint32(60)})
|
||||
}
|
||||
resp := ReplyPacket(req, msgType, conf.ServerIP, clientIP, conf.LeaseTime, opts)
|
||||
if conf.BootServer != "" {
|
||||
//resp.Options[OptOverload] = []byte{3}
|
||||
resp.SetSIAddr(net.ParseIP(conf.BootServer))
|
||||
|
||||
@@ -30,5 +30,5 @@ type SendPacket struct {
|
||||
type DHCP6Handler interface {
|
||||
DHCPHandler
|
||||
|
||||
OnRecvICMP6(pkt Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) error
|
||||
OnRecvICMP6(pkt Packet) error
|
||||
}
|
||||
|
||||
@@ -469,6 +469,9 @@ const (
|
||||
OptionTZPOSIXString OptionCode = 100
|
||||
OptionTZDatabaseString OptionCode = 101
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8925
|
||||
OptionIPv6Only OptionCode = 108
|
||||
|
||||
OptionDomainSearch OptionCode = 119
|
||||
|
||||
OptionClasslessRouteFormat OptionCode = 121
|
||||
|
||||
+156
-21
@@ -17,6 +17,7 @@ package dhcp
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
@@ -116,14 +117,111 @@ const (
|
||||
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) TID() uint32 {
|
||||
return binary.BigEndian.Uint32([]byte{0, p[1], p[2], p[3]})
|
||||
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
|
||||
@@ -176,6 +274,12 @@ func NewPacket6(opCode MessageType, tid uint32) Packet {
|
||||
return p
|
||||
}
|
||||
|
||||
func NewRelayPacket6() Packet {
|
||||
p := make(Packet, 34)
|
||||
p.SetType6(DHCPV6_RELAY_FORW)
|
||||
return p
|
||||
}
|
||||
|
||||
type Option6 struct {
|
||||
Code OptionCode6
|
||||
Value []byte
|
||||
@@ -227,15 +331,6 @@ func (p Packet) GetOption6s() []Option6 {
|
||||
return options
|
||||
}
|
||||
|
||||
// Creates a request packet that a Client would send to a server.
|
||||
/*func RequestPacket6(mt MessageType, tid uint32, options []Option6) Packet {
|
||||
p := NewPacket6(mt, tid)
|
||||
for _, o := range options {
|
||||
p.AddOption6(o)
|
||||
}
|
||||
return p
|
||||
}*/
|
||||
|
||||
func MakeDHCP6Reply(pkt Packet, conf *ResponseConfig) (Packet, error) {
|
||||
var msgType MessageType
|
||||
pktType := pkt.Type6()
|
||||
@@ -256,7 +351,7 @@ func MakeDHCP6Reply(pkt Packet, conf *ResponseConfig) (Packet, error) {
|
||||
return nil, errors.Wrapf(errors.ErrNotSupported, "unsupported message type %d", pktType)
|
||||
}
|
||||
|
||||
return makeDHCPReplyPacket6(pkt, conf, msgType), nil
|
||||
return makeDHCPReplyPacket6(pkt, conf, msgType)
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -282,14 +377,13 @@ func makeIAAddr(ip net.IP, preferLT, validLT uint32, opts []Option6) []byte {
|
||||
}
|
||||
|
||||
func responseIANA(buf []byte, opts []Option6) []byte {
|
||||
log.Debugf("responseIANA buf %d", len(buf))
|
||||
if len(buf) > 12 {
|
||||
buf = buf[:12]
|
||||
}
|
||||
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)
|
||||
// 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)
|
||||
buf = append(buf, optionsToBytes(opts)...)
|
||||
return buf
|
||||
}
|
||||
@@ -306,10 +400,21 @@ func makeIPv6s(ips []net.IP) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
func makeDHCPReplyPacket6(pkt Packet, conf *ResponseConfig, msgType MessageType) Packet {
|
||||
log.Debugf("makeDHCPReplyPacket6 msgType %d tid %x", msgType, pkt.TID())
|
||||
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
|
||||
}
|
||||
|
||||
resp := NewPacket6(msgType, pkt.TID())
|
||||
func makeDHCPReplyPacket6(pkt Packet, conf *ResponseConfig, msgType MessageType) (Packet, error) {
|
||||
tid, err := pkt.TID6()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "TID6")
|
||||
}
|
||||
|
||||
resp := NewPacket6(msgType, tid)
|
||||
originOpts := pkt.GetOption6s()
|
||||
getOption := func(code OptionCode6) Option6 {
|
||||
for _, o := range originOpts {
|
||||
@@ -320,6 +425,16 @@ func makeDHCPReplyPacket6(pkt Packet, conf *ResponseConfig, msgType MessageType)
|
||||
return Option6{}
|
||||
}
|
||||
|
||||
reqInfo := getOption(DHCPV6_OPTION_ORO)
|
||||
if 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)
|
||||
|
||||
// copy clientID
|
||||
@@ -362,5 +477,25 @@ func makeDHCPReplyPacket6(pkt Packet, conf *ResponseConfig, msgType MessageType)
|
||||
|
||||
resp = append(resp, optionsToBytes(options)...)
|
||||
|
||||
return resp
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
|
||||
|
||||
if addr.Port == icmpRAFakePort {
|
||||
// receive a RA solication
|
||||
handler.OnRecvICMP6(pkt, mac, addr)
|
||||
handler.OnRecvICMP6(pkt)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+90
-22
@@ -49,13 +49,20 @@ type SBaseICMP6Message struct {
|
||||
SrcIP net.IP
|
||||
DstMac net.HardwareAddr
|
||||
DstIP net.IP
|
||||
Vlan uint16
|
||||
}
|
||||
|
||||
func (msg SBaseICMP6Message) String() string {
|
||||
return fmt.Sprintf("%s[%s]->%s[%s]", msg.SrcIP.String(), msg.SrcMac.String(), msg.DstIP.String(), msg.DstMac.String())
|
||||
var vlanStr string
|
||||
if msg.Vlan > 1 {
|
||||
vlanStr += fmt.Sprintf("(vlan %d)", msg.Vlan)
|
||||
}
|
||||
return fmt.Sprintf("%s[%s]->%s[%s]%s", msg.SrcIP.String(), msg.SrcMac.String(), msg.DstIP.String(), msg.DstMac.String(), vlanStr)
|
||||
}
|
||||
|
||||
type IICMP6Message interface {
|
||||
fmt.Stringer
|
||||
|
||||
Payload() gopacket.SerializableLayer
|
||||
ICMP6TypeCode() layers.ICMPv6TypeCode
|
||||
|
||||
@@ -63,6 +70,8 @@ type IICMP6Message interface {
|
||||
SourceIP() net.IP
|
||||
DestinationMac() net.HardwareAddr
|
||||
DestinationIP() net.IP
|
||||
|
||||
VlanId() uint16
|
||||
}
|
||||
|
||||
func (msg SBaseICMP6Message) SourceIP() net.IP {
|
||||
@@ -81,6 +90,10 @@ func (msg SBaseICMP6Message) DestinationMac() net.HardwareAddr {
|
||||
return msg.DstMac
|
||||
}
|
||||
|
||||
func (msg SBaseICMP6Message) VlanId() uint16 {
|
||||
return msg.Vlan
|
||||
}
|
||||
|
||||
type SRouterSolicitation struct {
|
||||
SBaseICMP6Message
|
||||
}
|
||||
@@ -179,6 +192,10 @@ func (msg *SNeighborSolicitation) Unmarshal(data *layers.ICMPv6NeighborSolicitat
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg SNeighborSolicitation) String() string {
|
||||
return fmt.Sprintf("NeighborSolicitation %s target %s", msg.SBaseICMP6Message.String(), msg.TargetAddr.String())
|
||||
}
|
||||
|
||||
func (msg SNeighborAdvertisement) ICMP6TypeCode() layers.ICMPv6TypeCode {
|
||||
return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborAdvertisement, 0)
|
||||
}
|
||||
@@ -211,6 +228,10 @@ func (msg *SNeighborAdvertisement) Unmarshal(data *layers.ICMPv6NeighborAdvertis
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg SNeighborAdvertisement) String() string {
|
||||
return fmt.Sprintf("NeighborAdvertisement %s target %s", msg.SBaseICMP6Message.String(), msg.TargetAddr.String())
|
||||
}
|
||||
|
||||
func (msg SRouterSolicitation) ICMP6TypeCode() layers.ICMPv6TypeCode {
|
||||
return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRouterSolicitation, 0)
|
||||
}
|
||||
@@ -232,6 +253,10 @@ func (msg *SRouterSolicitation) Unmarshal(data *layers.ICMPv6RouterSolicitation)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg SRouterSolicitation) String() string {
|
||||
return fmt.Sprintf("RouterSolicitation %s", msg.SBaseICMP6Message.String())
|
||||
}
|
||||
|
||||
func (msg SRouterAdvertisement) ICMP6TypeCode() layers.ICMPv6TypeCode {
|
||||
return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRouterAdvertisement, 0)
|
||||
}
|
||||
@@ -265,16 +290,8 @@ func (msg SRouterAdvertisement) Payload() gopacket.SerializableLayer {
|
||||
pkt.ReachableTime = msg.ReachableTime
|
||||
pkt.RetransTimer = msg.RetransTimer
|
||||
|
||||
pkt.Options = layers.ICMPv6Options{
|
||||
layers.ICMPv6Option{
|
||||
Type: layers.ICMPv6OptSourceAddress,
|
||||
Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
|
||||
},
|
||||
layers.ICMPv6Option{
|
||||
Type: layers.ICMPv6OptMTU,
|
||||
Data: NewIcmpV6OptMtu(msg.MTU).Bytes(),
|
||||
},
|
||||
}
|
||||
pkt.Options = layers.ICMPv6Options{}
|
||||
|
||||
for i := range msg.PrefixInfo {
|
||||
pref := msg.PrefixInfo[i]
|
||||
pkt.Options = append(pkt.Options, layers.ICMPv6Option{
|
||||
@@ -289,6 +306,16 @@ func (msg SRouterAdvertisement) Payload() gopacket.SerializableLayer {
|
||||
Data: NewIcmpv6OptRouteInfo(rt).Bytes(),
|
||||
})
|
||||
}
|
||||
if msg.MTU > 0 {
|
||||
pkt.Options = append(pkt.Options, layers.ICMPv6Option{
|
||||
Type: layers.ICMPv6OptMTU,
|
||||
Data: NewIcmpV6OptMtu(msg.MTU).Bytes(),
|
||||
})
|
||||
}
|
||||
pkt.Options = append(pkt.Options, layers.ICMPv6Option{
|
||||
Type: layers.ICMPv6OptSourceAddress,
|
||||
Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
|
||||
})
|
||||
|
||||
return &pkt
|
||||
}
|
||||
@@ -327,42 +354,58 @@ func (msg *SRouterAdvertisement) Unmarshal(data *layers.ICMPv6RouterAdvertisemen
|
||||
|
||||
func (msg SRouterAdvertisement) String() string {
|
||||
return fmt.Sprintf(`RouterAdvertisement %s
|
||||
CurHopLimit: %d, IsManaged: %t, IsOther: %t, IsHomeAgent: %t, Preference: %s, RouterLifetime: %d, ReachableTime: %d, RetransTimer: %d
|
||||
MTU: %d
|
||||
PrefixInfo: %v
|
||||
RouteInfo: %v)`,
|
||||
CurHopLimit: %d, IsManaged: %t, IsOther: %t, IsHomeAgent: %t, Preference: %s, RouterLifetime: %d, ReachableTime: %d, RetransTimer: %d
|
||||
MTU: %d
|
||||
PrefixInfo: %v
|
||||
RouteInfo: %v)`,
|
||||
msg.SBaseICMP6Message.String(),
|
||||
msg.CurHopLimit, msg.IsManaged, msg.IsOther, msg.IsHomeAgent, msg.Preference.String(), msg.RouterLifetime, msg.ReachableTime, msg.RetransTimer,
|
||||
msg.MTU, msg.PrefixInfo, msg.RouteInfo)
|
||||
}
|
||||
|
||||
func EncodePacket(msg IICMP6Message) ([]byte, error) {
|
||||
var pktLayers []gopacket.SerializableLayer
|
||||
|
||||
var eth = &layers.Ethernet{
|
||||
EthernetType: layers.EthernetTypeIPv6,
|
||||
SrcMAC: msg.SourceMac(),
|
||||
DstMAC: msg.DestinationMac(),
|
||||
}
|
||||
pktLayers = append(pktLayers, eth)
|
||||
|
||||
if msg.VlanId() > 1 {
|
||||
eth.EthernetType = layers.EthernetTypeDot1Q
|
||||
dot1Q := &layers.Dot1Q{
|
||||
VLANIdentifier: msg.VlanId(),
|
||||
Priority: 6,
|
||||
Type: layers.EthernetTypeIPv6,
|
||||
}
|
||||
pktLayers = append(pktLayers, dot1Q)
|
||||
}
|
||||
|
||||
var ip = &layers.IPv6{
|
||||
Version: 6,
|
||||
HopLimit: 64,
|
||||
SrcIP: msg.SourceIP(),
|
||||
DstIP: msg.DestinationIP(),
|
||||
NextHeader: layers.IPProtocolICMPv6,
|
||||
Version: 6,
|
||||
HopLimit: 0xff,
|
||||
TrafficClass: 0xc0,
|
||||
SrcIP: msg.SourceIP(),
|
||||
DstIP: msg.DestinationIP(),
|
||||
NextHeader: layers.IPProtocolICMPv6,
|
||||
}
|
||||
pktLayers = append(pktLayers, ip)
|
||||
|
||||
var icmp6 = &layers.ICMPv6{
|
||||
TypeCode: msg.ICMP6TypeCode(),
|
||||
}
|
||||
icmp6.SetNetworkLayerForChecksum(ip)
|
||||
pktLayers = append(pktLayers, icmp6)
|
||||
|
||||
var payload = msg.Payload()
|
||||
pktLayers = append(pktLayers, msg.Payload())
|
||||
|
||||
var (
|
||||
buf = gopacket.NewSerializeBuffer()
|
||||
opts = gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true}
|
||||
)
|
||||
if err := gopacket.SerializeLayers(buf, opts, eth, ip, icmp6, payload); err != nil {
|
||||
if err := gopacket.SerializeLayers(buf, opts, pktLayers...); err != nil {
|
||||
return nil, errors.Wrap(err, "SerializeLayers error")
|
||||
}
|
||||
|
||||
@@ -388,6 +431,15 @@ func DecodePacket(data []byte) (IICMP6Message, error) {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// optional vlan layer
|
||||
dot1qLayer := packet.Layer(layers.LayerTypeDot1Q)
|
||||
if dot1qLayer != nil {
|
||||
dot1q := dot1qLayer.(*layers.Dot1Q)
|
||||
baseMsg.Vlan = dot1q.VLANIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ipLayer := packet.Layer(layers.LayerTypeIPv6)
|
||||
if ipLayer != nil {
|
||||
@@ -415,6 +467,10 @@ func DecodePacket(data []byte) (IICMP6Message, error) {
|
||||
msg := &SRouterSolicitation{
|
||||
SBaseICMP6Message: baseMsg,
|
||||
}
|
||||
err := msg.Unmarshal(payload.(*layers.ICMPv6RouterSolicitation))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal Router Solicitation packet")
|
||||
}
|
||||
return msg, nil
|
||||
} else {
|
||||
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Router Solicitation packet")
|
||||
@@ -425,6 +481,10 @@ func DecodePacket(data []byte) (IICMP6Message, error) {
|
||||
msg := &SRouterAdvertisement{
|
||||
SBaseICMP6Message: baseMsg,
|
||||
}
|
||||
err := msg.Unmarshal(payload.(*layers.ICMPv6RouterAdvertisement))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal Router Advertisement packet")
|
||||
}
|
||||
return msg, nil
|
||||
} else {
|
||||
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Router Advertisement packet")
|
||||
@@ -435,6 +495,10 @@ func DecodePacket(data []byte) (IICMP6Message, error) {
|
||||
msg := &SNeighborSolicitation{
|
||||
SBaseICMP6Message: baseMsg,
|
||||
}
|
||||
err := msg.Unmarshal(payload.(*layers.ICMPv6NeighborSolicitation))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal Neighbor Solicitation packet")
|
||||
}
|
||||
return msg, nil
|
||||
} else {
|
||||
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Neighbor Solicitation packet")
|
||||
@@ -445,6 +509,10 @@ func DecodePacket(data []byte) (IICMP6Message, error) {
|
||||
msg := &SNeighborAdvertisement{
|
||||
SBaseICMP6Message: baseMsg,
|
||||
}
|
||||
err := msg.Unmarshal(payload.(*layers.ICMPv6NeighborAdvertisement))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal Neighbor Advertisement packet")
|
||||
}
|
||||
return msg, nil
|
||||
} else {
|
||||
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Neighbor Advertisement packet")
|
||||
|
||||
@@ -65,6 +65,7 @@ func NewEthernetConfigMatchMac(macAddr string) *EthernetConfigMatch {
|
||||
type EthernetConfig struct {
|
||||
DHCP4 bool `json:"dhcp4,omitfalse"`
|
||||
DHCP6 bool `json:"dhcp6,omitfalse"`
|
||||
AcceptRa bool `json:"accept-ra,omitfalse"`
|
||||
Addresses []string `json:"addresses"`
|
||||
Match *EthernetConfigMatch `json:"match"`
|
||||
MacAddress string `json:"macaddress"`
|
||||
@@ -206,6 +207,7 @@ func NewDHCP4EthernetConfig() *EthernetConfig {
|
||||
|
||||
func (c *EthernetConfig) EnableDHCP6() {
|
||||
c.DHCP6 = true
|
||||
c.AcceptRa = true
|
||||
}
|
||||
|
||||
func NewStaticEthernetConfig(
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestNewEthernetConfig(t *testing.T) {
|
||||
assert.YAMLEq("dhcp4: true", c.YAMLString())
|
||||
|
||||
c.EnableDHCP6()
|
||||
assert.YAMLEq("dhcp4: true\ndhcp6: true", c.YAMLString())
|
||||
assert.YAMLEq("dhcp4: true\ndhcp6: true\naccept-ra: true", c.YAMLString())
|
||||
}
|
||||
|
||||
func TestNewBondMode4(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user