fix: dhcpv6 support, phase 2 (#22852)

Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
Jian Qiu
2025-07-09 14:41:34 +08:00
committed by GitHub
co-authored by Qiu Jian
parent 570b371290
commit 992c5f498e
21 changed files with 1061 additions and 244 deletions
+26 -4
View File
@@ -1040,20 +1040,22 @@ func (b *SBaremetalInstance) GetIPMINicIPAddr() string {
func (b *SBaremetalInstance) GetDHCPConfig(cliMac net.HardwareAddr) (*dhcp.ResponseConfig, error) {
var nic *types.SNic
var hostname string
var osName string
if b.GetServer() != nil && (b.GetTask() == nil || !b.GetTask().NeedPXEBoot()) {
nic = b.GetServer().GetNicByMac(cliMac)
hostname = b.GetServer().GetName()
hostname = b.GetServer().GetHostName()
osName = b.GetServer().GetOsName()
} else {
nic = b.GetNicByMac(cliMac)
}
if nic == nil {
return nil, fmt.Errorf("GetNicDHCPConfig no nic found by mac: %s", cliMac)
}
return b.getDHCPConfig(nic, hostname, false, 0)
return b.getDHCPConfig(nic, hostname, false, 0, osName)
}
func (b *SBaremetalInstance) GetPXEDHCPConfig(arch uint16) (*dhcp.ResponseConfig, error) {
return b.getDHCPConfig(b.GetAdminNic(), "", true, arch)
return b.getDHCPConfig(b.GetAdminNic(), "", true, arch, "Linux")
}
func (b *SBaremetalInstance) getDHCPConfig(
@@ -1061,10 +1063,14 @@ func (b *SBaremetalInstance) getDHCPConfig(
hostName string,
isPxe bool,
arch uint16,
osName string,
) (*dhcp.ResponseConfig, error) {
if hostName == "" {
hostName = b.GetName()
}
if osName == "" {
osName = "Linux"
}
serverIP, err := b.manager.Agent.GetDHCPServerIP()
if err != nil {
return nil, err
@@ -1078,7 +1084,7 @@ func (b *SBaremetalInstance) getDHCPConfig(
return nil, errors.Errorf("Baremetal %s not need UEFI PXE boot", b.GetName())
}
}
return GetNicDHCPConfig(nic, serverIP.String(), hostName, isPxe, arch)
return GetNicDHCPConfig(nic, serverIP, b.manager.Agent.ListenInterface.HardwareAddr, hostName, isPxe, arch, osName)
}
func (b *SBaremetalInstance) GetNotifyUrl() string {
@@ -2550,6 +2556,22 @@ func (server *SBaremetalServer) GetName() string {
return id
}
func (server *SBaremetalServer) GetHostName() string {
hostname, err := server.desc.GetString("hostname")
if err != nil {
log.Errorf("Get hostname from desc %s error: %v", server.desc.String(), err)
}
return hostname
}
func (server *SBaremetalServer) GetOsName() string {
osName, err := server.desc.GetString("os_name")
if err != nil {
log.Errorf("Get os name from desc %s error: %v", server.desc.String(), err)
}
return osName
}
func (server *SBaremetalServer) SaveDesc(desc jsonutils.JSONObject) error {
if desc != nil {
server.desc = desc.(*jsonutils.JSONDict)
+104 -44
View File
@@ -15,14 +15,15 @@
package baremetal
import (
"fmt"
"net"
"os"
"path/filepath"
"strings"
"time"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
@@ -31,68 +32,127 @@ import (
func GetNicDHCPConfig(
n *types.SNic,
serverIP string,
serverIP net.IP,
serverMac net.HardwareAddr,
hostName string,
isPxe bool,
arch uint16,
osName string,
) (*dhcp.ResponseConfig, error) {
if n == nil {
return nil, fmt.Errorf("Nic is nil")
return nil, errors.Wrap(errors.ErrEmpty, "Nic is nil")
}
if n.IpAddr == "" {
return nil, fmt.Errorf("Nic no ip address")
}
ipAddr, err := netutils.NewIPV4Addr(n.IpAddr)
if err != nil {
return nil, fmt.Errorf("Parse IP address error: %q", n.IpAddr)
if n.IpAddr == "" && n.Ip6Addr == "" {
return nil, errors.Wrap(errors.ErrEmpty, "Nic no ip or ip6 address")
}
subnetMask := net.ParseIP(netutils.Masklen2Mask(n.Masklen).String())
routes := make([][]string, 0)
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.SRouteInfo, 0)
for _, route := range n.Routes {
routes = append(routes, []string{route[0], route[1]})
routeInfo, err := dhcp.ParseRouteInfo([]string{route[0], route[1]})
if err != nil {
return nil, errors.Wrapf(err, "Parse route %s error: %q", route, err)
}
if regutils.MatchCIDR6(route[0]) {
routes6 = append(routes6, *routeInfo)
} else {
routes4 = append(routes4, *routeInfo)
}
}
parseIPs := func(ips string) []net.IP {
ret := make([]net.IP, 0)
iplist := strings.Split(ips, ",")
for _, ip := range iplist {
ret = append(ret, net.ParseIP(ip))
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),
})
}
return ret
}
parseDomains := func(domains string) []net.IP {
ret := make([]net.IP, 0)
domainlist := strings.Split(domains, ",")
for _, domain := range domainlist {
addrs, _ := net.LookupHost(domain)
for _, addr := range addrs {
ret = append(ret, net.ParseIP(addr))
}
if n.Gateway6 != "" {
routes6 = append(routes6, dhcp.SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP(n.Gateway6),
})
}
return ret
}
conf := &dhcp.ResponseConfig{
ServerIP: net.ParseIP(serverIP),
ClientIP: net.ParseIP(ipAddr.String()),
Gateway: net.ParseIP(n.Gateway),
SubnetMask: subnetMask,
BroadcastAddr: net.ParseIP(ipAddr.BroadcastAddr(n.Masklen).String()),
DNSServers: parseIPs(n.Dns),
NTPServers: parseDomains(n.Ntp),
Domain: n.Domain,
OsName: "Linux",
Hostname: hostName,
Routes: routes,
LeaseTime: time.Duration(o.Options.DhcpLeaseTime) * time.Second,
RenewalTime: time.Duration(o.Options.DhcpRenewalTime) * time.Second,
InterfaceMac: serverMac,
ServerIP: serverIP,
Domain: n.Domain,
OsName: osName,
Hostname: strings.ToLower(hostName),
Routes: routes4,
Routes6: routes6,
LeaseTime: time.Duration(o.Options.DhcpLeaseTime) * time.Second,
RenewalTime: time.Duration(o.Options.DhcpRenewalTime) * time.Second,
}
if n.IpAddr != "" {
ipAddr, err := netutils.NewIPV4Addr(n.IpAddr)
if err != nil {
return nil, errors.Wrapf(err, "Parse IP address error: %q", n.IpAddr)
}
subnetMask := net.ParseIP(netutils.Masklen2Mask(n.Masklen).String())
conf.ClientIP = net.ParseIP(ipAddr.String())
conf.SubnetMask = subnetMask
conf.BroadcastAddr = net.ParseIP(ipAddr.BroadcastAddr(n.Masklen).String())
conf.Gateway = net.ParseIP(n.Gateway)
}
if n.Ip6Addr != "" {
ipAddr6, err := netutils.NewIPV6Addr(n.Ip6Addr)
if err != nil {
return nil, errors.Wrapf(err, "Parse IPv6 address error: %q", n.Ip6Addr)
}
conf.ClientIP6 = net.ParseIP(ipAddr6.String())
conf.PrefixLen6 = n.Masklen6
conf.Gateway6 = net.ParseIP(n.Gateway6)
}
if len(n.Dns) > 0 {
conf.DNSServers = make([]net.IP, 0)
conf.DNSServers6 = make([]net.IP, 0)
for _, dns := range strings.Split(n.Dns, ",") {
if regutils.MatchIP4Addr(dns) {
conf.DNSServers = append(conf.DNSServers, net.ParseIP(dns))
} else if regutils.MatchIP6Addr(dns) {
conf.DNSServers6 = append(conf.DNSServers6, net.ParseIP(dns))
}
}
}
if len(n.Ntp) > 0 {
conf.NTPServers = make([]net.IP, 0)
conf.NTPServers6 = make([]net.IP, 0)
for _, ntp := range strings.Split(n.Ntp, ",") {
if regutils.MatchIP4Addr(ntp) {
conf.NTPServers = append(conf.NTPServers, net.ParseIP(ntp))
} else if regutils.MatchIP6Addr(ntp) {
conf.NTPServers6 = append(conf.NTPServers6, net.ParseIP(ntp))
} else if regutils.MatchDomainName(ntp) {
ntpAddrs, _ := net.LookupHost(ntp)
for _, ntpAddr := range ntpAddrs {
if regutils.MatchIP4Addr(ntpAddr) {
conf.NTPServers = append(conf.NTPServers, net.ParseIP(ntpAddr))
} else if regutils.MatchIP6Addr(ntpAddr) {
conf.NTPServers6 = append(conf.NTPServers6, net.ParseIP(ntpAddr))
}
}
}
}
}
if n.Mtu > 0 {
conf.MTU = uint16(n.Mtu)
}
if isPxe {
conf.BootServer = serverIP
conf.BootServer = serverIP.String()
if len(o.Options.TftpBootServer) > 0 {
conf.BootServer = o.Options.TftpBootServer
}
+3
View File
@@ -41,4 +41,7 @@ type IBaremetalServer interface {
GetNicByMac(mac net.HardwareAddr) *types.SNic
GetRootTemplateId() string
GetHostName() string
GetOsName() string
}
+4
View File
@@ -52,6 +52,8 @@ type SNic struct {
Bandwidth int `json:"bandwidth"`
Index int `json:"index"`
IsDefault *bool `json:"is_default"`
}
type SRoute []string
@@ -132,5 +134,7 @@ func (n SServerNic) ToNic() SNic {
Ip6Addr: n.Ip6,
Masklen6: uint8(n.Masklen6),
Gateway6: n.Gateway6,
IsDefault: &n.IsDefault,
}
}
+57 -24
View File
@@ -38,6 +38,7 @@ 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"
@@ -930,13 +931,18 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
if mainNic != nil {
mainIp = mainNic.Ip
}
mainNic6 := getMainNic6(allNics)
var mainIp6 string
if mainNic6 != nil {
mainIp6 = mainNic6.Ip6
}
netplanDir := "/etc/netplan"
if rootFs.Exists(netplanDir, false) {
for _, f := range rootFs.ListDir(netplanDir, false) {
rootFs.Remove(filepath.Join(netplanDir, f), false)
}
netplanConfig := NewNetplanConfig(allNics, bondNics, mainIp)
netplanConfig := NewNetplanConfig(allNics, bondNics, mainIp, mainIp6)
log.Debugf("netplanConfig:\n %s", netplanConfig.YAMLString())
if err := rootFs.FilePutContents(path.Join(netplanDir, "config.yaml"), netplanConfig.YAMLString(), false, false); err != nil {
return errors.Wrap(err, "Put netplan config")
@@ -978,11 +984,16 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
if nicDesc.Mtu > 0 {
cmds.WriteString(fmt.Sprintf(" mtu %d\n", nicDesc.Mtu))
}
var routes = make([][]string, 0)
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
for _, r := range routes {
cmds.WriteString(fmt.Sprintf(" up route add -net %s gw %s || true\n", r[0], r[1]))
cmds.WriteString(fmt.Sprintf(" down route del -net %s gw %s || true\n", r[0], r[1]))
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.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))
cmds.WriteString(fmt.Sprintf(" down ip route del %s/%d via %s || true\n", r.Prefix, r.PrefixLen, r.Gateway))
}
for _, r := range routes6 {
cmds.WriteString(fmt.Sprintf(" up ip -6 route add %s/%d via %s || true\n", r.Prefix, r.PrefixLen, r.Gateway))
cmds.WriteString(fmt.Sprintf(" down ip -6 route del %s/%d via %s || true\n", r.Prefix, r.PrefixLen, r.Gateway))
}
dnslist := netutils2.GetNicDns(nicDesc)
if len(dnslist) > 0 {
@@ -1326,7 +1337,21 @@ func getMainNic(nics []*types.SServerNic) *types.SServerNic {
}
}
for i := range nics {
if len(nics[i].Gateway) > 0 {
if len(nics[i].Ip) > 0 && len(nics[i].Gateway) > 0 {
return nics[i]
}
}
return nil
}
func getMainNic6(nics []*types.SServerNic) *types.SServerNic {
for i := range nics {
if nics[i].IsDefault {
return nics[i]
}
}
for i := range nics {
if len(nics[i].Ip6) > 0 && len(nics[i].Gateway6) > 0 {
return nics[i]
}
}
@@ -1386,6 +1411,12 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
if mainNic != nil {
mainIp = mainNic.Ip
}
mainNic6 := getMainNic6(allNics)
var mainIp6 string
if mainNic6 != nil {
mainIp6 = mainNic6.Ip6
}
for i := range allNics {
nicDesc := allNics[i]
var cmds strings.Builder
@@ -1436,7 +1467,7 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
} else if nicDesc.Manual {
cmds.WriteString("BOOTPROTO=none\n")
if nicDesc.VlanInterface {
if err := r.deployVlanNetworkingScripts(rootFs, scriptPath, mainIp, nicCnt, nicDesc); err != nil {
if err := r.deployVlanNetworkingScripts(rootFs, scriptPath, mainIp, mainIp6, nicCnt, nicDesc); err != nil {
return errors.Wrap(err, "deployVlanNetworkingScripts")
}
} else {
@@ -1452,13 +1483,14 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
cmds.WriteString(nicDesc.Gateway)
cmds.WriteString("\n")
}
var routes = make([][]string, 0)
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.SRouteInfo, 0)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
for _, r := range routes4 {
rtbl.WriteString(fmt.Sprintf("%s/%d", r.Prefix, r.PrefixLen))
rtbl.WriteString(" via ")
rtbl.WriteString(r[1])
rtbl.WriteString(r.Gateway.String())
rtbl.WriteString(" dev ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
@@ -1496,12 +1528,12 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
// IPv6 support static temporarily
// TODO
cmds.WriteString("IPV6INIT=yes\n")
cmds.WriteString("DHCPV6C=no\n")
cmds.WriteString("DHCPV6C=yes\n")
cmds.WriteString("IPV6_AUTOCONF=no\n")
cmds.WriteString(fmt.Sprintf("IPV6ADDR=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
if len(nicDesc.Gateway6) > 0 {
cmds.WriteString(fmt.Sprintf("IPV6_DEFAULTGW=%s\n", nicDesc.Gateway6))
}
// cmds.WriteString(fmt.Sprintf("IPV6ADDR=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
// if len(nicDesc.Gateway6) > 0 {
// cmds.WriteString("IPV6_DEFROUTE=yes\n", nicDesc.Gateway6))
// }
}
}
var fn = fmt.Sprintf("%s/ifcfg-%s", scriptPath, nicDesc.Name)
@@ -1513,7 +1545,7 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
return nil
}
func (r *sRedhatLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, scriptPath, mainIp string, nicCnt int, nicDesc *types.SServerNic) error {
func (r *sRedhatLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, scriptPath, mainIp, mainIp6 string, nicCnt int, nicDesc *types.SServerNic) error {
if nicDesc.Vlan <= 1 {
return nil
}
@@ -1544,13 +1576,14 @@ func (r *sRedhatLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, s
cmds.WriteString(nicDesc.Gateway)
cmds.WriteString("\n")
}
var routes = make([][]string, 0)
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
routes4 := make([]dhcp.SRouteInfo, 0)
routes6 := make([]dhcp.SRouteInfo, 0)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
for _, r := range routes4 {
rtbl.WriteString(fmt.Sprintf("%s/%d", r.Prefix, r.PrefixLen))
rtbl.WriteString(" via ")
rtbl.WriteString(r[1])
rtbl.WriteString(r.Gateway.String())
rtbl.WriteString(" dev ")
rtbl.WriteString(fmt.Sprintf("%s.%d", nicDesc.Name, nicDesc.Vlan))
rtbl.WriteString("\n")
+20 -12
View File
@@ -20,21 +20,22 @@ 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"
)
func NewNetplanConfig(allNics []*types.SServerNic, bondNics []*types.SServerNic, mainIp string) *netplan.Configuration {
network := newNetplanNetwork(allNics, bondNics, mainIp)
func NewNetplanConfig(allNics []*types.SServerNic, bondNics []*types.SServerNic, mainIp, mainIp6 string) *netplan.Configuration {
network := newNetplanNetwork(allNics, bondNics, mainIp, mainIp6)
return netplan.NewConfiguration(network)
}
func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic, mainIp string) *netplan.Network {
func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic, mainIp, mainIp6 string) *netplan.Network {
network := netplan.NewNetwork()
nicCnt := len(allNics) - len(bondNics)
for _, nic := range allNics {
nicConf := getNetplanEthernetConfig(nic, false, mainIp, nicCnt)
nicConf := getNetplanEthernetConfig(nic, false, mainIp, mainIp6, nicCnt)
if nicConf == nil {
continue
}
@@ -87,7 +88,7 @@ func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic
network.AddEthernet(sn.Name, nicConf)
}
netConf := getNetplanEthernetConfig(bondNic, true, mainIp, nicCnt)
netConf := getNetplanEthernetConfig(bondNic, true, mainIp, mainIp6, nicCnt)
if netConf.Mtu == 0 {
netConf.Mtu = defaultMtu
@@ -102,7 +103,7 @@ func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic
return network
}
func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool, mainIp string, nicCnt int) *netplan.EthernetConfig {
func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool, mainIp, mainIp6 string, nicCnt int) *netplan.EthernetConfig {
var nicConf *netplan.EthernetConfig
if !isBond && (nic.TeamingMaster != nil || nic.TeamingSlaves != nil) {
@@ -120,20 +121,27 @@ func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool, mainIp string,
gateway6 := ""
if len(nic.Ip6) > 0 {
addr6 = fmt.Sprintf("%s/%d", nic.Ip6, nic.Masklen6)
if nic.Ip == mainIp && len(mainIp) > 0 {
if nic.Ip6 == mainIp6 && len(mainIp6) > 0 {
gateway6 = nic.Gateway6
}
}
var routeArrs = make([][]string, 0)
routeArrs = netutils2.AddNicRoutes(routeArrs, nic, mainIp, nicCnt)
routeArrs4 := make([]dhcp.SRouteInfo, 0)
routeArrs6 := make([]dhcp.SRouteInfo, 0)
routeArrs4, routeArrs6 = netutils2.AddNicRoutes(routeArrs4, routeArrs6, nic, mainIp, mainIp6, nicCnt)
var routes []*netplan.Route
for _, route := range routeArrs {
for _, route := range routeArrs4 {
routes = append(routes, &netplan.Route{
To: route[0],
Via: route[1],
To: fmt.Sprintf("%s/%d", route.Prefix, route.PrefixLen),
Via: route.Gateway.String(),
})
}
for _, route := range routeArrs6 {
routes = append(routes, &netplan.Route{
To: fmt.Sprintf("%s/%d", route.Prefix, route.PrefixLen),
Via: route.Gateway.String(),
})
}
+131 -1
View File
@@ -28,6 +28,8 @@ func TestNewNetplanConfig(t *testing.T) {
mainIp string
nics []*types.SServerNic
want *netplan.Configuration
mainIp6 string
}{
{
mainIp: "10.168.222.175",
@@ -254,10 +256,138 @@ func TestNewNetplanConfig(t *testing.T) {
},
},
},
{
mainIp: "10.168.22.175",
mainIp6: "2001:db8::23",
nics: []*types.SServerNic{
&types.SServerNic{
Name: "eth0",
Index: 0,
Bridge: "br0",
Domain: "cloud.local",
Ip: "10.168.222.175",
Vlan: 1,
Driver: "virtio",
Masklen: 24,
Virtual: false,
Manual: true,
WireId: "399a06f3-7925-46c1-8b9f-d5a8580a74df",
NetId: "22c93412-5882-4de0-8357-45ce647ceada",
Mac: "00:24:c7:16:80:f2",
BandWidth: 1000,
Mtu: 1500,
Dns: "8.8.8.8",
Ntp: "",
Net: "vnet222",
Interface: "ens5",
Gateway: "10.168.222.1",
Ifname: "vnet222-175",
Routes: nil,
LinkUp: true,
Ip6: "2001:db8::23",
Masklen6: 64,
Gateway6: "2001:db8::1",
},
&types.SServerNic{
Name: "eth1",
Index: 1,
Bridge: "br0",
Domain: "cloud.local",
Ip: "",
Vlan: 1,
Driver: "virtio",
Masklen: 0,
Virtual: true,
Manual: true,
WireId: "399a06f3-7925-46c1-8b9f-d5a8580a74df",
NetId: "22c93412-5882-4de0-8357-45ce647ceada",
Mac: "00:24:c7:16:80:f3",
BandWidth: 1000,
Mtu: 1500,
Dns: "8.8.8.8",
Ntp: "",
Net: "vnet222",
Interface: "ens5",
Gateway: "",
Ifname: "vnet222-bpg",
Routes: nil,
LinkUp: true,
TeamWith: "00:24:c7:16:80:f2",
},
},
want: &netplan.Configuration{
Network: &netplan.Network{
Version: 2,
Renderer: netplan.NetworkRendererNetworkd,
Ethernets: map[string]*netplan.EthernetConfig{
"eth0": &netplan.EthernetConfig{
MacAddress: "00:24:c7:16:80:f2",
Match: &netplan.EthernetConfigMatch{
MacAddress: "00:24:c7:16:80:f2",
},
Mtu: 1500,
},
"eth1": &netplan.EthernetConfig{
MacAddress: "00:24:c7:16:80:f3",
Match: &netplan.EthernetConfigMatch{
MacAddress: "00:24:c7:16:80:f3",
},
Mtu: 1500,
},
},
Bonds: map[string]*netplan.Bond{
"bond0": &netplan.Bond{
EthernetConfig: netplan.EthernetConfig{
Addresses: []string{
"10.168.222.175/24",
"2001:db8::23/64",
},
MacAddress: "00:24:c7:16:80:f2",
Gateway6: "2001:db8::1",
Nameservers: &netplan.Nameservers{
Search: []string{
"cloud.local",
},
Addresses: []string{
"8.8.8.8",
},
},
Mtu: 1500,
Routes: []*netplan.Route{
{
Metric: 0,
To: "fd00:ec2::254/128",
Via: "::",
},
{
Metric: 0,
To: "fe80::a9fe:a9fe/128",
Via: "::",
},
},
},
Interfaces: []string{
"eth0",
"eth1",
},
Parameters: &netplan.BondMode4Params{
BondModeBaseParams: &netplan.BondModeBaseParams{
Mode: "802.3ad",
MiiMonitorInterval: 100,
},
},
},
},
},
},
},
}
for i, c := range cases {
allNics, bondNics := convertNicConfigs(c.nics)
netplanConfig := NewNetplanConfig(allNics, bondNics, c.mainIp)
netplanConfig := NewNetplanConfig(allNics, bondNics, c.mainIp, c.mainIp6)
if jsonutils.Marshal(netplanConfig).String() != jsonutils.Marshal(c.want).String() {
t.Errorf("nics %d: %s want: %s got: %s", i, jsonutils.Marshal(c.nics), jsonutils.Marshal(c.want).PrettyString(), jsonutils.Marshal(netplanConfig).PrettyString())
}
+56 -25
View File
@@ -16,6 +16,7 @@ package fsdriver
import (
"fmt"
"net"
"path"
"strings"
@@ -26,6 +27,7 @@ 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"
)
@@ -85,7 +87,7 @@ func (r *sSuseLikeRootFs) enableBondingModule(rootFs IDiskPartition, bondNics []
return rootFs.FilePutContents("/etc/modprobe.d/bonding.conf", content.String(), false, false)
}
func (r *sSuseLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, scriptPath, mainIp string, nicCnt int, nicDesc *types.SServerNic) error {
func (r *sSuseLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, scriptPath, mainIp, mainIp6 string, nicCnt int, nicDesc *types.SServerNic) error {
var cmds strings.Builder
var ifname = fmt.Sprintf("%s.%d", nicDesc.Name, nicDesc.Vlan)
cmds.WriteString("STARTMODE=auto\n")
@@ -104,26 +106,37 @@ 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 routes = make([][]string, 0)
var routes4 = make([]dhcp.SRouteInfo, 0)
var routes6 = make([]dhcp.SRouteInfo, 0)
var dnsSrv []string
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
routes = append(routes, []string{
"default",
nicDesc.Gateway,
routes4 = append(routes4, dhcp.SRouteInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway),
})
}
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip == mainIp {
routes = append(routes, []string{
"default",
nicDesc.Gateway6,
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip6 == mainIp6 {
routes6 = append(routes6, dhcp.SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway6),
})
}
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
for _, r := range routes4 {
rtbl.WriteString(fmt.Sprintf("%s/%d", r.Prefix, r.PrefixLen))
rtbl.WriteString(" ")
rtbl.WriteString(r[1])
rtbl.WriteString(r.Gateway.String())
rtbl.WriteString(" - ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
}
for _, r := range routes6 {
rtbl.WriteString(fmt.Sprintf("%s/%d", r.Prefix, r.PrefixLen))
rtbl.WriteString(" ")
rtbl.WriteString(r.Gateway.String())
rtbl.WriteString(" - ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
@@ -169,11 +182,18 @@ func (r *sSuseLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics []
nicCnt := len(allNics) - len(bondNics)
var dnsSrv []string
mainNic := getMainNic(allNics)
var mainIp string
if mainNic != nil {
mainIp = mainNic.Ip
}
mainNic6 := getMainNic6(allNics)
var mainIp6 string
if mainNic6 != nil {
mainIp6 = mainNic6.Ip6
}
for i := range allNics {
nicDesc := allNics[i]
var cmds strings.Builder
@@ -212,25 +232,36 @@ func (r *sSuseLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics []
cmds.WriteString(fmt.Sprintf("IPADDR_V6=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
}
var routes = make([][]string, 0)
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
var routes4 = make([]dhcp.SRouteInfo, 0)
var routes6 = make([]dhcp.SRouteInfo, 0)
routes4, routes6 = netutils2.AddNicRoutes(routes4, routes6, nicDesc, mainIp, mainIp6, nicCnt)
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
routes = append(routes, []string{
"default",
nicDesc.Gateway,
routes4 = append(routes4, dhcp.SRouteInfo{
Prefix: net.ParseIP("0.0.0.0"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway),
})
}
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip == mainIp {
routes = append(routes, []string{
"default",
nicDesc.Gateway6,
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip6 == mainIp6 {
routes6 = append(routes6, dhcp.SRouteInfo{
Prefix: net.ParseIP("::"),
PrefixLen: 0,
Gateway: net.ParseIP(nicDesc.Gateway6),
})
}
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
for _, r := range routes4 {
rtbl.WriteString(fmt.Sprintf("%s/%d", r.Prefix, r.PrefixLen))
rtbl.WriteString(" ")
rtbl.WriteString(r[1])
rtbl.WriteString(r.Gateway.String())
rtbl.WriteString(" - ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
}
for _, r := range routes6 {
rtbl.WriteString(fmt.Sprintf("%s/%d", r.Prefix, r.PrefixLen))
rtbl.WriteString(" ")
rtbl.WriteString(r.Gateway.String())
rtbl.WriteString(" - ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
+14 -5
View File
@@ -31,6 +31,7 @@ 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"
@@ -288,6 +289,10 @@ func (w *SWindowsRootFs) DeployNetworkingScripts(rootfs IDiskPartition, nics []*
if mainNic != nil {
mainIp = mainNic.Ip
}
mainIp6 := ""
if mainNic != nil {
mainIp6 = mainNic.Ip6
}
bootScript := strings.Join([]string{
`set NETCFG_SCRIPT=%SystemRoot%\netcfg.bat`,
`if exist %NETCFG_SCRIPT% (`,
@@ -322,15 +327,19 @@ func (w *SWindowsRootFs) DeployNetworkingScripts(rootfs IDiskPartition, nics []*
if len(snic.Ip6) > 0 {
cfg := fmt.Sprintf(` netsh interface ipv6 add address "%%%%b" %s/%d store=persistent`, snic.Ip6, snic.Masklen6)
lines = append(lines, cfg)
if len(snic.Gateway) > 0 && snic.Ip == mainIp {
if len(snic.Gateway6) > 0 && snic.Ip6 == mainIp6 {
cfg := fmt.Sprintf(` netsh interface ipv6 add route ::/0 "%%%%b" %s`, snic.Gateway6)
lines = append(lines, cfg)
}
}
routes := [][]string{}
routes = netutils2.AddNicRoutes(routes, snic, mainIp, len(nics))
for _, r := range routes {
lines = append(lines, fmt.Sprintf(` netsh interface ip add route %s "%%%%b" %s`, r[0], r[1]))
routes4 := []dhcp.SRouteInfo{}
routes6 := []dhcp.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()))
}
for _, r := range routes6 {
lines = append(lines, fmt.Sprintf(` netsh interface ipv6 add route %s/%d %s "%%%%b"`, r.Prefix, r.PrefixLen, r.Gateway.String()))
}
dnslist := netutils2.GetNicDns(snic)
if len(dnslist) > 0 {
+2 -2
View File
@@ -70,9 +70,9 @@ 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, error) {
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, err
return pkg, nil, nil, err
}
func (r *SDHCP6Relay) serveDHCPInternal(pkt dhcp.Packet, _ *net.UDPAddr) (dhcp.Packet, error) {
+95 -23
View File
@@ -20,7 +20,9 @@ import (
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
@@ -42,7 +44,7 @@ type SGuestDHCPServer struct {
relay *SDHCPRelay
conn *dhcp.Conn
iface string
ifaceDev *netutils2.SNetInterface
}
type SDHCPRelayUpstream struct {
@@ -56,19 +58,25 @@ func NewGuestDHCPServer(iface string, port int, relay *SDHCPRelayUpstream) (*SGu
guestdhcp = new(SGuestDHCPServer)
)
dev := netutils2.NewNetInterface(iface)
if dev.GetHardwareAddr() == nil {
return nil, errors.Wrapf(errors.ErrInvalidStatus, "iface %s no mac", iface)
}
guestdhcp.ifaceDev = dev
guestdhcp.server, guestdhcp.conn, err = dhcp.NewDHCPServer2(iface, DEFAULT_DHCP_SERVER_PORT)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "dhcp.NewDHCPServer2")
}
if relay != nil {
guestdhcp.relay, err = NewDHCPRelay(guestdhcp.conn, relay)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "NewDHCPRelay")
}
}
guestdhcp.iface = iface
return guestdhcp, nil
}
@@ -143,11 +151,31 @@ func GetMainNic(nics []*desc.SGuestNetwork) *desc.SGuestNetwork {
return n
}
}
for _, n := range nics {
if n.Ip != "" && n.Gateway != "" {
return n
}
}
return nil
}
func GetMainNic6(nics []*desc.SGuestNetwork) *desc.SGuestNetwork {
for _, n := range nics {
if n.IsDefault {
return n
}
}
for _, n := range nics {
if n.Ip6 != "" && n.Gateway6 != "" {
return n
}
}
return nil
}
func getGuestConfig(
guestDesc *desc.SGuestDesc, guestNic *desc.SGuestNetwork,
serverMac net.HardwareAddr,
) *dhcp.ResponseConfig {
var nicdesc = new(types.SServerNic)
if err := gusetnetworkJsonDescToServerNic(nicdesc, guestNic); err != nil {
@@ -156,6 +184,9 @@ func getGuestConfig(
}
var conf = new(dhcp.ResponseConfig)
conf.InterfaceMac = serverMac
nicIp := nicdesc.Ip
v4Ip, _ := netutils.NewIPV4Addr(nicIp)
conf.ClientIP = net.ParseIP(nicdesc.Ip)
@@ -174,46 +205,87 @@ func getGuestConfig(
if len(nicdesc.Ip6) > 0 {
// ipv6
conf.Gateway6 = net.ParseIP(nicdesc.Gateway6)
conf.PrefixLen6 = uint8(nicdesc.Masklen6)
conf.ClientIP6 = net.ParseIP(nicdesc.Ip6)
}
// get main ip
guestNics := guestDesc.Nics
manNic := GetMainNic(guestNics)
mainNic := GetMainNic(guestNics)
var mainIp string
if manNic != nil {
mainIp = manNic.Ip
if mainNic != nil {
mainIp = mainNic.Ip
}
mainNic6 := GetMainNic6(guestNics)
var mainIp6 string
if mainNic6 != nil {
mainIp6 = mainNic6.Ip6
}
var route = [][]string{}
route4 := make([]dhcp.SRouteInfo, 0)
route6 := make([]dhcp.SRouteInfo, 0)
if nicdesc.IsDefault {
conf.Gateway = net.ParseIP(nicdesc.Gateway)
osName := guestDesc.OsName
if len(osName) == 0 {
osName = "Linux"
}
if !strings.HasPrefix(strings.ToLower(osName), "win") {
route = append(route, []string{"0.0.0.0/0", nicdesc.Gateway})
if nicdesc.Gateway != "" {
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),
})
}
}
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),
})
}
route = append(route, []string{"169.254.169.254/32", "0.0.0.0"})
}
route = netutils2.AddNicRoutes(route, nicdesc, mainIp, len(guestNics))
conf.Routes = route
route4, route6 = netutils2.AddNicRoutes(route4, route6, nicdesc, mainIp, mainIp6, len(guestNics))
conf.Routes = route4
conf.Routes6 = route6
if len(nicdesc.Dns) > 0 {
conf.DNSServers = make([]net.IP, 0)
conf.DNSServers6 = make([]net.IP, 0)
for _, dns := range strings.Split(nicdesc.Dns, ",") {
conf.DNSServers = append(conf.DNSServers, net.ParseIP(dns))
if regutils.MatchIP4Addr(dns) {
conf.DNSServers = append(conf.DNSServers, net.ParseIP(dns))
} else if regutils.MatchIP6Addr(dns) {
conf.DNSServers6 = append(conf.DNSServers6, net.ParseIP(dns))
}
}
}
if len(nicdesc.Ntp) > 0 {
conf.NTPServers = make([]net.IP, 0)
conf.NTPServers6 = make([]net.IP, 0)
for _, ntp := range strings.Split(nicdesc.Ntp, ",") {
conf.NTPServers = append(conf.NTPServers, net.ParseIP(ntp))
if regutils.MatchIP4Addr(ntp) {
conf.NTPServers = append(conf.NTPServers, net.ParseIP(ntp))
} else if regutils.MatchIP6Addr(ntp) {
conf.NTPServers6 = append(conf.NTPServers6, net.ParseIP(ntp))
} else if regutils.MatchDomainName(ntp) {
ntpAddrs, _ := net.LookupHost(ntp)
for _, ntpAddr := range ntpAddrs {
if regutils.MatchIP4Addr(ntpAddr) {
conf.NTPServers = append(conf.NTPServers, net.ParseIP(ntpAddr))
} else if regutils.MatchIP6Addr(ntpAddr) {
conf.NTPServers6 = append(conf.NTPServers6, net.ParseIP(ntpAddr))
}
}
}
}
}
@@ -237,12 +309,12 @@ func (s *SGuestDHCPServer) getConfig(pkt dhcp.Packet) *dhcp.ResponseConfig {
ip, port = "", ""
isCandidate = false
)
guestDesc, guestNic := guestman.GuestDescGetter.GetGuestNicDesc(mac, ip, port, s.iface, isCandidate)
guestDesc, guestNic := guestman.GuestDescGetter.GetGuestNicDesc(mac, ip, port, s.ifaceDev.String(), isCandidate)
if guestNic == nil {
guestDesc, guestNic = guestman.GuestDescGetter.GetGuestNicDesc(mac, ip, port, s.iface, !isCandidate)
guestDesc, guestNic = guestman.GuestDescGetter.GetGuestNicDesc(mac, ip, port, s.ifaceDev.String(), !isCandidate)
}
if guestNic != nil && !guestNic.Virtual {
return getGuestConfig(guestDesc, guestNic)
if guestNic != nil && !guestNic.Virtual && len(guestNic.Ip6) > 0 {
return getGuestConfig(guestDesc, guestNic, s.ifaceDev.GetHardwareAddr())
}
return nil
}
+22 -13
View File
@@ -18,9 +18,11 @@ import (
"net"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
guestman "yunion.io/x/onecloud/pkg/hostman/guestman/types"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/netutils2"
)
const (
@@ -34,7 +36,7 @@ type SGuestDHCP6Server struct {
relay *SDHCP6Relay
conn *dhcp.Conn
iface string
ifaceDev *netutils2.SNetInterface
}
func NewGuestDHCP6Server(iface string, port int, relay *SDHCPRelayUpstream) (*SGuestDHCP6Server, error) {
@@ -43,19 +45,25 @@ func NewGuestDHCP6Server(iface string, port int, relay *SDHCPRelayUpstream) (*SG
guestdhcp = new(SGuestDHCP6Server)
)
dev := netutils2.NewNetInterface(iface)
if dev.GetHardwareAddr() == nil {
return nil, errors.Wrapf(errors.ErrInvalidStatus, "iface %s no mac", iface)
}
guestdhcp.ifaceDev = dev
guestdhcp.server, guestdhcp.conn, err = dhcp.NewDHCP6Server2(iface, DEFAULT_DHCP6_SERVER_PORT)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "dhcp.NewDHCP6Server2")
}
if relay != nil {
guestdhcp.relay, err = NewDHCP6Relay(guestdhcp.conn, relay)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "NewDHCP6Relay")
}
}
guestdhcp.iface = iface
return guestdhcp, nil
}
@@ -90,12 +98,12 @@ func (s *SGuestDHCP6Server) getConfig(cliMac net.HardwareAddr, _ dhcp.Packet) *d
ip, port = "", ""
isCandidate = false
)
guestDesc, guestNic := guestman.GuestDescGetter.GetGuestNicDesc(cliMac.String(), ip, port, s.iface, isCandidate)
guestDesc, guestNic := guestman.GuestDescGetter.GetGuestNicDesc(cliMac.String(), ip, port, s.ifaceDev.String(), isCandidate)
if guestNic == nil {
guestDesc, guestNic = guestman.GuestDescGetter.GetGuestNicDesc(cliMac.String(), ip, port, s.iface, !isCandidate)
guestDesc, guestNic = guestman.GuestDescGetter.GetGuestNicDesc(cliMac.String(), ip, port, s.ifaceDev.String(), !isCandidate)
}
if guestNic != nil && !guestNic.Virtual && len(guestNic.Ip6) > 0 {
return getGuestConfig(guestDesc, guestNic)
return getGuestConfig(guestDesc, guestNic, s.ifaceDev.GetHardwareAddr())
}
return nil
}
@@ -105,20 +113,21 @@ 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, error) {
var conf = s.getConfig(cliMac, pkt)
func (s *SGuestDHCP6Server) ServeRA(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (dhcp.Packet, net.IP, net.HardwareAddr, 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, uint32(conf.MTU))
return dhcp.MakeRouterAdverPacket(conf.Gateway6, conf.PrefixLen6, conf.Routes6, uint32(conf.MTU))
}
return nil, nil
return nil, nil, nil, nil
}
func (s *SGuestDHCP6Server) serveDHCPInternal(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (dhcp.Packet, error) {
var conf = s.getConfig(cliMac, pkt)
if conf != nil {
log.Infof("Make DHCPv6 Reply %s TO %s", conf.ClientIP6, pkt.CHAddr())
log.Infof("Make DHCPv6 Reply %s TO %s %s", conf.ClientIP6, cliMac.String(), addr.String())
// Guest request ip
return dhcp.MakeReplyPacket(pkt, conf)
return dhcp.MakeDHCP6Reply(pkt, conf)
} else if s.relay != nil && s.relay.server != nil {
// Host agent as dhcp relay, relay to baremetal
return s.relay.Relay(pkt, addr)
+11
View File
@@ -73,6 +73,7 @@ type conn interface {
Send(b []byte, addr *net.UDPAddr, destMac net.HardwareAddr) error
Recv(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr, int, 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)
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
@@ -208,6 +209,16 @@ func (c *Conn) SendDHCP(pkt Packet, addr *net.UDPAddr, mac net.HardwareAddr) err
return c.conn.Send(b, addr, mac)
}
func (c *Conn) SendDHCP6(pkt Packet, addr *net.UDPAddr, mac net.HardwareAddr) error {
b := pkt.Marshal()
return c.conn.Send6(b, addr, mac)
}
func (c *Conn) SendICMP6(pkt Packet, srcIp net.IP, srcmac, dstmac net.HardwareAddr) error {
b := pkt.Marshal()
return c.conn.SendICMP6(b, srcIp, srcmac, dstmac)
}
// SetReadDeadline sets the deadline for future Read calls. If the
// deadline is reached, Read will fail with a timeout (see net.Error)
// instead of blocking. A zero value for t means Read will not time
+6 -2
View File
@@ -70,11 +70,11 @@ func interfaceToIPv6Addr(ifi *net.Interface) (net.IP, error) {
for _, ifa := range ifat {
switch v := ifa.(type) {
case *net.IPAddr:
if len(v.IP) == net.IPv6len {
if v.IP.To4() == nil && v.IP.To16() != nil {
return v.IP, nil
}
case *net.IPNet:
if len(v.IP) == net.IPv6len {
if v.IP.To4() == nil && v.IP.To16() != nil {
return v.IP, nil
}
}
@@ -146,3 +146,7 @@ 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 {
return errors.Wrap(errors.ErrNotImplemented, "SendICMP6 not implemented")
}
+45 -2
View File
@@ -106,6 +106,7 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
icmpLayer := p.Layer(layers.LayerTypeICMPv6)
if icmpLayer != nil {
log.Infof("rawSocketConn Recv6 icmpLayer %s", icmpLayer.LayerType())
raLayer := p.Layer(layers.LayerTypeICMPv6RouterSolicitation)
if raLayer != nil {
// icmp6, ra solitation
@@ -143,14 +144,56 @@ func (s *rawSocketConn) Recv6(b []byte) ([]byte, *net.UDPAddr, net.HardwareAddr,
}
}
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")
}
// s.conn.SetWriteDeadline(time.Now().Add(DefaultWriteTimeout)) // 2 second
if _, err := s.conn.WriteTo(buf.Bytes(), &packet.Addr{HardwareAddr: destMac}); err != nil {
return errors.Wrap(err, "Send icmp packet error")
}
return nil
}
func (s *rawSocketConn) Send6(b []byte, addr *net.UDPAddr, destMac net.HardwareAddr) error {
var dhcp = new(layers.DHCPv4)
var dhcp = new(layers.DHCPv6)
if err := dhcp.DecodeFromBytes(b, gopacket.NilDecodeFeedback); err != nil {
return errors.Wrap(err, "Decode dhcp bytes error")
}
var eth = &layers.Ethernet{
EthernetType: layers.EthernetTypeIPv4,
EthernetType: layers.EthernetTypeIPv6,
SrcMAC: s.iface.HardwareAddr,
DstMAC: destMac,
}
+42 -12
View File
@@ -18,11 +18,11 @@ import (
"encoding/binary"
"fmt"
"net"
"strconv"
"strings"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
)
const (
@@ -34,6 +34,8 @@ const (
// http://www.networksorcery.com/enp/rfc/rfc2132.txt
type ResponseConfig struct {
InterfaceMac net.HardwareAddr
OsName string
ServerIP net.IP // OptServerIdentifier 54
ClientIP net.IP
@@ -45,13 +47,16 @@ type ResponseConfig struct {
Hostname string // OptHostname 12
SubnetMask net.IP // OptSubnetMask 1
DNSServers []net.IP // OptDNSServers
Routes [][]string // TODO: 249 for windows, 121 for linux
Routes []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
ClientIP6 net.IP
Gateway6 net.IP
PrefixLen6 uint8
DNSServers6 []net.IP
NTPServers6 []net.IP
Routes6 []SRouteInfo
// Relay Info https://datatracker.ietf.org/doc/html/rfc3046
RelayInfo []byte
@@ -90,14 +95,40 @@ func GetOptTime(d time.Duration) []byte {
return timeBytes
}
func getClasslessRoutePack(route []string) []byte {
var snet, gw = route[0], route[1]
tmp := strings.Split(snet, "/")
netaddr := net.ParseIP(tmp[0])
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 {
// var snet, gw = route[0], route[1]
// tmp := strings.Split(snet, "/")
netaddr := route.Prefix
if netaddr != nil {
netaddr = netaddr.To4()
}
masklen, _ := strconv.Atoi(tmp[1])
masklen := route.PrefixLen
netlen := masklen / 8
if masklen%8 > 0 {
netlen += 1
@@ -105,11 +136,10 @@ func getClasslessRoutePack(route []string) []byte {
if netlen < 4 {
netaddr = netaddr[0:netlen]
}
gwaddr := net.ParseIP(gw)
gwaddr := route.Gateway
if gwaddr != nil {
gwaddr = gwaddr.To4()
}
res := []byte{byte(masklen)}
res = append(res, []byte(netaddr)...)
return append(res, []byte(gwaddr)...)
+62 -1
View File
@@ -16,6 +16,7 @@ package dhcp
import (
"fmt"
"net"
"testing"
)
@@ -60,9 +61,69 @@ func TestGetClasslessRoutePack(t *testing.T) {
},
}
for _, c := range cases {
got := getClasslessRoutePack([]string{c.net, c.gw})
routeInfo, err := ParseRouteInfo([]string{c.net, c.gw})
if err != nil {
t.Errorf("parse route %s error: %v", c.net, err)
continue
}
got := getClasslessRoutePack(*routeInfo)
if err := compareBytes(c.want, got); err != nil {
t.Errorf("net: %s gw: %s want: %#v got: %#v", c.net, c.gw, c.want, got)
}
}
}
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())
}
}
}
+1 -1
View File
@@ -25,5 +25,5 @@ type DHCPHandler interface {
type DHCP6Handler interface {
DHCPHandler
ServeRA(pkt Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (Packet, error)
ServeRA(pkt Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (Packet, net.IP, net.HardwareAddr, error)
}
+290 -32
View File
@@ -16,15 +16,26 @@ 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"
"yunion.io/x/pkg/util/netutils"
)
/*
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
}
@@ -42,6 +53,7 @@ func NewIcmpV6OptMtu(mtu uint32) icmpV6OptMtu {
}
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -68,7 +80,7 @@ type icmpv6OptPrefixInfo struct {
ValidLifetime uint32
PreferredLifetime uint32
Reserved2 uint32
Prefix [16]byte
Prefix []byte
}
func (opt icmpv6OptPrefixInfo) Bytes() []byte {
@@ -83,52 +95,126 @@ func (opt icmpv6OptPrefixInfo) Bytes() []byte {
}
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: [16]byte(gw),
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 netutils.SMacAddr
Mac net.HardwareAddr
}
func (opt icmpv6OptSourceTargetAddress) Bytes() []byte {
return opt.Mac[:]
return opt.Mac[0:6]
}
func NewIcmpv6OptSourceTargetAddress(macAddr string) icmpv6OptSourceTargetAddress {
mac, _ := netutils.ParseMac(macAddr)
func NewIcmpv6OptSourceTargetAddress(macAddr net.HardwareAddr) icmpv6OptSourceTargetAddress {
return icmpv6OptSourceTargetAddress{
Mac: mac,
Mac: macAddr,
}
}
func MakeRouterAdverPacket(gwIP net.IP, preflen uint8, mtu uint32) (Packet, error) {
/*
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{}
pkt.Flags = 0x80 // M=1, O=0 stateful DHCPv6
// 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, preflen).Bytes(),
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, errors.Wrap(err, "SerializeTo")
return nil, nil, nil, errors.Wrap(err, "SerializeTo")
}
return sbf.Bytes(), nil
return sbf.Bytes(), gwIP, nil, nil
}
// DHCPv6 https://datatracker.ietf.org/doc/html/rfc8415
@@ -212,6 +298,17 @@ const (
DHCPV6_OPTION_INFORMATION_REFRESH_TIME OptionCode6 = 32
DHCPV6_OPTION_SOL_MAX_RT OptionCode6 = 82
DHCPV6_OPTION_INF_MAX_RT OptionCode6 = 83
// https://www.rfc-editor.org/rfc/rfc3646
OPTION_DNS_SERVERS OptionCode6 = 23
OPTION_DOMAIN_LIST OptionCode6 = 24
// https://www.rfc-editor.org/rfc/rfc4075
OPTION_SNTP_SERVERS OptionCode6 = 31
//https://www.rfc-editor.org/rfc/rfc5908
OPTION_NTP_SERVERS6 OptionCode6 = 56
// https://www.rfc-editor.org/rfc/rfc4833
OPTION_NEW_POSIX_TIMEZONE OptionCode6 = 41
OPTION_NEW_TZDB_TIMEZONE OptionCode6 = 42
)
// DHCPv6 message type
@@ -239,26 +336,32 @@ func (p Packet) PeerAddr() net.IP {
return net.IP(p[18:34])
}
func (p Packet) SetType6(hType MessageType) { p[0] = byte(hType) }
func (p Packet) IsRelayMsg() bool {
return p.Type6() == DHCPV6_RELAY_FORW || p.Type6() == DHCPV6_RELAY_REPL
}
func (p Packet) SetTID(tid uint32) {
func (p *Packet) SetType6(hType MessageType) {
(*p)[0] = byte(hType)
}
func (p *Packet) SetTID(tid uint32) {
tidBytes := make([]byte, 4)
binary.BigEndian.PutUint32(tidBytes, tid)
p[1] = tidBytes[1]
p[2] = tidBytes[2]
p[3] = tidBytes[3]
(*p)[1] = tidBytes[1]
(*p)[2] = tidBytes[2]
(*p)[3] = tidBytes[3]
}
func (p Packet) SetHopCount(hops byte) {
p[1] = hops
func (p *Packet) SetHopCount(hops byte) {
(*p)[1] = hops
}
func (p Packet) SetLinkAddr(linkAddr net.IP) {
copy(p[2:18], linkAddr)
func (p *Packet) SetLinkAddr(linkAddr net.IP) {
copy((*p)[2:18], linkAddr)
}
func (p Packet) SetPeerAddr(peerAddr net.IP) {
copy(p[18:34], peerAddr)
func (p *Packet) SetPeerAddr(peerAddr net.IP) {
copy((*p)[18:34], peerAddr)
}
func NewPacket6(opCode MessageType, tid uint32) Packet {
@@ -284,20 +387,175 @@ type Option6 struct {
| (option-len octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
func (p *Packet) AddOption6(o Option6) {
buf := make([]byte, 2)
binary.BigEndian.PutUint16(buf, uint16(o.Code))
*p = append(*p, buf...)
binary.BigEndian.PutUint16(buf, uint16(len(o.Value)))
*p = append(*p, buf...)
*p = append(*p, o.Value...)
func optionToBytes(o Option6) []byte {
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[0:2], uint16(o.Code))
binary.BigEndian.PutUint16(buf[2:4], uint16(len(o.Value)))
buf = append(buf, o.Value...)
return buf
}
func optionsToBytes(opts []Option6) []byte {
buf := make([]byte, 0)
for i := range opts {
buf = append(buf, optionToBytes(opts[i])...)
}
return buf
}
func (p Packet) GetOption6s() []Option6 {
options := make([]Option6, 0)
offset := 4
if p.IsRelayMsg() {
offset = 18
}
i := offset
for i < len(p) {
code := binary.BigEndian.Uint16(p[i : i+2])
length := binary.BigEndian.Uint16(p[i+2 : i+4])
options = append(options, Option6{
Code: OptionCode6(code),
Value: p[i+4 : i+4+int(length)],
})
i += 4 + int(length)
}
return options
}
// Creates a request packet that a Client would send to a server.
func RequestPacket6(mt MessageType, tid uint32, options []Option6) Packet {
/*func RequestPacket6(mt MessageType, tid uint32, options []Option6) Packet {
p := NewPacket6(mt, tid)
for _, o := range options {
p.AddOption6(o)
}
return p
}*/
func MakeDHCP6Reply(pkt Packet, conf *ResponseConfig) (Packet, error) {
var msgType MessageType
pktType := pkt.Type6()
switch pktType {
case DHCPV6_SOLICIT:
msgType = DHCPV6_ADVERTISE
case DHCPV6_REQUEST:
msgType = DHCPV6_REPLY
case DHCPV6_CONFIRM:
msgType = DHCPV6_REPLY
case DHCPV6_RENEW:
msgType = DHCPV6_REPLY
case DHCPV6_REBIND:
msgType = DHCPV6_REPLY
case DHCPV6_INFORMATION_REQUEST:
msgType = DHCPV6_REPLY
default:
return nil, errors.Wrapf(errors.ErrNotSupported, "unsupported message type %d", pktType)
}
return makeDHCPReplyPacket6(pkt, conf, msgType), nil
}
const (
DUID_TYPE_LINK_LAYER_ADDRESS = 3
DUID_HARDWARE_TYPE_ETHERNET = 1
)
func makeServerId(serverMac net.HardwareAddr) []byte {
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[0:2], uint16(DUID_TYPE_LINK_LAYER_ADDRESS))
binary.BigEndian.PutUint16(buf[2:4], uint16(DUID_HARDWARE_TYPE_ETHERNET))
buf = append(buf, serverMac[:6]...)
return buf
}
func makeIAAddr(ip net.IP, preferLT, validLT uint32, opts []Option6) []byte {
buf := make([]byte, 24)
copy(buf[0:16], ip.To16())
binary.BigEndian.PutUint32(buf[16:20], preferLT)
binary.BigEndian.PutUint32(buf[20:24], validLT)
buf = append(buf, optionsToBytes(opts)...)
return buf
}
func responseIANA(buf []byte, opts []Option6) []byte {
log.Debugf("responseIANA buf %d", len(buf))
if len(buf) > 12 {
buf = buf[:12]
}
iaID := binary.BigEndian.Uint32(buf[0:4])
t1 := binary.BigEndian.Uint32(buf[4:8])
t2 := binary.BigEndian.Uint32(buf[8:12])
log.Debugf("responseIANA IA_NA IAID %d t1 %d t2 %d", iaID, t1, t2)
buf = append(buf, optionsToBytes(opts)...)
return buf
}
func makeIPv6s(ips []net.IP) []byte {
buf := make([]byte, 0)
for _, ip := range ips {
ip6 := ip.To16()
if ip6 == nil {
continue
}
buf = append(buf, ip6...)
}
return buf
}
func makeDHCPReplyPacket6(pkt Packet, conf *ResponseConfig, msgType MessageType) Packet {
log.Debugf("makeDHCPReplyPacket6 msgType %d tid %x", msgType, pkt.TID())
resp := NewPacket6(msgType, pkt.TID())
originOpts := pkt.GetOption6s()
getOption := func(code OptionCode6) Option6 {
for _, o := range originOpts {
if o.Code == code {
return o
}
}
return Option6{}
}
options := make([]Option6, 0)
// copy clientID
options = append(options, getOption(DHCPV6_OPTION_CLIENTID))
// serverID
options = append(options, Option6{
Code: DHCPV6_OPTION_SERVERID,
Value: makeServerId(conf.InterfaceMac),
})
// Identity Association for Non-temporary Addresses Option
ianaOpt := getOption(DHCPV6_OPTION_IA_NA)
options = append(options, Option6{
Code: DHCPV6_OPTION_IA_NA,
Value: responseIANA(ianaOpt.Value, []Option6{
{
Code: DHCPV6_OPTION_IAADDR,
Value: makeIAAddr(conf.ClientIP6, uint32(conf.LeaseTime.Seconds()), uint32(conf.LeaseTime.Seconds()), []Option6{
{
Code: DHCPV6_OPTION_STATUS_CODE,
Value: []byte{0, 0, 'S', 'u', 'c', 'c', 'e', 's', 's'},
},
}),
},
}),
})
if len(conf.DNSServers6) > 0 {
options = append(options, Option6{
Code: OPTION_DNS_SERVERS,
Value: makeIPv6s(conf.DNSServers6),
})
}
if len(conf.NTPServers6) > 0 {
options = append(options, Option6{
Code: OPTION_SNTP_SERVERS,
Value: makeIPv6s(conf.NTPServers6),
})
}
resp = append(resp, optionsToBytes(options)...)
return resp
}
+30 -24
View File
@@ -68,8 +68,8 @@ const (
bpfProcDhcpResp6 uint8 = 8
bpfProcICMPv6 uint8 = 11
bpfProcReadPkt6 uint8 = 14
bpfProcEnd6 uint8 = 15
bpfProcReadPkt6 uint8 = 17
bpfProcEnd6 uint8 = 18
bpfProcReadPkt4 uint8 = 14
bpfProcEnd4 uint8 = 15
@@ -128,29 +128,35 @@ var (
&bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: udpProtocol, SkipTrue: bpfGoto(4, bpfProcICMPv6), SkipFalse: bpfContinue},
/* 4 load 14 + 40 + 0 (UDP src port), 2 bytes */
bpf.LoadAbsolute{Off: 54, Size: 2},
/* 5 if udp_src_port != 547 then jump to ipv6_resp/21(19+2) else continue */
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 547, SkipTrue: bpfGoto(6, bpfProcDhcpResp), SkipFalse: bpfContinue},
/* 5 if udp_src_port != 546 then jump to ipv6_resp/21(19+2) else continue */
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 546, SkipTrue: bpfGoto(6, bpfProcDhcpResp), SkipFalse: bpfContinue},
/* 6 load 14 + 40 + 2 (udp dst port), 2 bytes */
bpf.LoadAbsolute{Off: 56, Size: 2},
/* 7 if udp_dst_port == 546 then jump to read/24(21+3) else jump to end/25(21+4) */
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 546, SkipTrue: bpfGoto(8, bpfProcReadPkt6), SkipFalse: bpfGoto(8, bpfProcEnd6)},
/* 8 [dhcpv6_resp] if udp_src_port != 546 then jump to end/25(22+3) else continue, which means a UDP response */
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 546, SkipTrue: bpfGoto(9, bpfProcEnd6), SkipFalse: bpfContinue},
/* 7 if udp_dst_port == 547 then jump to readPkt6 else jump to end */
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 547, SkipTrue: bpfGoto(8, bpfProcReadPkt6), SkipFalse: bpfGoto(8, bpfProcEnd6)},
/* 8 [dhcpv6_resp] if udp_src_port != 547 then jump to end else continue, which means a DHCPv6 response */
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 547, SkipTrue: bpfGoto(9, bpfProcEnd6), SkipFalse: bpfContinue},
/* 9 load 14 + 40 + 2 (udp dst port), 2 bytes */
bpf.LoadAbsolute{Off: 56, Size: 2},
/* 10 if udp_dst_port != 547 then jump to end/25(24+1) else jump to read/ */
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 547, SkipTrue: bpfGoto(11, bpfProcEnd6), SkipFalse: bpfGoto(11, bpfProcReadPkt6)},
/* 10 if udp_dst_port != 546 then jump to end else jump to readPkt6 */
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 546, SkipTrue: bpfGoto(11, bpfProcEnd6), SkipFalse: bpfGoto(11, bpfProcReadPkt6)},
/* 11 [icmp6] if ip_proto != ICMP then jump to [end]/24(17 + 7) else continue */
/* 11 [icmp6] if ip_proto != ICMP then jump to [end] else continue */
&bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: icmpProtocol, SkipTrue: bpfGoto(12, bpfProcEnd6), SkipFalse: bpfContinue},
/* 12 [icmp6 type] load 14 + 40 + 0 (ICMPv6 type), 1 bytes */
bpf.LoadAbsolute{Off: 54, Size: 1},
/* 13 [Router Solicitation] if icmp6_type == 133 then jump to [read] else [end] */
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 133, SkipTrue: bpfGoto(14, bpfProcReadPkt6), SkipFalse: bpfGoto(14, bpfProcEnd6)},
/* 13 [Router Solicitation] if icmp6_type == 133 then jump to [read] else [continue] */
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 133, SkipTrue: bpfGoto(14, bpfProcReadPkt6), SkipFalse: bpfContinue},
/* 14 [Router Advertisement] if icmp6_type == 134 then jump to [read] else [continue] */
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 134, SkipTrue: bpfGoto(15, bpfProcReadPkt6), SkipFalse: bpfContinue},
/* 15 [Neightbor Solicitation] if icmp6_type == 135 then jump to [read] else [continue] */
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 135, SkipTrue: bpfGoto(16, bpfProcReadPkt6), SkipFalse: bpfContinue},
/* 16 [Neighbor Advertisement] if icmp6_type == 136 then jump to [read] else [end] */
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 136, SkipTrue: bpfGoto(17, bpfProcReadPkt6), SkipFalse: bpfGoto(17, bpfProcEnd6)},
/* 14 [read] return the whole packet */
/* 17 [read] return the whole packet */
bpf.RetConstant{Val: 0x40000},
/* 15 [end] return none */
/* 18 [end] return none */
bpf.RetConstant{Val: 0x0},
}
)
@@ -285,7 +291,7 @@ func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
if addr.Port == icmpRAFakePort {
// receive a RA solication
resp, err := handler.ServeRA(pkt, mac, addr)
resp, gwIp, gwMac, err := handler.ServeRA(pkt, mac, addr)
if err != nil {
log.Warningf("[DHCP6] handler ServeRA error: %s", err)
return
@@ -295,20 +301,21 @@ func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
return
}
//log.Debugf("[DHCP] send response packet: %s to interface: %#v", resp.DebugString(), intf)
err = s.conn.SendDHCP(resp, addr, mac)
// RA should be sent to FF02::1 from gwIP
err = s.conn.SendICMP6(resp, gwIp, gwMac, mac)
if err != nil {
log.Errorf("[DHCP] failed to response packet for %s: %v", pkt.CHAddr(), err)
log.Errorf("[DHCP6] failed to response packet for ICMP6 message: %s", err)
}
return
}
resp, targets, err := handler.ServeDHCP(pkt, mac, addr)
if err != nil {
log.Warningf("[DHCP] handler serve error: %s", err)
log.Warningf("[DHCP6] handler serve error: %s", err)
return
}
if resp == nil {
// log.Warningf("[DHCP] hander response null packet")
log.Warningf("[DHCP6] hander response null packet")
return
}
if len(targets) > 0 {
@@ -316,15 +323,14 @@ func (s *DHCP6Server) serveDHCP(handler DHCP6Handler) error {
for _, target := range targets {
log.Debugf("[DHCP6] Send packet back to %s", target)
targetUdpAddr.IP = net.ParseIP(target)
resp.SetGIAddr(targetUdpAddr.IP)
if err = s.conn.SendDHCP(resp, &targetUdpAddr, mac); err != nil {
if err = s.conn.SendDHCP6(resp, &targetUdpAddr, mac); err != nil {
log.Errorf("[DHCP6] failed to response packet for %s: %v", pkt.CHAddr(), err)
}
}
return
}
//log.Debugf("[DHCP] send response packet: %s to interface: %#v", resp.DebugString(), intf)
if err = s.conn.SendDHCP(resp, addr, mac); err != nil {
if err = s.conn.SendDHCP6(resp, addr, mac); err != nil {
log.Errorf("[DHCP6] failed to response packet for %s: %v", pkt.CHAddr(), err)
return
}
+40 -17
View File
@@ -30,6 +30,7 @@ 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"
)
@@ -131,22 +132,31 @@ func Netlen2Mask(netmasklen int) string {
return netutils.Netlen2Mask(netmasklen)
}
func addRoute(routes [][]string, net, gw string) [][]string {
for _, rt := range routes {
if rt[0] == net {
return routes
func addRoute(routes []dhcp.SRouteInfo, net, gw string) []dhcp.SRouteInfo {
route, _ := dhcp.ParseRouteInfo([]string{net, gw})
if route != nil {
for _, rt := range routes {
if rt.Prefix.String() == route.Prefix.String() && rt.PrefixLen == route.PrefixLen {
return routes
}
}
}
return append(routes, []string{net, gw})
}
func extendRoutes(routes [][]string, nicRoutes []types.SRoute) [][]string {
for i := 0; i < len(nicRoutes); i++ {
routes = addRoute(routes, nicRoutes[i][0], nicRoutes[i][1])
// not found
routes = append(routes, *route)
}
return routes
}
func extendRoutes(routes4, routes6 []dhcp.SRouteInfo, nicRoutes []types.SRoute) ([]dhcp.SRouteInfo, []dhcp.SRouteInfo) {
for i := 0; i < len(nicRoutes); i++ {
if regutils.MatchCIDR6(nicRoutes[i][0]) {
routes6 = addRoute(routes6, nicRoutes[i][0], nicRoutes[i][1])
} else {
routes4 = addRoute(routes4, nicRoutes[i][0], nicRoutes[i][1])
}
}
return routes4, routes6
}
func isExitAddress(ip string) bool {
ipv4, err := netutils.NewIPV4Addr(ip)
if err != nil {
@@ -156,28 +166,33 @@ func isExitAddress(ip string) bool {
return netutils.IsExitAddress(ipv4)
}
func AddNicRoutes(routes [][]string, nicDesc *types.SServerNic, mainIp string, nicCnt int) [][]string {
func AddNicRoutes(routes4 []dhcp.SRouteInfo, routes6 []dhcp.SRouteInfo, nicDesc *types.SServerNic, mainIp string, mainIp6 string, nicCnt int) ([]dhcp.SRouteInfo, []dhcp.SRouteInfo) {
// always add static routes, even if this is the default NIC
// if mainIp == nicDesc.Ip {
// return routes
// }
if len(nicDesc.Routes) > 0 {
routes = extendRoutes(routes, nicDesc.Routes)
routes4, routes6 = extendRoutes(routes4, routes6, nicDesc.Routes)
} else if len(nicDesc.Gateway) > 0 && !isExitAddress(nicDesc.Ip) &&
nicCnt == 2 && nicDesc.Ip != mainIp && isExitAddress(mainIp) {
for _, pref := range netutils.GetPrivateIPRanges() {
prefs := pref.ToPrefixes()
for _, p := range prefs {
routes = addRoute(routes, p.String(), nicDesc.Gateway)
routes4 = addRoute(routes4, p.String(), nicDesc.Gateway)
}
}
}
if nicDesc.Ip == mainIp {
if len(mainIp) > 0 && nicDesc.Ip == mainIp {
// always add 169.254.169.254 for default NIC
routes = addRoute(routes, "169.254.169.254/32", "0.0.0.0")
routes4 = addRoute(routes4, "169.254.169.254/32", "0.0.0.0")
}
return routes
if len(mainIp6) > 0 && nicDesc.Ip6 == mainIp6 {
routes6 = addRoute(routes6, "fd00:ec2::254/128", "::")
routes6 = addRoute(routes6, "fe80::a9fe:a9fe/128", "::")
}
return routes4, routes6
}
func GetNicDns(nicdesc *types.SServerNic) []string {
@@ -324,6 +339,14 @@ func (n *SNetInterface) GetMac() string {
return n.mac
}
func (n *SNetInterface) GetHardwareAddr() net.HardwareAddr {
mac, err := net.ParseMAC(n.mac)
if err != nil {
return nil
}
return mac
}
func (n *SNetInterface) GetAllMacs() []string {
macs := make([]string, 0, len(n.BondingSlaves)+1)
find := false