fix: dhcpv6 support phase 3 (#22881)

Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
Jian Qiu
2025-07-12 12:10:31 +08:00
committed by GitHub
parent 382c2bc6cd
commit c6f84dfa94
19 changed files with 745 additions and 162 deletions
+48 -17
View File
@@ -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)
+6
View File
@@ -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 != "" {
+1 -1
View File
@@ -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))
+93 -33
View File
@@ -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
}
+34 -24
View File
@@ -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 {
+45 -6
View File
@@ -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
}
}
}
}
}
}
+3
View File
@@ -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
View File
@@ -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)
}
-1
View File
@@ -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]
+1 -14
View File
@@ -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
View File
@@ -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))
+1 -1
View File
@@ -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
}
+3
View File
@@ -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
View File
@@ -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")
}
+1 -1
View File
@@ -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
View File
@@ -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")
+2
View File
@@ -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(
+1 -1
View File
@@ -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) {