diff --git a/pkg/baremetal/nic.go b/pkg/baremetal/nic.go index 9dd7af2534..d0fe121daa 100644 --- a/pkg/baremetal/nic.go +++ b/pkg/baremetal/nic.go @@ -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), }) } } diff --git a/pkg/hostman/guestfs/fsdriver/linux.go b/pkg/hostman/guestfs/fsdriver/linux.go index 38e4187b06..2ea7e3970d 100644 --- a/pkg/hostman/guestfs/fsdriver/linux.go +++ b/pkg/hostman/guestfs/fsdriver/linux.go @@ -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 { diff --git a/pkg/hostman/guestfs/fsdriver/netplan.go b/pkg/hostman/guestfs/fsdriver/netplan.go index 6b2ccd8f58..af3a8dadbe 100644 --- a/pkg/hostman/guestfs/fsdriver/netplan.go +++ b/pkg/hostman/guestfs/fsdriver/netplan.go @@ -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 diff --git a/pkg/hostman/guestfs/fsdriver/suse.go b/pkg/hostman/guestfs/fsdriver/suse.go index 8deb6944f0..f799faf79e 100644 --- a/pkg/hostman/guestfs/fsdriver/suse.go +++ b/pkg/hostman/guestfs/fsdriver/suse.go @@ -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 diff --git a/pkg/hostman/guestfs/fsdriver/windows.go b/pkg/hostman/guestfs/fsdriver/windows.go index 48d0c12dce..aa3a2e811b 100644 --- a/pkg/hostman/guestfs/fsdriver/windows.go +++ b/pkg/hostman/guestfs/fsdriver/windows.go @@ -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())) diff --git a/pkg/hostman/host_services.go b/pkg/hostman/host_services.go index 031b60e545..94a2c6b73c 100644 --- a/pkg/hostman/host_services.go +++ b/pkg/hostman/host_services.go @@ -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() { diff --git a/pkg/hostman/hostinfo/hostbridge/hostbridge.go b/pkg/hostman/hostinfo/hostbridge/hostbridge.go index b80f20c3d0..0b0f007f84 100644 --- a/pkg/hostman/hostinfo/hostbridge/hostbridge.go +++ b/pkg/hostman/hostinfo/hostbridge/hostbridge.go @@ -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 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 } } diff --git a/pkg/hostman/hostinfo/hostdhcp/dhcprelay6.go b/pkg/hostman/hostinfo/hostdhcp/dhcprelay6.go index c34791b959..016358101c 100644 --- a/pkg/hostman/hostinfo/hostdhcp/dhcprelay6.go +++ b/pkg/hostman/hostinfo/hostdhcp/dhcprelay6.go @@ -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) { diff --git a/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go b/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go index 34cfbdf5ce..701be2f663 100644 --- a/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go +++ b/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go @@ -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), }) } } diff --git a/pkg/hostman/hostinfo/hostdhcp/dhcpserver6.go b/pkg/hostman/hostinfo/hostdhcp/dhcpserver6.go index fd4ed79a8f..8ec7b94b32 100644 --- a/pkg/hostman/hostinfo/hostdhcp/dhcpserver6.go +++ b/pkg/hostman/hostinfo/hostdhcp/dhcpserver6.go @@ -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) { diff --git a/pkg/hostman/hostinfo/hostinfohelper.go b/pkg/hostman/hostinfo/hostinfohelper.go index 9856b6e3ed..643dc89d90 100644 --- a/pkg/hostman/hostinfo/hostinfohelper.go +++ b/pkg/hostman/hostinfo/hostinfohelper.go @@ -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) } diff --git a/pkg/util/dhcp/conn.go b/pkg/util/dhcp/conn.go index 40d3e5b2c3..d9a117de8e 100644 --- a/pkg/util/dhcp/conn.go +++ b/pkg/util/dhcp/conn.go @@ -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 } } diff --git a/pkg/util/dhcp/conn6.go b/pkg/util/dhcp/conn6.go index 2d344b7f64..84f736f351 100644 --- a/pkg/util/dhcp/conn6.go +++ b/pkg/util/dhcp/conn6.go @@ -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") } diff --git a/pkg/util/dhcp/conn_linux.go b/pkg/util/dhcp/conn_linux.go index a29e5ff3e3..54b6e59843 100644 --- a/pkg/util/dhcp/conn_linux.go +++ b/pkg/util/dhcp/conn_linux.go @@ -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") } } diff --git a/pkg/util/dhcp/conn_linux6.go b/pkg/util/dhcp/conn_linux6.go index 3fe871bba4..92b21ea7ce 100644 --- a/pkg/util/dhcp/conn_linux6.go +++ b/pkg/util/dhcp/conn_linux6.go @@ -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 diff --git a/pkg/util/dhcp/helpers.go b/pkg/util/dhcp/helpers.go index 48245c79c5..f68ec32c2d 100644 --- a/pkg/util/dhcp/helpers.go +++ b/pkg/util/dhcp/helpers.go @@ -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 diff --git a/pkg/util/dhcp/helpers_test.go b/pkg/util/dhcp/helpers_test.go index 294d8c3f1f..4806d583f7 100644 --- a/pkg/util/dhcp/helpers_test.go +++ b/pkg/util/dhcp/helpers_test.go @@ -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()) - } - } -} diff --git a/pkg/util/dhcp/interface.go b/pkg/util/dhcp/interface.go index 8695000b53..1678a1b8b5 100644 --- a/pkg/util/dhcp/interface.go +++ b/pkg/util/dhcp/interface.go @@ -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 } diff --git a/pkg/util/dhcp/packet6.go b/pkg/util/dhcp/packet6.go index 67c8fa7fa5..450511b026 100644 --- a/pkg/util/dhcp/packet6.go +++ b/pkg/util/dhcp/packet6.go @@ -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 diff --git a/pkg/util/dhcp/server.go b/pkg/util/dhcp/server.go index bff000c883..1544279a5b 100644 --- a/pkg/util/dhcp/server.go +++ b/pkg/util/dhcp/server.go @@ -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 } diff --git a/pkg/util/icmp6/doc.go b/pkg/util/icmp6/doc.go new file mode 100644 index 0000000000..46dae62355 --- /dev/null +++ b/pkg/util/icmp6/doc.go @@ -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" diff --git a/pkg/util/icmp6/messages.go b/pkg/util/icmp6/messages.go new file mode 100644 index 0000000000..7e3c589074 --- /dev/null +++ b/pkg/util/icmp6/messages.go @@ -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") + } + } +} diff --git a/pkg/util/icmp6/options.go b/pkg/util/icmp6/options.go new file mode 100644 index 0000000000..5ee6fa294e --- /dev/null +++ b/pkg/util/icmp6/options.go @@ -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, + } +} diff --git a/pkg/util/netutils2/netutils.go b/pkg/util/netutils2/netutils.go index d680046649..9c589f45cd 100644 --- a/pkg/util/netutils2/netutils.go +++ b/pkg/util/netutils2/netutils.go @@ -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 diff --git a/pkg/util/netutils2/netutils_linux.go b/pkg/util/netutils2/netutils_linux.go index 106c261747..643539948b 100644 --- a/pkg/util/netutils2/netutils_linux.go +++ b/pkg/util/netutils2/netutils_linux.go @@ -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 diff --git a/pkg/util/netutils2/netutils_test.go b/pkg/util/netutils2/netutils_test.go index 5baf8674c2..223bb91dc0 100644 --- a/pkg/util/netutils2/netutils_test.go +++ b/pkg/util/netutils2/netutils_test.go @@ -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()) + } + } +} diff --git a/pkg/util/netutils2/routeinfo.go b/pkg/util/netutils2/routeinfo.go new file mode 100644 index 0000000000..9b0c4ce75c --- /dev/null +++ b/pkg/util/netutils2/routeinfo.go @@ -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 +} diff --git a/pkg/util/netutils2/routeinfo_test.go b/pkg/util/netutils2/routeinfo_test.go new file mode 100644 index 0000000000..8f968d3fb6 --- /dev/null +++ b/pkg/util/netutils2/routeinfo_test.go @@ -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()) + } + } +}