fix: host no default routes due to empty ipv6 addr (#22870)

Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
Jian Qiu
2025-07-10 22:33:59 +08:00
committed by GitHub
parent abfedab61c
commit 96cf60afeb
28 changed files with 1062 additions and 503 deletions

View File

@@ -28,6 +28,7 @@ import (
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/netutils2"
)
func GetNicDHCPConfig(
@@ -46,10 +47,10 @@ func GetNicDHCPConfig(
return nil, errors.Wrap(errors.ErrEmpty, "Nic no ip or ip6 address")
}
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.SRouteInfo, 0)
routes4 := make([]netutils2.SRouteInfo, 0)
routes6 := make([]netutils2.SRouteInfo, 0)
for _, route := range n.Routes {
routeInfo, err := dhcp.ParseRouteInfo([]string{route[0], route[1]})
routeInfo, err := netutils2.ParseRouteInfo([]string{route[0], route[1]})
if err != nil {
return nil, errors.Wrapf(err, "Parse route %s error: %q", route, err)
}
@@ -62,17 +63,21 @@ func GetNicDHCPConfig(
if n.IsDefault == nil || *n.IsDefault {
if n.Gateway != "" && !strings.HasPrefix(strings.ToLower(osName), "win") {
routes4 = append(routes4, dhcp.SRouteInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
Gateway: net.ParseIP(n.Gateway),
routes4 = append(routes4, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
},
Gateway: net.ParseIP(n.Gateway),
})
}
if n.Gateway6 != "" {
routes6 = append(routes6, dhcp.SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP(n.Gateway6),
routes6 = append(routes6, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
},
Gateway: net.ParseIP(n.Gateway6),
})
}
}

View File

@@ -38,7 +38,6 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/types"
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
"yunion.io/x/onecloud/pkg/util/coreosutils"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/fstabutils"
"yunion.io/x/onecloud/pkg/util/netutils2"
@@ -984,8 +983,8 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
if nicDesc.Mtu > 0 {
cmds.WriteString(fmt.Sprintf(" mtu %d\n", nicDesc.Mtu))
}
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.SRouteInfo, 0)
routes4 := make([]netutils2.SRouteInfo, 0)
routes6 := make([]netutils2.SRouteInfo, 0)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
for _, r := range routes4 {
cmds.WriteString(fmt.Sprintf(" up ip route add %s/%d via %s || true\n", r.Prefix, r.PrefixLen, r.Gateway))
@@ -1483,8 +1482,8 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
cmds.WriteString(nicDesc.Gateway)
cmds.WriteString("\n")
}
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.SRouteInfo, 0)
routes4 := make([]netutils2.SRouteInfo, 0)
routes6 := make([]netutils2.SRouteInfo, 0)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
var rtbl strings.Builder
for _, r := range routes4 {
@@ -1576,8 +1575,8 @@ func (r *sRedhatLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, s
cmds.WriteString(nicDesc.Gateway)
cmds.WriteString("\n")
}
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.SRouteInfo, 0)
routes4 := make([]netutils2.SRouteInfo, 0)
routes6 := make([]netutils2.SRouteInfo, 0)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
var rtbl strings.Builder
for _, r := range routes4 {

View File

@@ -20,7 +20,6 @@ import (
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/netplan"
"yunion.io/x/onecloud/pkg/util/netutils2"
)
@@ -126,8 +125,8 @@ func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool, mainIp, mainIp
}
}
routeArrs4 := make([]dhcp.SRouteInfo, 0)
routeArrs6 := make([]dhcp.SRouteInfo, 0)
routeArrs4 := make([]netutils2.SRouteInfo, 0)
routeArrs6 := make([]netutils2.SRouteInfo, 0)
routeArrs4, routeArrs6 = netutils2.AddNicRoutes(routeArrs4, routeArrs6, nic, mainIp, mainIp6, nicCnt)
var routes []*netplan.Route

View File

@@ -27,7 +27,6 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/types"
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/netutils2"
)
@@ -106,22 +105,26 @@ func (r *sSuseLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, scr
cmds.WriteString(fmt.Sprintf("VLAN_ID=%d\n", nicDesc.Vlan))
cmds.WriteString(fmt.Sprintf("ETHERDEVICE=%s\n", nicDesc.Name))
var routes4 = make([]dhcp.SRouteInfo, 0)
var routes6 = make([]dhcp.SRouteInfo, 0)
var routes4 = make([]netutils2.SRouteInfo, 0)
var routes6 = make([]netutils2.SRouteInfo, 0)
var dnsSrv []string
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
routes4 = append(routes4, dhcp.SRouteInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway),
routes4 = append(routes4, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
},
Gateway: net.ParseIP(nicDesc.Gateway),
})
}
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip6 == mainIp6 {
routes6 = append(routes6, dhcp.SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway6),
routes6 = append(routes6, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
},
Gateway: net.ParseIP(nicDesc.Gateway6),
})
}
var rtbl strings.Builder
@@ -232,21 +235,25 @@ func (r *sSuseLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics []
cmds.WriteString(fmt.Sprintf("IPADDR_V6=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
}
var routes4 = make([]dhcp.SRouteInfo, 0)
var routes6 = make([]dhcp.SRouteInfo, 0)
var routes4 = make([]netutils2.SRouteInfo, 0)
var routes6 = make([]netutils2.SRouteInfo, 0)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
routes4 = append(routes4, dhcp.SRouteInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway),
routes4 = append(routes4, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
},
Gateway: net.ParseIP(nicDesc.Gateway),
})
}
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip6 == mainIp6 {
routes6 = append(routes6, dhcp.SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway6),
routes6 = append(routes6, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
},
Gateway: net.ParseIP(nicDesc.Gateway6),
})
}
var rtbl strings.Builder

View File

@@ -31,7 +31,6 @@ import (
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/procutils"
@@ -332,8 +331,8 @@ func (w *SWindowsRootFs) DeployNetworkingScripts(rootfs IDiskPartition, nics []*
lines = append(lines, cfg)
}
}
routes4 := []dhcp.SRouteInfo{}
routes6 := []dhcp.SRouteInfo{}
routes4 := []netutils2.SRouteInfo{}
routes6 := []netutils2.SRouteInfo{}
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, snic, mainIp, mainIp6, len(nics))
for _, r := range routes4 {
lines = append(lines, fmt.Sprintf(` netsh interface ip add route %s/%d %s "%%%%b"`, r.Prefix, r.PrefixLen, r.Gateway.String()))

View File

@@ -15,6 +15,8 @@
package hostman
import (
"time"
execlient "yunion.io/x/executor/client"
"yunion.io/x/log"
@@ -85,7 +87,9 @@ func (host *SHostService) RunService() {
hostInstance := hostinfo.Instance()
if err := hostInstance.Init(app.GetContext()); err != nil {
log.Fatalf("Host instance init error: %v", err)
log.Errorf("Host instance init error: %s, wait 3 minutes to retry", err)
time.Sleep(time.Minute * 3)
log.Fatalf("Host instance init error: %s, reboot now", err)
}
app_common.InitAuth(&options.HostOptions.CommonOptions, func() {

View File

@@ -39,7 +39,7 @@ import (
type IBridgeDriver interface {
MigrateSlaveConfigs(IBridgeDriver) error
ConfirmToConfig() (bool, error)
ConfirmToConfig() (bool, string, error)
GetMac() string
GetVlanId() int
FetchConfig()
@@ -318,10 +318,10 @@ func (d *SBaseBridgeDriver) MigrateSlaveConfigs(o IBridgeDriver) error {
return nil
}
func (d *SBaseBridgeDriver) ConfirmToConfig() (bool, error) {
func (d *SBaseBridgeDriver) ConfirmToConfig() (bool, string, error) {
exist, err := d.drv.Exists()
if err != nil {
return false, err
return false, "", err
}
if exist {
d.bridge.FetchConfig2(d.ip, d.ip6)
@@ -329,86 +329,82 @@ func (d *SBaseBridgeDriver) ConfirmToConfig() (bool, error) {
if len(d.bridge.Addr) == 0 {
log.Infof("bridge %s has no ip assignment initially", d.bridge)
if len(d.inter.Addr) == 0 {
return false, fmt.Errorf("Neither %s nor %s owner address %s",
d.inter, d.bridge, d.ip)
return false, "", fmt.Errorf("Neither %s nor %s owner address %s", d.inter, d.bridge, d.ip)
}
if d.inter.Addr != d.ip {
return false, fmt.Errorf("%s!=%s, %s not same as config",
d.ip, d.inter.Addr, d.inter)
return false, "", fmt.Errorf("%s!=%s, %s not same as config", d.ip, d.inter.Addr, d.inter)
}
log.Infof("Bridge address is not configured")
return false, nil
return false, fmt.Sprintf("bridge %s address is empty, should be configured", d.bridge), nil
} else {
log.Infof("bridge %s already has ip %s", d.bridge, d.bridge.Addr)
}
if d.bridge.Addr != d.ip {
return false, fmt.Errorf("%s IP %s!=%s, mismatch", d.bridge, d.bridge.Addr, d.ip)
return false, "", fmt.Errorf("bridge %s IP %s is not expected IP %s, mismatch", d.bridge, d.bridge.Addr, d.ip)
}
} else {
if d.inter != nil && len(d.inter.Addr) > 0 {
return false, fmt.Errorf("%s should have no address", d.inter)
return false, "", fmt.Errorf("interface %s should have no address", d.inter)
}
if len(d.bridge.Addr) == 0 {
return false, nil
return false, "", fmt.Errorf("bridge %s should have 169.254.0.0/16 address", d.bridge)
}
if !d.bridge.IsSecretInterface() {
return false, fmt.Errorf("%s should have address in 169.254.0.0/16", d.bridge)
return false, "", fmt.Errorf("%s should have address in 169.254.0.0/16", d.bridge)
}
}
if len(d.ip6) > 0 {
if len(d.bridge.Addr6) == 0 {
log.Infof("bridge %s has no ipv6 assignment initially", d.bridge)
if len(d.inter.Addr6) == 0 {
return false, fmt.Errorf("Neither %s nor %s owner ipv6 address %s",
d.inter, d.bridge, d.ip6)
return false, "", fmt.Errorf("neither interface%s nor bridge %s owner ipv6 address %s", d.inter, d.bridge, d.ip6)
}
if d.inter.Addr6 != d.ip6 {
return false, fmt.Errorf("%s!=%s, %s not same as config",
d.ip6, d.inter.Addr6, d.inter)
return false, "", fmt.Errorf("interface %s IP %s is not %s as configured", d.inter, d.inter.Addr6, d.ip6)
}
log.Infof("Bridge ipv6 address is not configured")
return false, nil
return false, fmt.Sprintf("bridge %s ipv6 address is empty, should be configured", d.bridge), nil
} else {
log.Infof("bridge %s already has ipv6 address %s", d.bridge, d.bridge.Addr6)
}
if d.bridge.Addr6 != d.ip6 {
return false, fmt.Errorf("%s IP %s!=%s, mismatch", d.bridge, d.bridge.Addr6, d.ip6)
return false, "", fmt.Errorf("%s IP %s!=%s, mismatch", d.bridge, d.bridge.Addr6, d.ip6)
}
} else {
if d.inter != nil && len(d.inter.Addr6) > 0 {
return false, fmt.Errorf("%s should have no ipv6 address", d.inter)
return false, "", fmt.Errorf("interface %s should have no ipv6 address", d.inter)
}
if len(d.bridge.Addr6) == 0 {
return false, nil
if len(d.bridge.Addr6) != 0 {
return false, "", fmt.Errorf("bridge %s (%s) shoud have no ipv6 address", d.bridge, d.bridge.Addr6)
}
if !d.bridge.IsSecretInterface6() {
return false, fmt.Errorf("%s(%s,%s) should have link local address in fe80::/10", d.bridge, d.bridge.Addr6, d.bridge.Addr6LinkLocal)
return false, "", fmt.Errorf("bridge %s(%s,%s) should have link local address in fe80::/10", d.bridge, d.bridge.Addr6, d.bridge.Addr6LinkLocal)
}
}
infs, err := d.drv.Interfaces()
if err != nil {
return false, err
return false, "", fmt.Errorf("get all interfaces of bridge %s failed: %s", d.bridge, err)
}
if d.inter != nil && !utils.IsInStringArray(d.inter.String(), infs) {
log.Infof("Interface %s not in bridge...", d.inter)
return false, nil
return false, fmt.Sprintf("interface %s not in bridge %s", d.inter, d.bridge), nil
}
if err := d.BringupInterface(); err != nil {
log.Errorln(err)
return false, err
return false, "", fmt.Errorf("bring up bridge %s failed: %s", d.bridge, err)
}
return true, nil
return true, "", nil
} else {
if d.bridge.FetchInter() != nil {
return false, fmt.Errorf("Bridge %s exists, but not created by this driver????", d.bridge)
return false, "", fmt.Errorf("bridge %s exists, but not created by this driver????", d.bridge)
}
if len(d.ip) > 0 && (d.inter == nil || len(d.inter.Addr) == 0) {
return false, fmt.Errorf("Interface %s not configured", d.inter)
return false, "", fmt.Errorf("interface %s not configured", d.inter)
}
if len(d.ip6) > 0 && (d.inter == nil || len(d.inter.Addr6) == 0) {
return false, fmt.Errorf("Interface %s ipv6 not configured", d.inter)
return false, "", fmt.Errorf("interface %s ipv6 not configured", d.inter)
}
return false, nil
return false, fmt.Sprintf("bridge %s does not exist", d.bridge), nil
}
}
@@ -420,17 +416,11 @@ func (d *SBaseBridgeDriver) SetupAddresses() error {
if err := d.inter.Shutdown(); err != nil {
return errors.Wrapf(err, "shutdown bridge %s slave ifname: %s", br, ifname)
}
for _, cmd := range [][]string{
{"/sbin/ifdown", ifname},
{"nmcli", "connection", "down", ifname},
} {
output, err := procutils.NewRemoteCommandAsFarAsPossible(cmd[0], cmd[1:]...).Output()
if err != nil {
log.Errorf("run cmd: %s, output: %s, error: %s", strings.Join(cmd, " "), string(output), err)
} else {
log.Infof("run cmd: %s, output: %s", strings.Join(cmd, " "), string(output))
break
}
// ensure the device is not managed by NetworkManager
// nmcli dev set <interface_name> managed no
output, err := procutils.NewRemoteCommandAsFarAsPossible("nmcli", "dev", "set", ifname, "managed", "no").Output()
if err != nil {
log.Errorf("run cmd: nmcli dev set %s managed no, output: %s, error: %s", ifname, string(output), err)
}
}
{
@@ -466,8 +456,9 @@ func (d *SBaseBridgeDriver) SetupAddresses() error {
return errors.Wrapf(err, "setting bridge %s up", br)
}
if d.inter != nil {
ifname := d.inter.String()
// bring up the origin interface
ethLink := iproute2.NewLink(d.inter.String()).Up()
ethLink := iproute2.NewLink(ifname).Up()
if options.HostOptions.TunnelPaddingBytes > 0 {
mtu := 1500 + int(options.HostOptions.TunnelPaddingBytes)
ethLink.MTU(mtu)
@@ -475,6 +466,9 @@ func (d *SBaseBridgeDriver) SetupAddresses() error {
if err := ethLink.Err(); err != nil {
return errors.Wrapf(err, "setting origin interface %s up", d.inter.String())
}
if err := iproute2.NewAddress(ifname).Exact().Err(); err != nil {
return errors.Wrapf(err, "clear interface %s address", ifname)
}
}
}
@@ -591,14 +585,13 @@ func (d *SBaseBridgeDriver) Setup(o IBridgeDriver) error {
// sleep 1 second to wait for bridge setup
time.Sleep(1 * time.Second)
// to setup default routes
if len(d.ip) > 0 || len(d.ip6) > 0 {
setupRoutes := make([]iproute2.RouteSpec, 0)
{
for i := range routes {
find := false
if !find {
if routes[i].Dst.String() == addr2Prefix(d.ip, d.maskLen) || routes[i].Dst.String() == addr2Prefix(d.ip6, d.mask6Len) {
if (len(d.ip) > 0 && routes[i].Dst.String() == addr2Prefix(d.ip, d.maskLen)) || (len(d.ip6) > 0 && routes[i].Dst.String() == addr2Prefix(d.ip6, d.mask6Len)) {
find = true
}
}

View File

@@ -70,9 +70,8 @@ func (r *SDHCP6Relay) ServeDHCP(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *
return pkg, nil, err
}
func (r *SDHCP6Relay) ServeRA(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (dhcp.Packet, net.IP, net.HardwareAddr, error) {
pkg, err := r.serveDHCPInternal(pkt, addr)
return pkg, nil, nil, err
func (r *SDHCP6Relay) OnRecvICMP6(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) error {
return nil
}
func (r *SDHCP6Relay) serveDHCPInternal(pkt dhcp.Packet, _ *net.UDPAddr) (dhcp.Packet, error) {

View File

@@ -221,8 +221,8 @@ func getGuestConfig(
mainIp6 = mainNic6.Ip6
}
route4 := make([]dhcp.SRouteInfo, 0)
route6 := make([]dhcp.SRouteInfo, 0)
route4 := make([]netutils2.SRouteInfo, 0)
route6 := make([]netutils2.SRouteInfo, 0)
if nicdesc.IsDefault {
osName := guestDesc.OsName
if len(osName) == 0 {
@@ -233,10 +233,12 @@ func getGuestConfig(
conf.Gateway = net.ParseIP(nicdesc.Gateway)
if !strings.HasPrefix(strings.ToLower(osName), "win") {
route4 = append(route4, dhcp.SRouteInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
Gateway: net.ParseIP(nicdesc.Gateway),
route4 = append(route4, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
},
Gateway: net.ParseIP(nicdesc.Gateway),
})
}
}
@@ -244,10 +246,12 @@ func getGuestConfig(
if len(nicdesc.Gateway6) > 0 {
conf.Gateway6 = net.ParseIP(nicdesc.Gateway6)
conf.PrefixLen6 = uint8(nicdesc.Masklen6)
route6 = append(route6, dhcp.SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP(nicdesc.Gateway6),
route6 = append(route6, netutils2.SRouteInfo{
SPrefixInfo: netutils2.SPrefixInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
},
Gateway: net.ParseIP(nicdesc.Gateway6),
})
}
}

View File

@@ -113,13 +113,9 @@ func (s *SGuestDHCP6Server) ServeDHCP(pkt dhcp.Packet, cliMac net.HardwareAddr,
return pkg, nil, err
}
func (s *SGuestDHCP6Server) ServeRA(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (dhcp.Packet, net.IP, net.HardwareAddr, error) {
func (s *SGuestDHCP6Server) OnRecvICMP6(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) error {
log.Infof("SGuestDHCP6Server ServeRA from %s", cliMac.String())
var conf = s.getConfig(cliMac, nil)
if conf != nil {
return dhcp.MakeRouterAdverPacket(conf.Gateway6, conf.PrefixLen6, conf.Routes6, uint32(conf.MTU))
}
return nil, nil, nil, nil
return nil
}
func (s *SGuestDHCP6Server) serveDHCPInternal(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (dhcp.Packet, error) {

View File

@@ -329,12 +329,12 @@ func NewNIC(desc string) (*SNIC, error) {
return nil, errors.Wrapf(err, "hostbridge.NewDriver driver: %s, bridge: %s, interface: %s, ip: %s/%d, ip6: %s/%d", options.HostOptions.BridgeDriver, nic.Bridge, nic.Inter, nic.Ip, nic.Mask, nic.Ip6, nic.Mask6)
}
confirm, err := nic.BridgeDev.ConfirmToConfig()
confirm, msg, err := nic.BridgeDev.ConfirmToConfig()
if err != nil {
return nil, errors.Wrapf(err, "nic.BridgeDev.ConfirmToConfig %#v", nic.BridgeDev)
}
if !confirm {
log.Infof("Not confirm to configuration")
log.Infof("Not confirm to configuration, bridge %s reason: %s", nic.Bridge, msg)
if err = nic.BridgeDev.Setup(nic.BridgeDev); err != nil {
return nil, errors.Wrapf(err, "nic.BridgeDev.Setup %v", nic.BridgeDev)
}

View File

@@ -71,12 +71,13 @@ const (
type conn interface {
io.Closer
Send(b []byte, addr *net.UDPAddr, destMac net.HardwareAddr) error
Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int, error)
Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, error)
Send6(b []byte, addr *net.UDPAddr, destMac net.HardwareAddr) error
SendICMP6(b []byte, srcIp net.IP, srcMac, destMac net.HardwareAddr) error
Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int, error)
Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, error)
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
SendRaw(b []byte, destMac net.HardwareAddr) error
}
// Conn is a DHCP-oriented packet socket.
@@ -171,7 +172,7 @@ func (c *Conn) Close() error {
// packet and the interface it was received on.
func (c *Conn) RecvDHCP() (Packet, *net.UDPAddr, net.HardwareAddr, error) {
var buf [1500]byte
b, addr, mac, _, err := c.conn.Recv(buf[:])
b, addr, mac, err := c.conn.Recv(buf[:])
if err != nil {
return nil, nil, nil, err
}
@@ -181,7 +182,7 @@ func (c *Conn) RecvDHCP() (Packet, *net.UDPAddr, net.HardwareAddr, error) {
func (c *Conn) RecvDHCP6() (Packet, *net.UDPAddr, net.HardwareAddr, error) {
var buf [1500]byte
b, addr, mac, _, err := c.conn.Recv6(buf[:])
b, addr, mac, err := c.conn.Recv6(buf[:])
if err != nil {
return nil, nil, nil, err
}
@@ -214,9 +215,9 @@ func (c *Conn) SendDHCP6(pkt Packet, addr *net.UDPAddr, mac net.HardwareAddr) er
return c.conn.Send6(b, addr, mac)
}
func (c *Conn) SendICMP6(pkt Packet, srcIp net.IP, srcmac, dstmac net.HardwareAddr) error {
func (c *Conn) SendRaw(pkt Packet, dstmac net.HardwareAddr) error {
b := pkt.Marshal()
return c.conn.SendICMP6(b, srcIp, srcmac, dstmac)
return c.conn.SendRaw(b, dstmac)
}
// SetReadDeadline sets the deadline for future Read calls. If the
@@ -303,13 +304,13 @@ func (s *socketConn) Close() error {
return syscall.Close(s.sock)
}
func (s *socketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int, error) {
func (s *socketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, error) {
n, a, err := syscall.Recvfrom(s.sock, b, 0)
if err != nil {
return nil, nil, nil, 0, err
return nil, nil, nil, err
}
if addr, ok := a.(*syscall.SockaddrInet4); !ok {
return nil, nil, nil, 0, errors.Wrap(errors.ErrUnsupportedProtocol, "Recvfrom recevice address is not famliy Inet4")
return nil, nil, nil, errors.Wrap(errors.ErrUnsupportedProtocol, "Recvfrom recevice address is not famliy Inet4")
} else {
ip := net.IP{addr.Addr[0], addr.Addr[1], addr.Addr[2], addr.Addr[3]}
udpAddr := &net.UDPAddr{
@@ -317,7 +318,7 @@ func (s *socketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int
Port: addr.Port,
}
// there is no interface index info
return b[:n], udpAddr, nil, 0, nil
return b[:n], udpAddr, nil, nil
}
}

View File

@@ -119,13 +119,13 @@ func newSocketConn6(addr net.IP, port int, disableBroadcast bool) (conn, error)
return &socketConn{sock}, nil
}
func (s *socketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int, error) {
func (s *socketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, error) {
n, a, err := syscall.Recvfrom(s.sock, b, 0)
if err != nil {
return nil, nil, nil, 0, err
return nil, nil, nil, err
}
if addr, ok := a.(*syscall.SockaddrInet6); !ok {
return nil, nil, nil, 0, errors.Wrap(errors.ErrUnsupportedProtocol, "Recvfrom recevice address is not famliy Inet6")
return nil, nil, nil, errors.Wrap(errors.ErrUnsupportedProtocol, "Recvfrom recevice address is not famliy Inet6")
} else {
ip := net.IP(addr.Addr[:])
udpAddr := &net.UDPAddr{
@@ -133,7 +133,7 @@ func (s *socketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, in
Port: addr.Port,
}
// there is no interface index info
return b[:n], udpAddr, nil, 0, nil
return b[:n], udpAddr, nil, nil
}
}
@@ -147,6 +147,6 @@ func (s *socketConn) Send6(b []byte, addr *net.UDPAddr, destMac net.HardwareAddr
return syscall.Sendto(s.sock, b, 0, destAddr)
}
func (s *socketConn) SendICMP6(b []byte, srcIp net.IP, srcMac, destMac net.HardwareAddr) error {
func (s *socketConn) SendRaw(b []byte, destMac net.HardwareAddr) error {
return errors.Wrap(errors.ErrNotImplemented, "SendICMP6 not implemented")
}

View File

@@ -87,11 +87,11 @@ func (s *rawSocketConn) Close() error {
return s.conn.Close()
}
func (s *rawSocketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int, error) {
func (s *rawSocketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, error) {
// read packet
n, addr, err := s.conn.ReadFrom(b)
if err != nil {
return nil, nil, nil, 0, errors.Wrap(err, "Read from errror")
return nil, nil, nil, errors.Wrap(err, "Read from errror")
}
log.Debugf("rawSocketConn Recv %d bytes", n)
@@ -99,12 +99,12 @@ func (s *rawSocketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
srcMac, err := net.ParseMAC(addr.String())
if err != nil {
return nil, nil, nil, 0, errors.Wrap(err, "Parse mac error")
return nil, nil, nil, errors.Wrap(err, "Parse mac error")
}
p := gopacket.NewPacket(b, layers.LayerTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "Failed to decode packet")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "Failed to decode packet")
}
var srcIp net.IP
@@ -115,7 +115,7 @@ func (s *rawSocketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
ip4 := ipLayer.(*layers.IPv4)
srcIp = ip4.SrcIP
} else {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "Expect IP packet")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "Expect IP packet")
}
}
@@ -125,7 +125,7 @@ func (s *rawSocketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
udpInfo := udpLayer.(*layers.UDP)
srcPort = uint16(udpInfo.SrcPort)
} else {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "Expect UDP packet")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "Expect UDP packet")
}
dhcpLayer := p.Layer(layers.LayerTypeDHCPv4)
@@ -134,11 +134,11 @@ func (s *rawSocketConn) Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
dhcp4 := dhcpLayer.(*layers.DHCPv4)
sbf := gopacket.NewSerializeBuffer()
if err := dhcp4.SerializeTo(sbf, gopacket.SerializeOptions{}); err != nil {
return nil, nil, nil, 0, errors.Wrap(err, "Serialize dhcp packet error")
return nil, nil, nil, errors.Wrap(err, "Serialize dhcp packet error")
}
return sbf.Bytes(), &net.UDPAddr{IP: srcIp, Port: int(srcPort)}, srcMac, 0, nil
return sbf.Bytes(), &net.UDPAddr{IP: srcIp, Port: int(srcPort)}, srcMac, nil
} else {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "Expect DHCP packet")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "Expect DHCP packet")
}
}

View File

@@ -72,11 +72,11 @@ func newRawSocketConn6(iface string, filter []bpf.RawInstruction, serverPort uin
}, nil
}
func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int, error) {
func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, error) {
// read packet
n, addr, err := s.conn.ReadFrom(b)
if err != nil {
return nil, nil, nil, 0, errors.Wrap(err, "Read from errror")
return nil, nil, nil, errors.Wrap(err, "Read from errror")
}
log.Debugf("rawSocketConn Recv6 %d bytes from %s", n, addr.String())
@@ -84,12 +84,12 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
srcMac, err := net.ParseMAC(addr.String())
if err != nil {
return nil, nil, nil, 0, errors.Wrap(err, "Parse mac error")
return nil, nil, nil, errors.Wrap(err, "Parse mac error")
}
p := gopacket.NewPacket(b, layers.LayerTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "Failed to decode packet")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "Failed to decode packet")
}
var srcIp net.IP
@@ -100,14 +100,14 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
ip6 := ipLayer.(*layers.IPv6)
srcIp = ip6.SrcIP
} else {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "Expect IPv6 packet")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "Expect IPv6 packet")
}
}
icmpLayer := p.Layer(layers.LayerTypeICMPv6)
if icmpLayer != nil {
log.Infof("rawSocketConn Recv6 icmpLayer %s", icmpLayer.LayerType())
raLayer := p.Layer(layers.LayerTypeICMPv6RouterSolicitation)
/*raLayer := p.Layer(layers.LayerTypeICMPv6RouterSolicitation)
if raLayer != nil {
// icmp6, ra solitation
raPkt := raLayer.(*layers.ICMPv6RouterSolicitation)
@@ -118,7 +118,8 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
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")
}
}*/
return b, &net.UDPAddr{IP: srcIp, Port: icmpRAFakePort}, srcMac, nil
}
var srcPort uint16
@@ -127,7 +128,7 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
udpInfo := udpLayer.(*layers.UDP)
srcPort = uint16(udpInfo.SrcPort)
} else {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "expect UDP packet")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "expect UDP packet")
}
dhcpLayer := p.Layer(layers.LayerTypeDHCPv6)
@@ -136,51 +137,17 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
dhcp6 := dhcpLayer.(*layers.DHCPv6)
sbf := gopacket.NewSerializeBuffer()
if err := dhcp6.SerializeTo(sbf, gopacket.SerializeOptions{}); err != nil {
return nil, nil, nil, 0, errors.Wrap(err, "Serialize dhcp6 packet error")
return nil, nil, nil, errors.Wrap(err, "Serialize dhcp6 packet error")
}
return sbf.Bytes(), &net.UDPAddr{IP: srcIp, Port: int(srcPort)}, srcMac, 0, nil
return sbf.Bytes(), &net.UDPAddr{IP: srcIp, Port: int(srcPort)}, srcMac, nil
} else {
return nil, nil, nil, 0, errors.Wrap(p.ErrorLayer().Error(), "Fetch dhcp layer failed")
return nil, nil, nil, errors.Wrap(p.ErrorLayer().Error(), "Fetch dhcp layer failed")
}
}
func (s *rawSocketConn) SendICMP6(b []byte, srcIP net.IP, srcMac, destMac net.HardwareAddr) error {
var icmpRouterAdvMsg = new(layers.ICMPv6RouterAdvertisement)
if err := icmpRouterAdvMsg.DecodeFromBytes(b, gopacket.NilDecodeFeedback); err != nil {
return errors.Wrap(err, "Decode icmp router advertisement bytes error")
}
var eth = &layers.Ethernet{
EthernetType: layers.EthernetTypeIPv6,
SrcMAC: s.iface.HardwareAddr,
DstMAC: destMac,
}
dstIP := net.ParseIP("ff02::1")
var ip = &layers.IPv6{
Version: 6,
HopLimit: 64,
SrcIP: srcIP,
DstIP: dstIP,
NextHeader: layers.IPProtocolICMPv6,
}
var icmp6 = &layers.ICMPv6{
TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRouterAdvertisement, 0),
}
icmp6.SetNetworkLayerForChecksum(ip)
var (
buf = gopacket.NewSerializeBuffer()
opts = gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true}
)
if err := gopacket.SerializeLayers(buf, opts, eth, ip, icmp6, icmpRouterAdvMsg); err != nil {
return errors.Wrap(err, "SerializeLayers error")
}
func (s *rawSocketConn) SendRaw(b []byte, destMac net.HardwareAddr) error {
// s.conn.SetWriteDeadline(time.Now().Add(DefaultWriteTimeout)) // 2 second
if _, err := s.conn.WriteTo(buf.Bytes(), &packet.Addr{HardwareAddr: destMac}); err != nil {
if _, err := s.conn.WriteTo(b, &packet.Addr{HardwareAddr: destMac}); err != nil {
return errors.Wrap(err, "Send icmp packet error")
}
return nil

View File

@@ -22,7 +22,8 @@ import (
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/netutils2"
)
const (
@@ -39,24 +40,24 @@ type ResponseConfig struct {
OsName string
ServerIP net.IP // OptServerIdentifier 54
ClientIP net.IP
Gateway net.IP // OptRouters 3
Domain string // OptDomainName 15
LeaseTime time.Duration // OptLeaseTime 51
RenewalTime time.Duration // OptRenewalTime 58
BroadcastAddr net.IP // OptBroadcastAddr 28
Hostname string // OptHostname 12
SubnetMask net.IP // OptSubnetMask 1
DNSServers []net.IP // OptDNSServers
Routes []SRouteInfo // TODO: 249 for windows, 121 for linux
NTPServers []net.IP // OptNTPServers 42
MTU uint16 // OptMTU 26
Gateway net.IP // OptRouters 3
Domain string // OptDomainName 15
LeaseTime time.Duration // OptLeaseTime 51
RenewalTime time.Duration // OptRenewalTime 58
BroadcastAddr net.IP // OptBroadcastAddr 28
Hostname string // OptHostname 12
SubnetMask net.IP // OptSubnetMask 1
DNSServers []net.IP // OptDNSServers
Routes []netutils2.SRouteInfo // TODO: 249 for windows, 121 for linux
NTPServers []net.IP // OptNTPServers 42
MTU uint16 // OptMTU 26
ClientIP6 net.IP
Gateway6 net.IP
PrefixLen6 uint8
DNSServers6 []net.IP
NTPServers6 []net.IP
Routes6 []SRouteInfo
Routes6 []netutils2.SRouteInfo
// Relay Info https://datatracker.ietf.org/doc/html/rfc3046
RelayInfo []byte
@@ -95,33 +96,7 @@ func GetOptTime(d time.Duration) []byte {
return timeBytes
}
type SRouteInfo struct {
Prefix net.IP
PrefixLen uint8
Gateway net.IP
}
func (r SRouteInfo) String() string {
return fmt.Sprintf("%s/%d via %s", r.Prefix.String(), r.PrefixLen, r.Gateway.String())
}
func ParseRouteInfo(route []string) (*SRouteInfo, error) {
if len(route) < 2 {
return nil, errors.Wrapf(errors.ErrInvalidStatus, "invalid route %#v", route)
}
_, prefixLen, err := net.ParseCIDR(route[0])
if err != nil {
return nil, errors.Wrapf(err, "net.ParseCIDR %s", route[0])
}
ones, _ := prefixLen.Mask.Size()
return &SRouteInfo{
Prefix: prefixLen.IP,
PrefixLen: uint8(ones),
Gateway: net.ParseIP(route[1]),
}, nil
}
func getClasslessRoutePack(route SRouteInfo) []byte {
func getClasslessRoutePack(route netutils2.SRouteInfo) []byte {
// var snet, gw = route[0], route[1]
// tmp := strings.Split(snet, "/")
netaddr := route.Prefix

View File

@@ -16,8 +16,9 @@ package dhcp
import (
"fmt"
"net"
"testing"
"yunion.io/x/onecloud/pkg/util/netutils2"
)
func compareBytes(b1, b2 []byte) error {
@@ -61,7 +62,7 @@ func TestGetClasslessRoutePack(t *testing.T) {
},
}
for _, c := range cases {
routeInfo, err := ParseRouteInfo([]string{c.net, c.gw})
routeInfo, err := netutils2.ParseRouteInfo([]string{c.net, c.gw})
if err != nil {
t.Errorf("parse route %s error: %v", c.net, err)
continue
@@ -72,58 +73,3 @@ func TestGetClasslessRoutePack(t *testing.T) {
}
}
}
func TestParseRouteInfo(t *testing.T) {
cases := []struct {
net string
gw string
want *SRouteInfo
}{
{
net: "10.0.0.0/8",
gw: "10.168.120.1",
want: &SRouteInfo{
Prefix: net.ParseIP("10.0.0.0"),
PrefixLen: 8,
Gateway: net.ParseIP("10.168.120.1"),
},
},
{
net: "::/0",
gw: "3ffe:3200:fe::1",
want: &SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP("3ffe:3200:fe::1"),
},
},
{
net: "fd00:ec2::254/128",
gw: "::",
want: &SRouteInfo{
Prefix: net.ParseIP("fd00:ec2::254"),
PrefixLen: 128,
Gateway: net.ParseIP("::"),
},
},
{
net: "fe80::a9fe:a9fe/128",
gw: "::",
want: &SRouteInfo{
Prefix: net.ParseIP("fe80::a9fe:a9fe"),
PrefixLen: 128,
Gateway: net.ParseIP("::"),
},
},
}
for _, c := range cases {
routeInfo, err := ParseRouteInfo([]string{c.net, c.gw})
if err != nil {
t.Errorf("parse route %s error: %v", c.net, err)
continue
}
if routeInfo.String() != c.want.String() {
t.Errorf("parse route expect %s got: %s", c.want.String(), routeInfo.String())
}
}
}

View File

@@ -22,8 +22,13 @@ type DHCPHandler interface {
ServeDHCP(pkt Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (Packet, []string, error)
}
type SendPacket struct {
Packet []byte
DestMac net.HardwareAddr
}
type DHCP6Handler interface {
DHCPHandler
ServeRA(pkt Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (Packet, net.IP, net.HardwareAddr, error)
OnRecvICMP6(pkt Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) error
}

View File

@@ -16,207 +16,12 @@ package dhcp
import (
"encoding/binary"
"fmt"
"net"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
)
/*
https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.4
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MTU |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpV6OptMtu struct {
MTU uint32
}
func (opt icmpV6OptMtu) Bytes() []byte {
buf := make([]byte, 6)
binary.BigEndian.PutUint32(buf[2:], opt.MTU)
return buf
}
func NewIcmpV6OptMtu(mtu uint32) icmpV6OptMtu {
return icmpV6OptMtu{
MTU: mtu,
}
}
/*
https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.2
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Prefix Length |L|A| Reserved1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Valid Lifetime |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Preferred Lifetime |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Prefix +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpv6OptPrefixInfo struct {
PrefixLen uint8
Flag uint8
ValidLifetime uint32
PreferredLifetime uint32
Reserved2 uint32
Prefix []byte
}
func (opt icmpv6OptPrefixInfo) Bytes() []byte {
buf := make([]byte, 30)
buf[0] = opt.PrefixLen
buf[1] = opt.Flag
binary.BigEndian.PutUint32(buf[2:], opt.ValidLifetime)
binary.BigEndian.PutUint32(buf[6:], opt.PreferredLifetime)
binary.BigEndian.PutUint32(buf[10:], opt.Reserved2)
copy(buf[14:], opt.Prefix[:])
return buf
}
func NewIcmpv6OptPrefixInfo(gw net.IP, preflen uint8) icmpv6OptPrefixInfo {
_, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%s/%d", gw.String(), preflen))
return icmpv6OptPrefixInfo{
PrefixLen: preflen,
Flag: 0x80,
ValidLifetime: 0xffffffff,
PreferredLifetime: 0xffffffff,
Reserved2: 0,
Prefix: ipnet.IP,
}
}
/*
https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.1
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Link-Layer Address ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpv6OptSourceTargetAddress struct {
Mac net.HardwareAddr
}
func (opt icmpv6OptSourceTargetAddress) Bytes() []byte {
return opt.Mac[0:6]
}
func NewIcmpv6OptSourceTargetAddress(macAddr net.HardwareAddr) icmpv6OptSourceTargetAddress {
return icmpv6OptSourceTargetAddress{
Mac: macAddr,
}
}
/*
https://datatracker.ietf.org/doc/html/rfc4191#section-2.3
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Prefix Length |Resvd|Prf|Resvd|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Route Lifetime |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Prefix (Variable Length) |
. .
. .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpv6OptRouteInfo struct {
PrefixLen uint8
Reserved1 uint8
RouteLifetime uint32
Prefix []byte
}
func (opt icmpv6OptRouteInfo) Bytes() []byte {
buf := make([]byte, 6)
buf[0] = opt.PrefixLen
buf[1] = opt.Reserved1
binary.BigEndian.PutUint32(buf[2:6], opt.RouteLifetime)
buf = append(buf, opt.Prefix...)
return buf
}
func NewIcmpv6OptRouteInfo(routeLifetime uint32, prefix []byte, prefixLen uint8) icmpv6OptRouteInfo {
if prefixLen <= 0 {
prefix = prefix[0:0]
} else if prefixLen <= 64 {
prefix = prefix[0:8]
} else {
prefix = prefix[0:16]
}
return icmpv6OptRouteInfo{
PrefixLen: prefixLen,
Reserved1: 0,
RouteLifetime: routeLifetime,
Prefix: prefix,
}
}
func MakeRouterAdverPacket(gwIP net.IP, prefixLen uint8, routes6 []SRouteInfo, mtu uint32) (Packet, net.IP, net.HardwareAddr, error) {
pkt := layers.ICMPv6RouterAdvertisement{}
// https://datatracker.ietf.org/doc/html/rfc4861#section-4.2
// https://datatracker.ietf.org/doc/html/rfc4191
pkt.Flags = 0xc0 // M=1, O=1, stateful DHCPv6, pref=00
pkt.HopLimit = 64
pkt.RouterLifetime = 9000
pkt.ReachableTime = 0
pkt.RetransTimer = 0
// gwMac, _ := net.ParseMAC("14:09:dc:cf:16:d5")
pkt.Options = layers.ICMPv6Options{
/*layers.ICMPv6Option{
Type: layers.ICMPv6OptSourceAddress,
Data: NewIcmpv6OptSourceTargetAddress(gwMac).Bytes(),
},*/
layers.ICMPv6Option{
Type: layers.ICMPv6OptMTU,
Data: NewIcmpV6OptMtu(mtu).Bytes(),
},
layers.ICMPv6Option{
Type: layers.ICMPv6OptPrefixInfo,
Data: NewIcmpv6OptPrefixInfo(gwIP, prefixLen).Bytes(),
},
}
for i := range routes6 {
rt := routes6[i]
pkt.Options = append(pkt.Options, layers.ICMPv6Option{
Type: 24,
Data: NewIcmpv6OptRouteInfo(9000, rt.Prefix.To16(), rt.PrefixLen).Bytes(),
})
}
sbf := gopacket.NewSerializeBuffer()
err := pkt.SerializeTo(sbf, gopacket.SerializeOptions{})
if err != nil {
return nil, nil, nil, errors.Wrap(err, "SerializeTo")
}
return sbf.Bytes(), gwIP, nil, nil
}
// DHCPv6 https://datatracker.ietf.org/doc/html/rfc8415
/*
0 1 2 3

View File

@@ -229,9 +229,6 @@ func (s *DHCPServer) serveDHCP(handler DHCPHandler) error {
log.Errorf("Receiving DHCP packet: %s", err)
continue
}
// if intf == nil {
// return fmt.Errorf("Received DHCP packet with no interface information (this is a violation of dhcp4.Conn's contract)")
// }
go func() {
defer func() {
@@ -279,8 +276,6 @@ func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
continue
}
log.Debugf("[DHCP6] received packet %d from %s mac %s", len(pkt), addr, mac)
go func() {
defer func() {
if r := recover(); r != nil {
@@ -291,21 +286,7 @@ func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
if addr.Port == icmpRAFakePort {
// receive a RA solication
resp, gwIp, gwMac, err := handler.ServeRA(pkt, mac, addr)
if err != nil {
log.Warningf("[DHCP6] handler ServeRA error: %s", err)
return
}
if resp == nil {
log.Warningf("[DHCP6] hander ServeRA response null packet")
return
}
// RA should be sent to FF02::1 from gwIP
err = s.conn.SendICMP6(resp, gwIp, gwMac, mac)
if err != nil {
log.Errorf("[DHCP6] failed to response packet for ICMP6 message: %s", err)
}
handler.OnRecvICMP6(pkt, mac, addr)
return
}

15
pkg/util/icmp6/doc.go Normal file
View File

@@ -0,0 +1,15 @@
// 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 icmp6 // import "yunion.io/x/onecloud/pkg/util/icmp6"

459
pkg/util/icmp6/messages.go Normal file
View File

@@ -0,0 +1,459 @@
// 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 icmp6
import (
"fmt"
"net"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"yunion.io/x/pkg/errors"
)
type TPreference int
const (
PreferenceLow TPreference = 0
PreferenceMedium TPreference = 1
PreferenceHigh TPreference = 2
)
func (pref TPreference) String() string {
switch pref {
case PreferenceLow:
return "low"
case PreferenceMedium:
return "medium"
case PreferenceHigh:
return "high"
}
return "unknown"
}
type SBaseICMP6Message struct {
SrcMac net.HardwareAddr
SrcIP net.IP
DstMac net.HardwareAddr
DstIP net.IP
}
func (msg SBaseICMP6Message) String() string {
return fmt.Sprintf("%s[%s]->%s[%s]", msg.SrcIP.String(), msg.SrcMac.String(), msg.DstIP.String(), msg.DstMac.String())
}
type IICMP6Message interface {
Payload() gopacket.SerializableLayer
ICMP6TypeCode() layers.ICMPv6TypeCode
SourceMac() net.HardwareAddr
SourceIP() net.IP
DestinationMac() net.HardwareAddr
DestinationIP() net.IP
}
func (msg SBaseICMP6Message) SourceIP() net.IP {
return msg.SrcIP
}
func (msg SBaseICMP6Message) SourceMac() net.HardwareAddr {
return msg.SrcMac
}
func (msg SBaseICMP6Message) DestinationIP() net.IP {
return msg.DstIP
}
func (msg SBaseICMP6Message) DestinationMac() net.HardwareAddr {
return msg.DstMac
}
type SRouterSolicitation struct {
SBaseICMP6Message
}
type SPrefixInfoOption struct {
IsOnlink bool
IsAutoconf bool
Prefix net.IP
PrefixLen uint8
ValidLifetime uint32
PreferredLifetime uint32
}
func (opt SPrefixInfoOption) String() string {
return fmt.Sprintf("%s/%d (IsOnlink: %t, IsAutoconf: %t) ValidLifetime: %d, PreferredLifetime: %d", opt.Prefix.String(), opt.PrefixLen, opt.IsOnlink, opt.IsAutoconf, opt.ValidLifetime, opt.PreferredLifetime)
}
type SRouteInfoOption struct {
RouteLifetime uint32
Prefix net.IP
PrefixLen uint8
Preference TPreference
}
func (opt SRouteInfoOption) String() string {
return fmt.Sprintf("%s/%d (Pref: %s) RouteLifetime: %d", opt.Prefix.String(), opt.PrefixLen, opt.Preference.String(), opt.RouteLifetime)
}
type SRouterAdvertisement struct {
SBaseICMP6Message
CurHopLimit uint8
IsManaged bool
IsOther bool
IsHomeAgent bool
Preference TPreference
RouterLifetime uint16
ReachableTime uint32
RetransTimer uint32
MTU uint32
PrefixInfo []SPrefixInfoOption
RouteInfo []SRouteInfoOption
}
type SNeighborSolicitation struct {
SBaseICMP6Message
/*
The IP address of the target of the solicitation.
It MUST NOT be a multicast address.
*/
TargetAddr net.IP
}
type SNeighborAdvertisement struct {
SBaseICMP6Message
/*
For solicited advertisements, the Target Address
field in the Neighbor Solicitation message that
prompted this advertisement. For an unsolicited
advertisement, the address whose link-layer address
has changed. The Target Address MUST NOT be a
multicast address.
*/
TargetAddr net.IP
IsRouter bool
IsSolicited bool
}
func (msg SNeighborSolicitation) ICMP6TypeCode() layers.ICMPv6TypeCode {
return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborSolicitation, 0)
}
func (msg SNeighborSolicitation) Payload() gopacket.SerializableLayer {
pkt := layers.ICMPv6NeighborSolicitation{}
pkt.TargetAddress = msg.TargetAddr
pkt.Options = layers.ICMPv6Options{
layers.ICMPv6Option{
Type: layers.ICMPv6OptSourceAddress,
Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
},
}
return &pkt
}
func (msg *SNeighborSolicitation) Unmarshal(data *layers.ICMPv6NeighborSolicitation) error {
msg.TargetAddr = data.TargetAddress
return nil
}
func (msg SNeighborAdvertisement) ICMP6TypeCode() layers.ICMPv6TypeCode {
return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborAdvertisement, 0)
}
func (msg SNeighborAdvertisement) Payload() gopacket.SerializableLayer {
pkt := layers.ICMPv6NeighborAdvertisement{}
pkt.TargetAddress = msg.TargetAddr
pkt.Flags = 0x00
if msg.IsRouter {
pkt.Flags |= 0x80
}
if msg.IsSolicited {
pkt.Flags |= 0x40
}
pkt.Options = layers.ICMPv6Options{
layers.ICMPv6Option{
Type: layers.ICMPv6OptSourceAddress,
Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
},
}
return &pkt
}
func (msg *SNeighborAdvertisement) Unmarshal(data *layers.ICMPv6NeighborAdvertisement) error {
msg.TargetAddr = data.TargetAddress
msg.IsRouter = data.Flags&0x80 != 0
msg.IsSolicited = data.Flags&0x40 != 0
return nil
}
func (msg SRouterSolicitation) ICMP6TypeCode() layers.ICMPv6TypeCode {
return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRouterSolicitation, 0)
}
func (msg SRouterSolicitation) Payload() gopacket.SerializableLayer {
pkt := layers.ICMPv6RouterSolicitation{}
pkt.Options = layers.ICMPv6Options{
layers.ICMPv6Option{
Type: layers.ICMPv6OptSourceAddress,
Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
},
}
return &pkt
}
func (msg *SRouterSolicitation) Unmarshal(data *layers.ICMPv6RouterSolicitation) error {
return nil
}
func (msg SRouterAdvertisement) ICMP6TypeCode() layers.ICMPv6TypeCode {
return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRouterAdvertisement, 0)
}
func (msg SRouterAdvertisement) Payload() gopacket.SerializableLayer {
pkt := layers.ICMPv6RouterAdvertisement{}
// https://datatracker.ietf.org/doc/html/rfc4861#section-4.2
// https://datatracker.ietf.org/doc/html/rfc4191
pkt.Flags = 0x00 // M=1, O=1, stateful DHCPv6, pref=00
if msg.IsManaged {
pkt.Flags |= 0x80
}
if msg.IsOther {
pkt.Flags |= 0x40
}
if msg.IsHomeAgent {
pkt.Flags |= 0x20
}
switch msg.Preference {
case PreferenceLow:
pkt.Flags |= 0x18
case PreferenceMedium:
// do nothing
case PreferenceHigh:
pkt.Flags |= 0x08
}
pkt.HopLimit = msg.CurHopLimit
pkt.RouterLifetime = msg.RouterLifetime
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(),
},
}
for i := range msg.PrefixInfo {
pref := msg.PrefixInfo[i]
pkt.Options = append(pkt.Options, layers.ICMPv6Option{
Type: layers.ICMPv6OptPrefixInfo,
Data: NewIcmpv6OptPrefixInfo(pref).Bytes(),
})
}
for i := range msg.RouteInfo {
rt := msg.RouteInfo[i]
pkt.Options = append(pkt.Options, layers.ICMPv6Option{
Type: 24,
Data: NewIcmpv6OptRouteInfo(rt).Bytes(),
})
}
return &pkt
}
func (msg *SRouterAdvertisement) Unmarshal(data *layers.ICMPv6RouterAdvertisement) error {
msg.CurHopLimit = data.HopLimit
msg.IsManaged = data.Flags&0x80 != 0
msg.IsOther = data.Flags&0x40 != 0
msg.IsHomeAgent = data.Flags&0x20 != 0
switch data.Flags & 0x18 {
case 0x18:
msg.Preference = PreferenceLow
case 0x08:
msg.Preference = PreferenceHigh
default:
msg.Preference = PreferenceMedium
}
msg.RouterLifetime = data.RouterLifetime
for i := range data.Options {
opt := data.Options[i]
switch opt.Type {
case layers.ICMPv6OptMTU:
msg.MTU = DecodeIcmpV6OptMtu(opt.Data)
case layers.ICMPv6OptPrefixInfo:
prefix := DecodeIcmpv6OptPrefixInfo(opt.Data)
msg.PrefixInfo = append(msg.PrefixInfo, prefix)
case 24:
route := DecodeIcmpv6OptRouteInfo(opt.Data)
msg.RouteInfo = append(msg.RouteInfo, route)
}
}
return nil
}
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)`,
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 eth = &layers.Ethernet{
EthernetType: layers.EthernetTypeIPv6,
SrcMAC: msg.SourceMac(),
DstMAC: msg.DestinationMac(),
}
var ip = &layers.IPv6{
Version: 6,
HopLimit: 64,
SrcIP: msg.SourceIP(),
DstIP: msg.DestinationIP(),
NextHeader: layers.IPProtocolICMPv6,
}
var icmp6 = &layers.ICMPv6{
TypeCode: msg.ICMP6TypeCode(),
}
icmp6.SetNetworkLayerForChecksum(ip)
var payload = 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 {
return nil, errors.Wrap(err, "SerializeLayers error")
}
return buf.Bytes(), nil
}
func DecodePacket(data []byte) (IICMP6Message, error) {
packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
if packet.ErrorLayer() != nil {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "DecodePacket error")
}
var baseMsg SBaseICMP6Message
{
ethLayer := packet.Layer(layers.LayerTypeEthernet)
if ethLayer != nil {
eth := ethLayer.(*layers.Ethernet)
baseMsg.SrcMac = eth.SrcMAC
baseMsg.DstMac = eth.DstMAC
} else {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect Ethernet layer")
}
}
{
ipLayer := packet.Layer(layers.LayerTypeIPv6)
if ipLayer != nil {
// ipv6
ip6 := ipLayer.(*layers.IPv6)
baseMsg.SrcIP = ip6.SrcIP
baseMsg.DstIP = ip6.DstIP
} else {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect IPv6 packet")
}
}
{
icmpLayer := packet.Layer(layers.LayerTypeICMPv6)
if icmpLayer != nil {
icmp6 := icmpLayer.(*layers.ICMPv6)
if icmp6 == nil {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect IPv6 packet")
}
switch icmp6.TypeCode.Type() {
case 133:
payload := packet.Layer(layers.LayerTypeICMPv6RouterSolicitation)
if payload != nil {
msg := &SRouterSolicitation{
SBaseICMP6Message: baseMsg,
}
return msg, nil
} else {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Router Solicitation packet")
}
case 134:
payload := packet.Layer(layers.LayerTypeICMPv6RouterAdvertisement)
if payload != nil {
msg := &SRouterAdvertisement{
SBaseICMP6Message: baseMsg,
}
return msg, nil
} else {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Router Advertisement packet")
}
case 135:
payload := packet.Layer(layers.LayerTypeICMPv6NeighborSolicitation)
if payload != nil {
msg := &SNeighborSolicitation{
SBaseICMP6Message: baseMsg,
}
return msg, nil
} else {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Neighbor Solicitation packet")
}
case 136:
payload := packet.Layer(layers.LayerTypeICMPv6NeighborAdvertisement)
if payload != nil {
msg := &SNeighborAdvertisement{
SBaseICMP6Message: baseMsg,
}
return msg, nil
} else {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Neighbor Advertisement packet")
}
default:
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 packet")
}
} else {
return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect IPv6 packet")
}
}
}

246
pkg/util/icmp6/options.go Normal file
View File

@@ -0,0 +1,246 @@
// 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 icmp6
import (
"encoding/binary"
"net"
)
type ICMP6OptionType int
const (
ICMP6OptMTU ICMP6OptionType = 1
ICMP6OptPrefixInfo ICMP6OptionType = 2
ICMP6OptLinkLayerAddress ICMP6OptionType = 3
ICMP6OptRouteInfo ICMP6OptionType = 24
)
type ICMP6Option struct {
Type ICMP6OptionType
Data []byte
}
/*
https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.4
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MTU |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpV6OptMtu struct {
MTU uint32
}
func (opt icmpV6OptMtu) Bytes() []byte {
buf := make([]byte, 6)
binary.BigEndian.PutUint32(buf[2:], opt.MTU)
return buf
}
func NewIcmpV6OptMtu(mtu uint32) icmpV6OptMtu {
return icmpV6OptMtu{
MTU: mtu,
}
}
func DecodeIcmpV6OptMtu(data []byte) uint32 {
return binary.BigEndian.Uint32(data[2:])
}
/*
https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.2
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Prefix Length |L|A| Reserved1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Valid Lifetime |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Preferred Lifetime |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Prefix +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpv6OptPrefixInfo struct {
PrefixLen uint8
Flag uint8
ValidLifetime uint32
PreferredLifetime uint32
Reserved2 uint32
Prefix []byte
}
func (opt icmpv6OptPrefixInfo) Bytes() []byte {
buf := make([]byte, 30)
buf[0] = opt.PrefixLen
buf[1] = opt.Flag
binary.BigEndian.PutUint32(buf[2:], opt.ValidLifetime)
binary.BigEndian.PutUint32(buf[6:], opt.PreferredLifetime)
binary.BigEndian.PutUint32(buf[10:], opt.Reserved2)
copy(buf[14:], opt.Prefix[:])
return buf
}
func DecodeIcmpv6OptPrefixInfo(data []byte) SPrefixInfoOption {
rawInfo := icmpv6OptPrefixInfo{
PrefixLen: data[0],
Flag: data[1],
ValidLifetime: binary.BigEndian.Uint32(data[2:6]),
PreferredLifetime: binary.BigEndian.Uint32(data[6:10]),
Reserved2: binary.BigEndian.Uint32(data[10:14]),
Prefix: data[14:],
}
return SPrefixInfoOption{
IsOnlink: rawInfo.Flag&0x80 != 0,
IsAutoconf: rawInfo.Flag&0x40 != 0,
Prefix: net.IP(rawInfo.Prefix),
PrefixLen: rawInfo.PrefixLen,
ValidLifetime: rawInfo.ValidLifetime,
PreferredLifetime: rawInfo.PreferredLifetime,
}
}
func NewIcmpv6OptPrefixInfo(prefInfo SPrefixInfoOption) icmpv6OptPrefixInfo {
flag := uint8(0)
if prefInfo.IsOnlink {
flag |= 0x80
}
if prefInfo.IsAutoconf {
flag |= 0x40
}
return icmpv6OptPrefixInfo{
PrefixLen: prefInfo.PrefixLen,
Flag: flag,
ValidLifetime: prefInfo.ValidLifetime,
PreferredLifetime: prefInfo.PreferredLifetime,
Reserved2: 0,
Prefix: prefInfo.Prefix.To16(),
}
}
/*
https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.1
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Link-Layer Address ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpv6OptSourceTargetAddress struct {
Mac net.HardwareAddr
}
func (opt icmpv6OptSourceTargetAddress) Bytes() []byte {
return opt.Mac[0:6]
}
func DecodeIcmpv6OptSourceTargetAddress(data []byte) net.HardwareAddr {
return net.HardwareAddr(data[0:6])
}
func NewIcmpv6OptSourceTargetAddress(macAddr net.HardwareAddr) icmpv6OptSourceTargetAddress {
return icmpv6OptSourceTargetAddress{
Mac: macAddr,
}
}
/*
https://datatracker.ietf.org/doc/html/rfc4191#section-2.3
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | Prefix Length |Resvd|Prf|Resvd|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Route Lifetime |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Prefix (Variable Length) |
. .
. .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type icmpv6OptRouteInfo struct {
PrefixLen uint8
Reserved1 uint8
RouteLifetime uint32
Prefix []byte
}
func (opt icmpv6OptRouteInfo) Bytes() []byte {
buf := make([]byte, 6)
buf[0] = opt.PrefixLen
buf[1] = opt.Reserved1
binary.BigEndian.PutUint32(buf[2:6], opt.RouteLifetime)
buf = append(buf, opt.Prefix...)
return buf
}
func DecodeIcmpv6OptRouteInfo(data []byte) SRouteInfoOption {
rawInfo := icmpv6OptRouteInfo{
PrefixLen: data[0],
Reserved1: data[1],
RouteLifetime: binary.BigEndian.Uint32(data[2:6]),
Prefix: data[6:],
}
for i := len(rawInfo.Prefix); i < 16; i++ {
rawInfo.Prefix = append(rawInfo.Prefix, 0)
}
var preference TPreference
switch rawInfo.Reserved1 {
case 0xc0:
preference = PreferenceLow
case 0x0:
preference = PreferenceMedium
case 0x40:
preference = PreferenceHigh
}
return SRouteInfoOption{
RouteLifetime: rawInfo.RouteLifetime,
Prefix: net.IP(rawInfo.Prefix[:16]),
PrefixLen: rawInfo.PrefixLen,
Preference: preference,
}
}
func NewIcmpv6OptRouteInfo(routeInfo SRouteInfoOption) icmpv6OptRouteInfo {
prefix := routeInfo.Prefix.To16()
prefixLen := routeInfo.PrefixLen
routeLifetime := routeInfo.RouteLifetime
if prefixLen <= 0 {
prefix = prefix[0:0]
} else if prefixLen <= 64 {
prefix = prefix[0:8]
} else {
prefix = prefix[0:16]
}
return icmpv6OptRouteInfo{
PrefixLen: prefixLen,
Reserved1: 0,
RouteLifetime: routeLifetime,
Prefix: prefix,
}
}

View File

@@ -30,7 +30,6 @@ import (
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/procutils"
)
@@ -132,8 +131,8 @@ func Netlen2Mask(netmasklen int) string {
return netutils.Netlen2Mask(netmasklen)
}
func addRoute(routes []dhcp.SRouteInfo, net, gw string) []dhcp.SRouteInfo {
route, _ := dhcp.ParseRouteInfo([]string{net, gw})
func addRoute(routes []SRouteInfo, net, gw string) []SRouteInfo {
route, _ := ParseRouteInfo([]string{net, gw})
if route != nil {
for _, rt := range routes {
if rt.Prefix.String() == route.Prefix.String() && rt.PrefixLen == route.PrefixLen {
@@ -146,7 +145,7 @@ func addRoute(routes []dhcp.SRouteInfo, net, gw string) []dhcp.SRouteInfo {
return routes
}
func extendRoutes(routes4, routes6 []dhcp.SRouteInfo, nicRoutes []types.SRoute) ([]dhcp.SRouteInfo, []dhcp.SRouteInfo) {
func extendRoutes(routes4, routes6 []SRouteInfo, nicRoutes []types.SRoute) ([]SRouteInfo, []SRouteInfo) {
for i := 0; i < len(nicRoutes); i++ {
if regutils.MatchCIDR6(nicRoutes[i][0]) {
routes6 = addRoute(routes6, nicRoutes[i][0], nicRoutes[i][1])
@@ -166,7 +165,7 @@ func isExitAddress(ip string) bool {
return netutils.IsExitAddress(ipv4)
}
func AddNicRoutes(routes4 []dhcp.SRouteInfo, routes6 []dhcp.SRouteInfo, nicDesc *types.SServerNic, mainIp string, mainIp6 string, nicCnt int) ([]dhcp.SRouteInfo, []dhcp.SRouteInfo) {
func AddNicRoutes(routes4 []SRouteInfo, routes6 []SRouteInfo, nicDesc *types.SServerNic, mainIp string, mainIp6 string, nicCnt int) ([]SRouteInfo, []SRouteInfo) {
// always add static routes, even if this is the default NIC
// if mainIp == nicDesc.Ip {
// return routes

View File

@@ -63,9 +63,8 @@ func (n *SNetInterface) getAddresses(listFunc func() ([]net.IPNet, error)) []SNi
func (n *SNetInterface) GetRouteSpecs() []iproute2.RouteSpec {
routeList := iproute2.NewRoute(n.name)
routes4 := getRouteSpecs(routeList.List4)
routes6 := getRouteSpecs(routeList.List6)
rets := make([]iproute2.RouteSpec, 0)
routes4 := getRouteSpecs(routeList.List4)
for i := range routes4 {
if routes4[i].Gw == nil {
continue
@@ -75,6 +74,7 @@ func (n *SNetInterface) GetRouteSpecs() []iproute2.RouteSpec {
}
rets = append(rets, routes4[i])
}
routes6 := getRouteSpecs(routeList.List6)
for i := range routes6 {
if routes6[i].Gw == nil {
continue

View File

@@ -15,6 +15,7 @@
package netutils2
import (
"os"
"testing"
"yunion.io/x/jsonutils"
@@ -182,3 +183,15 @@ func TestGetMainNicFromDeployApi(t *testing.T) {
}
}
}
func TestGetRouteSpecs(t *testing.T) {
dirs, _ := os.ReadDir("/sys/class/net")
for _, file := range dirs {
t.Logf("dir: %s", file.Name())
n := NewNetInterface(file.Name())
routes := n.GetRouteSpecs()
for i := range routes {
t.Logf("route %s via %s", routes[i].Dst.String(), routes[i].Gw.String())
}
}
}

View File

@@ -0,0 +1,59 @@
// 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 netutils2
import (
"fmt"
"net"
"yunion.io/x/pkg/errors"
)
type SPrefixInfo struct {
Prefix net.IP
PrefixLen uint8
}
func (p SPrefixInfo) String() string {
return fmt.Sprintf("%s/%d", p.Prefix.String(), p.PrefixLen)
}
type SRouteInfo struct {
SPrefixInfo
Gateway net.IP
}
func (r SRouteInfo) String() string {
return fmt.Sprintf("%s via %s", r.SPrefixInfo.String(), r.Gateway.String())
}
func ParseRouteInfo(route []string) (*SRouteInfo, error) {
if len(route) < 2 {
return nil, errors.Wrapf(errors.ErrInvalidStatus, "invalid route %#v", route)
}
_, prefixLen, err := net.ParseCIDR(route[0])
if err != nil {
return nil, errors.Wrapf(err, "net.ParseCIDR %s", route[0])
}
ones, _ := prefixLen.Mask.Size()
return &SRouteInfo{
SPrefixInfo: SPrefixInfo{
Prefix: prefixLen.IP,
PrefixLen: uint8(ones),
},
Gateway: net.ParseIP(route[1]),
}, nil
}

View File

@@ -0,0 +1,83 @@
// 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 netutils2
import (
"net"
"testing"
)
func TestParseRouteInfo(t *testing.T) {
cases := []struct {
net string
gw string
want *SRouteInfo
}{
{
net: "10.0.0.0/8",
gw: "10.168.120.1",
want: &SRouteInfo{
SPrefixInfo: SPrefixInfo{
Prefix: net.ParseIP("10.0.0.0"),
PrefixLen: 8,
},
Gateway: net.ParseIP("10.168.120.1"),
},
},
{
net: "::/0",
gw: "3ffe:3200:fe::1",
want: &SRouteInfo{
SPrefixInfo: SPrefixInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
},
Gateway: net.ParseIP("3ffe:3200:fe::1"),
},
},
{
net: "fd00:ec2::254/128",
gw: "::",
want: &SRouteInfo{
SPrefixInfo: SPrefixInfo{
Prefix: net.ParseIP("fd00:ec2::254"),
PrefixLen: 128,
},
Gateway: net.ParseIP("::"),
},
},
{
net: "fe80::a9fe:a9fe/128",
gw: "::",
want: &SRouteInfo{
SPrefixInfo: SPrefixInfo{
Prefix: net.ParseIP("fe80::a9fe:a9fe"),
PrefixLen: 128,
},
Gateway: net.ParseIP("::"),
},
},
}
for _, c := range cases {
routeInfo, err := ParseRouteInfo([]string{c.net, c.gw})
if err != nil {
t.Errorf("parse route %s error: %v", c.net, err)
continue
}
if routeInfo.String() != c.want.String() {
t.Errorf("parse route expect %s got: %s", c.want.String(), routeInfo.String())
}
}
}