From 5dbb0f2a281b70380a3cbd373b7028d170c11c23 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 7 Mar 2017 09:49:28 +1000 Subject: [PATCH 01/13] simplistic start Signed-off-by: Sven Dowideit --- cmd/cloudinitsave/cloudinitsave.go | 33 +++++++++---------- .../metadata/digitalocean/metadata.go | 3 ++ .../datasource/metadata/ec2/metadata.go | 3 ++ .../datasource/metadata/gce/metadata.go | 10 ++++-- .../datasource/metadata/packet/metadata.go | 4 +++ 5 files changed, 32 insertions(+), 21 deletions(-) mode change 100644 => 100755 config/cloudinit/datasource/metadata/digitalocean/metadata.go mode change 100644 => 100755 config/cloudinit/datasource/metadata/ec2/metadata.go mode change 100644 => 100755 config/cloudinit/datasource/metadata/packet/metadata.go diff --git a/cmd/cloudinitsave/cloudinitsave.go b/cmd/cloudinitsave/cloudinitsave.go index 06c277c4..bcef1480 100755 --- a/cmd/cloudinitsave/cloudinitsave.go +++ b/cmd/cloudinitsave/cloudinitsave.go @@ -181,23 +181,24 @@ func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.D for _, ds := range cfg.Rancher.CloudInit.Datasources { parts := strings.SplitN(ds, ":", 2) + root := "" + if len(parts) > 1 { + root = parts[1] + } + switch parts[0] { case "ec2": if network { - if len(parts) == 1 { - dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress)) - } else { - dss = append(dss, ec2.NewDatasource(parts[1])) - } + dss = append(dss, ec2.NewDatasource(root)) } case "file": - if len(parts) == 2 { - dss = append(dss, file.NewDatasource(parts[1])) + if root != "" { + dss = append(dss, file.NewDatasource(root)) } case "url": if network { - if len(parts) == 2 { - dss = append(dss, url.NewDatasource(parts[1])) + if root != "" { + dss = append(dss, url.NewDatasource(root)) } } case "cmdline": @@ -207,28 +208,24 @@ func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.D } } case "configdrive": - if len(parts) == 2 { - dss = append(dss, configdrive.NewDatasource(parts[1])) + if root != "" { + dss = append(dss, configdrive.NewDatasource(root)) } case "digitalocean": if network { - if len(parts) == 1 { - dss = append(dss, digitalocean.NewDatasource(digitalocean.DefaultAddress)) - } else { - dss = append(dss, digitalocean.NewDatasource(parts[1])) - } + dss = append(dss, digitalocean.NewDatasource(root)) } else { enableDoLinkLocal() } case "gce": if network { - dss = append(dss, gce.NewDatasource("http://metadata.google.internal/")) + dss = append(dss, gce.NewDatasource(root)) } case "packet": if !network { enablePacketNetwork(&cfg.Rancher) } - dss = append(dss, packet.NewDatasource("https://metadata.packet.net/")) + dss = append(dss, packet.NewDatasource(root)) } } diff --git a/config/cloudinit/datasource/metadata/digitalocean/metadata.go b/config/cloudinit/datasource/metadata/digitalocean/metadata.go old mode 100644 new mode 100755 index 5fc0df48..f8ca981c --- a/config/cloudinit/datasource/metadata/digitalocean/metadata.go +++ b/config/cloudinit/datasource/metadata/digitalocean/metadata.go @@ -66,6 +66,9 @@ type MetadataService struct { } func NewDatasource(root string) *MetadataService { + if root == "" { + root = DefaultAddress + } return &MetadataService{Service: metadata.NewDatasource(root, apiVersion, userdataURL, metadataPath, nil)} } diff --git a/config/cloudinit/datasource/metadata/ec2/metadata.go b/config/cloudinit/datasource/metadata/ec2/metadata.go old mode 100644 new mode 100755 index ee3fa1d7..36be3f70 --- a/config/cloudinit/datasource/metadata/ec2/metadata.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata.go @@ -39,6 +39,9 @@ type MetadataService struct { } func NewDatasource(root string) *MetadataService { + if root == "" { + root = DefaultAddress + } return &MetadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, nil)} } diff --git a/config/cloudinit/datasource/metadata/gce/metadata.go b/config/cloudinit/datasource/metadata/gce/metadata.go index 35e3ce9f..2e54f7b5 100755 --- a/config/cloudinit/datasource/metadata/gce/metadata.go +++ b/config/cloudinit/datasource/metadata/gce/metadata.go @@ -26,9 +26,10 @@ import ( ) const ( - apiVersion = "computeMetadata/v1/" - metadataPath = apiVersion - userdataPath = apiVersion + "instance/attributes/user-data" + DefaultAddress = "http://metadata.google.internal/" + apiVersion = "computeMetadata/v1/" + metadataPath = apiVersion + userdataPath = apiVersion + "instance/attributes/user-data" ) type MetadataService struct { @@ -36,6 +37,9 @@ type MetadataService struct { } func NewDatasource(root string) *MetadataService { + if root == "" { + root = DefaultAddress + } return &MetadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, http.Header{"Metadata-Flavor": {"Google"}})} } diff --git a/config/cloudinit/datasource/metadata/packet/metadata.go b/config/cloudinit/datasource/metadata/packet/metadata.go old mode 100644 new mode 100755 index 6b7b11c1..b00dad57 --- a/config/cloudinit/datasource/metadata/packet/metadata.go +++ b/config/cloudinit/datasource/metadata/packet/metadata.go @@ -62,6 +62,10 @@ type MetadataService struct { } func NewDatasource(root string) *MetadataService { + if root == "" { + root = DefaultAddress + } + return &MetadataService{Service: metadata.NewDatasource(root, apiVersion, userdataURL, metadataPath, nil)} } From 0779e13d46e7009904274daef5fd40a7434b9587 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 7 Mar 2017 13:21:14 +1000 Subject: [PATCH 02/13] Refactor the cloud-init metadata to return a netconf.NetworkConfig Signed-off-by: Sven Dowideit --- cmd/cloudinitsave/cloudinitsave.go | 42 +++++- cmd/cloudinitsave/packet.go | 88 +++--------- cmd/network/network.go | 1 + .../datasource/configdrive/configdrive.go | 9 +- .../configdrive/configdrive_test.go | 3 +- config/cloudinit/datasource/datasource.go | 18 ++- .../metadata/digitalocean/metadata.go | 66 ++++++++- .../metadata/digitalocean/metadata_test.go | 37 +++--- .../datasource/metadata/ec2/metadata.go | 10 ++ .../datasource/metadata/ec2/metadata_test.go | 21 +++ .../datasource/metadata/gce/metadata.go | 18 +++ .../datasource/metadata/gce/metadata_test.go | 12 ++ .../cloudinit/datasource/metadata/metadata.go | 6 +- .../datasource/metadata/packet/metadata.go | 125 +++++++++++------- config/cloudinit/datasource/vmware/vmware.go | 2 +- .../datasource/vmware/vmware_test.go | 72 +++++----- config/cloudinit/network/packet.go | 72 +++++----- config/disk.go | 1 + config/types.go | 44 +----- dfs/scratch.go | 7 +- netconf/netconf_linux.go | 29 ++-- netconf/types.go | 34 +++++ scripts/ros | 29 ++++ 23 files changed, 459 insertions(+), 287 deletions(-) mode change 100644 => 100755 cmd/cloudinitsave/packet.go mode change 100644 => 100755 config/cloudinit/datasource/metadata/digitalocean/metadata_test.go mode change 100644 => 100755 config/cloudinit/datasource/metadata/ec2/metadata_test.go mode change 100644 => 100755 config/cloudinit/network/packet.go mode change 100644 => 100755 config/types.go create mode 100755 netconf/types.go create mode 100755 scripts/ros diff --git a/cmd/cloudinitsave/cloudinitsave.go b/cmd/cloudinitsave/cloudinitsave.go index bcef1480..5e7593b3 100755 --- a/cmd/cloudinitsave/cloudinitsave.go +++ b/cmd/cloudinitsave/cloudinitsave.go @@ -16,8 +16,10 @@ package cloudinitsave import ( + "bytes" "errors" "os" + "path" "strings" "sync" "time" @@ -110,8 +112,8 @@ func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadat if err := util.WriteFileAtomic(rancherConfig.CloudConfigBootFile, cloudConfigBytes, 400); err != nil { return err } - // Don't put secrets into the log - //log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes)) + // TODO: Don't put secrets into the log + log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes)) } metaDataBytes, err := yaml.Marshal(metadata) @@ -122,8 +124,34 @@ func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadat if err = util.WriteFileAtomic(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil { return err } - // Don't put secrets into the log - //log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes)) + // TODO: Don't put secrets into the log + log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes)) + + // if we write the empty meta yml, the merge fails. + // TODO: the problem is that a partially filled one will still have merge issues, so that needs fixing - presumably by making merge more clever, and making more fields optional + emptyMeta, err := yaml.Marshal(datasource.Metadata{}) + if err != nil { + return err + } + if bytes.Compare(metaDataBytes, emptyMeta) == 0 { + log.Infof("not writing %s: its all defaults.", rancherConfig.CloudConfigNetworkFile) + return nil + } + // write the network.yml file from metadata + cc := rancherConfig.CloudConfig{ + Rancher: rancherConfig.RancherConfig{ + Network: metadata.NetworkConfig, + }, + } + + if err := os.MkdirAll(path.Dir(rancherConfig.CloudConfigNetworkFile), 0700); err != nil { + log.Errorf("Failed to create directory for file %s: %v", rancherConfig.CloudConfigNetworkFile, err) + } + + if err := rancherConfig.WriteToFile(cc, rancherConfig.CloudConfigNetworkFile); err != nil { + log.Errorf("Failed to save config file %s: %v", rancherConfig.CloudConfigNetworkFile, err) + } + log.Infof("Written to %s:", rancherConfig.CloudConfigNetworkFile) return nil } @@ -161,7 +189,7 @@ func fetchAndSave(ds datasource.Datasource) error { userDataBytes = []byte{} } } else { - log.Errorf("Unrecognized user-data\n%s", userData) + log.Errorf("Unrecognized user-data\n(%s)", userData) userDataBytes = []byte{} } @@ -233,8 +261,8 @@ func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.D } func enableDoLinkLocal() { - err := netconf.ApplyNetworkConfigs(&rancherConfig.NetworkConfig{ - Interfaces: map[string]rancherConfig.InterfaceConfig{ + err := netconf.ApplyNetworkConfigs(&netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ "eth0": { IPV4LL: true, }, diff --git a/cmd/cloudinitsave/packet.go b/cmd/cloudinitsave/packet.go old mode 100644 new mode 100755 index d93eaf33..5fe8a71c --- a/cmd/cloudinitsave/packet.go +++ b/cmd/cloudinitsave/packet.go @@ -1,18 +1,8 @@ package cloudinitsave import ( - "bytes" - "fmt" - "net/http" - "os" - "path" - "strings" - "github.com/rancher/os/log" - yaml "github.com/cloudfoundry-incubator/candiedyaml" - - "github.com/packethost/packngo/metadata" "github.com/rancher/os/config" "github.com/rancher/os/netconf" ) @@ -34,71 +24,29 @@ func enablePacketNetwork(cfg *config.RancherConfig) { return } - c := metadata.NewClient(http.DefaultClient) - m, err := c.Metadata.Get() - if err != nil { - log.Errorf("Failed to get Packet metadata: %v", err) - return - } - - bondCfg := config.InterfaceConfig{ - Addresses: []string{}, - BondOpts: map[string]string{ - "lacp_rate": "1", - "xmit_hash_policy": "layer3+4", - "downdelay": "200", - "updelay": "200", - "miimon": "100", - "mode": "4", - }, - } - netCfg := config.NetworkConfig{ - Interfaces: map[string]config.InterfaceConfig{}, - } - for _, iface := range m.Network.Interfaces { - netCfg.Interfaces["mac="+iface.Mac] = config.InterfaceConfig{ - Bond: "bond0", - } - } - for _, addr := range m.Network.Addresses { - bondCfg.Addresses = append(bondCfg.Addresses, fmt.Sprintf("%s/%d", addr.Address, addr.Cidr)) - if addr.Gateway != "" { - if addr.AddressFamily == 4 { - if addr.Public { - bondCfg.Gateway = addr.Gateway - } - } else { - bondCfg.GatewayIpv6 = addr.Gateway + // Post to phone home URL on first boot + /* + // TODO: bring this back + if _, err = os.Stat(config.CloudConfigNetworkFile); err != nil { + if _, err = http.Post(m.PhoneHomeURL, "application/json", bytes.NewReader([]byte{})); err != nil { + log.Errorf("Failed to post to Packet phone home URL: %v", err) } } + */ - if addr.AddressFamily == 4 && strings.HasPrefix(addr.Gateway, "10.") { - bondCfg.PostUp = append(bondCfg.PostUp, "ip route add 10.0.0.0/8 via "+addr.Gateway) + /* + cc := config.CloudConfig{ + Rancher: config.RancherConfig{ + Network: netCfg, + }, } - } - netCfg.Interfaces["bond0"] = bondCfg - b, _ := yaml.Marshal(netCfg) - log.Debugf("Generated network config: %s", string(b)) - - cc := config.CloudConfig{ - Rancher: config.RancherConfig{ - Network: netCfg, - }, - } - - // Post to phone home URL on first boot - if _, err = os.Stat(config.CloudConfigNetworkFile); err != nil { - if _, err = http.Post(m.PhoneHomeURL, "application/json", bytes.NewReader([]byte{})); err != nil { - log.Errorf("Failed to post to Packet phone home URL: %v", err) + if err := os.MkdirAll(path.Dir(config.CloudConfigNetworkFile), 0700); err != nil { + log.Errorf("Failed to create directory for file %s: %v", config.CloudConfigNetworkFile, err) } - } - if err := os.MkdirAll(path.Dir(config.CloudConfigNetworkFile), 0700); err != nil { - log.Errorf("Failed to create directory for file %s: %v", config.CloudConfigNetworkFile, err) - } - - if err := config.WriteToFile(cc, config.CloudConfigNetworkFile); err != nil { - log.Errorf("Failed to save config file %s: %v", config.CloudConfigNetworkFile, err) - } + if err := config.WriteToFile(cc, config.CloudConfigNetworkFile); err != nil { + log.Errorf("Failed to save config file %s: %v", config.CloudConfigNetworkFile, err) + } + */ } diff --git a/cmd/network/network.go b/cmd/network/network.go index 34d6eeed..00289927 100644 --- a/cmd/network/network.go +++ b/cmd/network/network.go @@ -28,6 +28,7 @@ func ApplyNetworkConfig(cfg *config.CloudConfig) { search = cfg.Rancher.Defaults.Network.DNS.Search } + // TODO: don't write to the file if nameservers is still empty if _, err := resolvconf.Build("/etc/resolv.conf", nameservers, search, nil); err != nil { log.Error(err) } diff --git a/config/cloudinit/datasource/configdrive/configdrive.go b/config/cloudinit/datasource/configdrive/configdrive.go index cb92067b..a648a731 100755 --- a/config/cloudinit/datasource/configdrive/configdrive.go +++ b/config/cloudinit/datasource/configdrive/configdrive.go @@ -102,10 +102,11 @@ func (cd *ConfigDrive) FetchMetadata() (metadata datasource.Metadata, err error) metadata.SSHPublicKeys = m.SSHAuthorizedKeyMap metadata.Hostname = m.Hostname - if m.NetworkConfig.ContentPath != "" { - metadata.NetworkConfig, err = cd.tryReadFile(path.Join(cd.openstackRoot(), m.NetworkConfig.ContentPath)) - } - + // TODO: I don't think we've used this for anything + /* if m.NetworkConfig.ContentPath != "" { + metadata.NetworkConfig, err = cd.tryReadFile(path.Join(cd.openstackRoot(), m.NetworkConfig.ContentPath)) + } + */ return } diff --git a/config/cloudinit/datasource/configdrive/configdrive_test.go b/config/cloudinit/datasource/configdrive/configdrive_test.go index 73e7aa17..9b66b522 100755 --- a/config/cloudinit/datasource/configdrive/configdrive_test.go +++ b/config/cloudinit/datasource/configdrive/configdrive_test.go @@ -48,8 +48,7 @@ func TestFetchMetadata(t *testing.T) { test.File{Path: "/media/configdrive/openstack/config_file.json", Contents: "make it work"}, ), metadata: datasource.Metadata{ - Hostname: "host", - NetworkConfig: []byte("make it work"), + Hostname: "host", SSHPublicKeys: map[string]string{ "1": "key1", "2": "key2", diff --git a/config/cloudinit/datasource/datasource.go b/config/cloudinit/datasource/datasource.go index 363ee4a8..bd0cb70d 100755 --- a/config/cloudinit/datasource/datasource.go +++ b/config/cloudinit/datasource/datasource.go @@ -16,6 +16,8 @@ package datasource import ( "net" + + "github.com/rancher/os/netconf" ) type Datasource interface { @@ -31,11 +33,17 @@ type Datasource interface { } type Metadata struct { - PublicIPv4 net.IP - PublicIPv6 net.IP - PrivateIPv4 net.IP - PrivateIPv6 net.IP + // TODO: move to netconf/types.go ? + // see https://ahmetalpbalkan.com/blog/comparison-of-instance-metadata-services/ Hostname string SSHPublicKeys map[string]string - NetworkConfig interface{} + NetworkConfig netconf.NetworkConfig + + // probably unused, but its in the initialize.env code + // TODO: work out if there's any reason to keep it. + // Lets see if anyone notices when its not set. + PublicIPv4 net.IP + PublicIPv6 net.IP + PrivateIPv4 net.IP + PrivateIPv6 net.IP } diff --git a/config/cloudinit/datasource/metadata/digitalocean/metadata.go b/config/cloudinit/datasource/metadata/digitalocean/metadata.go index f8ca981c..045b1044 100755 --- a/config/cloudinit/datasource/metadata/digitalocean/metadata.go +++ b/config/cloudinit/datasource/metadata/digitalocean/metadata.go @@ -16,9 +16,13 @@ package digitalocean import ( "encoding/json" - "net" + "fmt" "strconv" + "github.com/rancher/os/netconf" + + "net" + "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource/metadata" ) @@ -99,12 +103,70 @@ func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err er metadata.PrivateIPv6 = net.ParseIP(m.Interfaces.Private[0].IPv6.IPAddress) } } + + metadata.NetworkConfig.Interfaces = make(map[string]netconf.InterfaceConfig) + + ethNumber := 0 + + for _, eth := range m.Interfaces.Public { + network := netconf.InterfaceConfig{} + + if eth.IPv4 != nil { + network.Gateway = eth.IPv4.Gateway + + network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.IPv4.IPAddress, eth.IPv4.Netmask)) + if metadata.PublicIPv4 == nil { + metadata.PublicIPv4 = net.ParseIP(eth.IPv4.IPAddress) + } + } + if eth.AnchorIPv4 != nil { + network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.AnchorIPv4.IPAddress, eth.AnchorIPv4.Netmask)) + + } + if eth.IPv6 != nil { + network.Addresses = append(network.Addresses, eth.IPv6.IPAddress) + network.GatewayIpv6 = eth.IPv6.Gateway + if metadata.PublicIPv6 == nil { + metadata.PublicIPv6 = net.ParseIP(eth.IPv6.IPAddress) + } + } + metadata.NetworkConfig.Interfaces[fmt.Sprintf("eth%d", ethNumber)] = network + ethNumber = ethNumber + 1 + } + + for _, eth := range m.Interfaces.Private { + network := netconf.InterfaceConfig{} + if eth.IPv4 != nil { + network.Gateway = eth.IPv4.Gateway + + network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.IPv4.IPAddress, eth.IPv4.Netmask)) + if metadata.PrivateIPv4 == nil { + metadata.PrivateIPv4 = net.ParseIP(eth.IPv6.IPAddress) + } + } + if eth.AnchorIPv4 != nil { + network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.AnchorIPv4.IPAddress, eth.AnchorIPv4.Netmask)) + + } + if eth.IPv6 != nil { + network.Address = eth.IPv6.IPAddress + network.GatewayIpv6 = eth.IPv6.Gateway + if metadata.PrivateIPv6 == nil { + metadata.PrivateIPv6 = net.ParseIP(eth.IPv6.IPAddress) + } + } + metadata.NetworkConfig.Interfaces[fmt.Sprintf("eth%d", ethNumber)] = network + ethNumber = ethNumber + 1 + } + + metadata.NetworkConfig.DNS.Nameservers = m.DNS.Nameservers + metadata.Hostname = m.Hostname metadata.SSHPublicKeys = map[string]string{} for i, key := range m.PublicKeys { metadata.SSHPublicKeys[strconv.Itoa(i)] = key } - metadata.NetworkConfig = m + // metadata.NetworkConfig = m return } diff --git a/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go b/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go old mode 100644 new mode 100755 index 1329967f..bc0d36fa --- a/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go +++ b/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go @@ -20,6 +20,8 @@ import ( "reflect" "testing" + "github.com/rancher/os/netconf" + "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata/test" @@ -90,26 +92,23 @@ func TestFetchMetadata(t *testing.T) { "0": "publickey1", "1": "publickey2", }, - NetworkConfig: Metadata{ - Interfaces: Interfaces{ - Public: []Interface{ - { - IPv4: &Address{ - IPAddress: "192.168.1.2", - Netmask: "255.255.255.0", - Gateway: "192.168.1.1", - }, - IPv6: &Address{ - IPAddress: "fe00::", - Cidr: 126, - Gateway: "fe00::", - }, - MAC: "ab:cd:ef:gh:ij", - Type: "public", + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Addresses: []string{ + "192.168.1.2/255.255.255.0", + "fe00::", }, + //Netmask: "255.255.255.0", + Gateway: "192.168.1.1", + + //Cidr: 126, + GatewayIpv6: "fe00::", + //MAC: "ab:cd:ef:gh:ij", + //Type: "public", }, }, - PublicKeys: []string{"publickey1", "publickey2"}, + //PublicKeys: []string{"publickey1", "publickey2"}, }, }, }, @@ -127,10 +126,10 @@ func TestFetchMetadata(t *testing.T) { } metadata, err := service.FetchMetadata() if Error(err) != Error(tt.expectErr) { - t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err) + t.Fatalf("bad error (%q): \nwant %#v,\n got %#v", tt.resources, tt.expectErr, err) } if !reflect.DeepEqual(tt.expect, metadata) { - t.Fatalf("bad fetch (%q): want %#q, got %#q", tt.resources, tt.expect, metadata) + t.Fatalf("bad fetch (%q): \nwant %#v,\n got %#v", tt.resources, tt.expect, metadata) } } } diff --git a/config/cloudinit/datasource/metadata/ec2/metadata.go b/config/cloudinit/datasource/metadata/ec2/metadata.go index 36be3f70..2ffef4df 100755 --- a/config/cloudinit/datasource/metadata/ec2/metadata.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata.go @@ -22,6 +22,8 @@ import ( "net" "strings" + "github.com/rancher/os/netconf" + "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/pkg" @@ -47,6 +49,7 @@ func NewDatasource(root string) *MetadataService { func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { metadata := datasource.Metadata{} + metadata.NetworkConfig = netconf.NetworkConfig{} if keynames, err := ms.fetchAttributes(fmt.Sprintf("%s/public-keys", ms.MetadataURL())); err == nil { keyIDs := make(map[string]string) @@ -77,18 +80,25 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { return metadata, err } + network := netconf.InterfaceConfig{} if localAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/local-ipv4", ms.MetadataURL())); err == nil { metadata.PrivateIPv4 = net.ParseIP(localAddr) + network.Addresses = append(network.Addresses, localAddr) + } else if _, ok := err.(pkg.ErrNotFound); !ok { return metadata, err } if publicAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/public-ipv4", ms.MetadataURL())); err == nil { metadata.PublicIPv4 = net.ParseIP(publicAddr) + network.Addresses = append(network.Addresses, publicAddr) } else if _, ok := err.(pkg.ErrNotFound); !ok { return metadata, err } + metadata.NetworkConfig.Interfaces = make(map[string]netconf.InterfaceConfig) + metadata.NetworkConfig.Interfaces["eth0"] = network + return metadata, nil } diff --git a/config/cloudinit/datasource/metadata/ec2/metadata_test.go b/config/cloudinit/datasource/metadata/ec2/metadata_test.go old mode 100644 new mode 100755 index 120fc769..e6784a95 --- a/config/cloudinit/datasource/metadata/ec2/metadata_test.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata_test.go @@ -24,6 +24,7 @@ import ( "github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata/test" "github.com/rancher/os/config/cloudinit/pkg" + "github.com/rancher/os/netconf" ) func TestType(t *testing.T) { @@ -174,6 +175,16 @@ func TestFetchMetadata(t *testing.T) { PrivateIPv4: net.ParseIP("1.2.3.4"), PublicIPv4: net.ParseIP("5.6.7.8"), SSHPublicKeys: map[string]string{"test1": "key"}, + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Addresses: []string{ + "1.2.3.4", + "5.6.7.8", + }, + }, + }, + }, }, }, { @@ -192,6 +203,16 @@ func TestFetchMetadata(t *testing.T) { PrivateIPv4: net.ParseIP("1.2.3.4"), PublicIPv4: net.ParseIP("5.6.7.8"), SSHPublicKeys: map[string]string{"test1": "key"}, + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Addresses: []string{ + "1.2.3.4", + "5.6.7.8", + }, + }, + }, + }, }, }, { diff --git a/config/cloudinit/datasource/metadata/gce/metadata.go b/config/cloudinit/datasource/metadata/gce/metadata.go index 2e54f7b5..b03f655b 100755 --- a/config/cloudinit/datasource/metadata/gce/metadata.go +++ b/config/cloudinit/datasource/metadata/gce/metadata.go @@ -21,6 +21,8 @@ import ( "strconv" "strings" + "github.com/rancher/os/netconf" + "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource/metadata" ) @@ -72,6 +74,22 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { SSHPublicKeys: nil, } + addresses := []string{} + if public != nil { + addresses = append(addresses, public.String()) + } + if local != nil { + addresses = append(addresses, local.String()) + } + if len(addresses) > 0 { + network := netconf.InterfaceConfig{ + Addresses: addresses, + } + + md.NetworkConfig.Interfaces = make(map[string]netconf.InterfaceConfig) + md.NetworkConfig.Interfaces["eth0"] = network + } + keyStrings := strings.Split(projectSSHKeys+"\n"+instanceSSHKeys, "\n") i := 0 diff --git a/config/cloudinit/datasource/metadata/gce/metadata_test.go b/config/cloudinit/datasource/metadata/gce/metadata_test.go index 4e88ec40..156fa7f2 100755 --- a/config/cloudinit/datasource/metadata/gce/metadata_test.go +++ b/config/cloudinit/datasource/metadata/gce/metadata_test.go @@ -20,6 +20,8 @@ import ( "reflect" "testing" + "github.com/rancher/os/netconf" + "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata/test" @@ -73,6 +75,16 @@ func TestFetchMetadata(t *testing.T) { Hostname: "host", PrivateIPv4: net.ParseIP("1.2.3.4"), PublicIPv4: net.ParseIP("5.6.7.8"), + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Addresses: []string{ + "5.6.7.8", + "1.2.3.4", + }, + }, + }, + }, }, }, { diff --git a/config/cloudinit/datasource/metadata/metadata.go b/config/cloudinit/datasource/metadata/metadata.go index 6d513309..37bf3944 100755 --- a/config/cloudinit/datasource/metadata/metadata.go +++ b/config/cloudinit/datasource/metadata/metadata.go @@ -20,6 +20,7 @@ import ( "strings" "github.com/rancher/os/config/cloudinit/pkg" + "github.com/rancher/os/log" ) type Service struct { @@ -40,6 +41,9 @@ func NewDatasource(root, apiVersion, userdataPath, metadataPath string, header h func (ms Service) IsAvailable() bool { _, ms.lastError = ms.Client.Get(ms.Root + ms.APIVersion) + if ms.lastError != nil { + log.Errorf("%s: %s (lastError: %s)", "IsAvailable", ms.Root+":"+ms.UserdataPath, ms.lastError) + } return (ms.lastError == nil) } @@ -48,7 +52,7 @@ func (ms *Service) Finish() error { } func (ms *Service) String() string { - return fmt.Sprintf("%s: %s (lastError: %s)", "metadata", ms.Root+":"+ms.UserdataPath, ms.lastError) + return fmt.Sprintf("%s: %s (lastError: %s)", "metadata", ms.Root+ms.UserdataPath, ms.lastError) } func (ms Service) AvailabilityChanges() bool { diff --git a/config/cloudinit/datasource/metadata/packet/metadata.go b/config/cloudinit/datasource/metadata/packet/metadata.go index b00dad57..0ec92377 100755 --- a/config/cloudinit/datasource/metadata/packet/metadata.go +++ b/config/cloudinit/datasource/metadata/packet/metadata.go @@ -15,12 +15,18 @@ package packet import ( - "encoding/json" - "net" + "fmt" + "net/http" "strconv" + "strings" "github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource/metadata" + "github.com/rancher/os/log" + "github.com/rancher/os/netconf" + + yaml "github.com/cloudfoundry-incubator/candiedyaml" + packetMetadata "github.com/packethost/packngo/metadata" ) const ( @@ -30,33 +36,6 @@ const ( metadataPath = "metadata" ) -type Netblock struct { - Address net.IP `json:"address"` - Cidr int `json:"cidr"` - Netmask net.IP `json:"netmask"` - Gateway net.IP `json:"gateway"` - AddressFamily int `json:"address_family"` - Public bool `json:"public"` -} - -type Nic struct { - Name string `json:"name"` - Mac string `json:"mac"` -} - -type NetworkData struct { - Interfaces []Nic `json:"interfaces"` - Netblocks []Netblock `json:"addresses"` - DNS []net.IP `json:"dns"` -} - -// Metadata that will be pulled from the https://metadata.packet.net/metadata only. We have the opportunity to add more later. -type Metadata struct { - Hostname string `json:"hostname"` - SSHKeys []string `json:"ssh_keys"` - NetworkData NetworkData `json:"network"` -} - type MetadataService struct { metadata.Service } @@ -70,37 +49,85 @@ func NewDatasource(root string) *MetadataService { } func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err error) { - var data []byte - var m Metadata - - if data, err = ms.FetchData(ms.MetadataURL()); err != nil || len(data) == 0 { + c := packetMetadata.NewClient(http.DefaultClient) + m, err := c.Metadata.Get() + if err != nil { + log.Errorf("Failed to get Packet metadata: %v", err) return } - if err = json.Unmarshal(data, &m); err != nil { - return + bondCfg := netconf.InterfaceConfig{ + Addresses: []string{}, + BondOpts: map[string]string{ + "lacp_rate": "1", + "xmit_hash_policy": "layer3+4", + "downdelay": "200", + "updelay": "200", + "miimon": "100", + "mode": "4", + }, } - - if len(m.NetworkData.Netblocks) > 0 { - for _, Netblock := range m.NetworkData.Netblocks { - if Netblock.AddressFamily == 4 { - if Netblock.Public == true { - metadata.PublicIPv4 = Netblock.Address - } else { - metadata.PrivateIPv4 = Netblock.Address - } - } else { - metadata.PublicIPv6 = Netblock.Address - } + netCfg := netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{}, + } + for _, iface := range m.Network.Interfaces { + netCfg.Interfaces["mac="+iface.Mac] = netconf.InterfaceConfig{ + Bond: "bond0", } } + for _, addr := range m.Network.Addresses { + bondCfg.Addresses = append(bondCfg.Addresses, fmt.Sprintf("%s/%d", addr.Address, addr.Cidr)) + if addr.Gateway != "" { + if addr.AddressFamily == 4 { + if addr.Public { + bondCfg.Gateway = addr.Gateway + } + } else { + bondCfg.GatewayIpv6 = addr.Gateway + } + } + + if addr.AddressFamily == 4 && strings.HasPrefix(addr.Gateway, "10.") { + bondCfg.PostUp = append(bondCfg.PostUp, "ip route add 10.0.0.0/8 via "+addr.Gateway) + } + } + + netCfg.Interfaces["bond0"] = bondCfg + b, _ := yaml.Marshal(netCfg) + log.Debugf("Generated network config: %s", string(b)) + + // the old code var data []byte + /* var m Metadata + + if data, err = ms.FetchData(ms.MetadataURL()); err != nil || len(data) == 0 { + return + } + + if err = json.Unmarshal(data, &m); err != nil { + return + } + + if len(m.NetworkData.Netblocks) > 0 { + for _, Netblock := range m.NetworkData.Netblocks { + if Netblock.AddressFamily == 4 { + if Netblock.Public == true { + metadata.PublicIPv4 = Netblock.Address + } else { + metadata.PrivateIPv4 = Netblock.Address + } + } else { + metadata.PublicIPv6 = Netblock.Address + } + } + } + */ metadata.Hostname = m.Hostname metadata.SSHPublicKeys = map[string]string{} - for i, key := range m.SSHKeys { + for i, key := range m.SshKeys { metadata.SSHPublicKeys[strconv.Itoa(i)] = key } - metadata.NetworkConfig = m.NetworkData + metadata.NetworkConfig = netCfg return } diff --git a/config/cloudinit/datasource/vmware/vmware.go b/config/cloudinit/datasource/vmware/vmware.go index f1593aab..c68fe389 100755 --- a/config/cloudinit/datasource/vmware/vmware.go +++ b/config/cloudinit/datasource/vmware/vmware.go @@ -125,7 +125,7 @@ func (v VMWare) FetchMetadata() (metadata datasource.Metadata, err error) { } } } - metadata.NetworkConfig = netconf + // metadata.NetworkConfig = netconf return } diff --git a/config/cloudinit/datasource/vmware/vmware_test.go b/config/cloudinit/datasource/vmware/vmware_test.go index be4af511..44d85f72 100644 --- a/config/cloudinit/datasource/vmware/vmware_test.go +++ b/config/cloudinit/datasource/vmware/vmware_test.go @@ -56,10 +56,10 @@ func TestFetchMetadata(t *testing.T) { "interface.0.dhcp": "yes", }, metadata: datasource.Metadata{ - NetworkConfig: map[string]string{ - "interface.0.mac": "test mac", - "interface.0.dhcp": "yes", - }, + // NetworkConfig: map[string]string{ + // "interface.0.mac": "test mac", + // "interface.0.dhcp": "yes", + // }, }, }, { @@ -68,10 +68,10 @@ func TestFetchMetadata(t *testing.T) { "interface.0.dhcp": "yes", }, metadata: datasource.Metadata{ - NetworkConfig: map[string]string{ - "interface.0.name": "test name", - "interface.0.dhcp": "yes", - }, + // NetworkConfig: map[string]string{ + // "interface.0.name": "test name", + // "interface.0.dhcp": "yes", + // }, }, }, { @@ -86,12 +86,12 @@ func TestFetchMetadata(t *testing.T) { metadata: datasource.Metadata{ Hostname: "test host", PrivateIPv6: net.ParseIP("fe00::100"), - NetworkConfig: map[string]string{ - "interface.0.mac": "test mac", - "interface.0.ip.0.address": "fe00::100/64", - "interface.0.route.0.gateway": "fe00::1", - "interface.0.route.0.destination": "::", - }, + // NetworkConfig: map[string]string{ + // "interface.0.mac": "test mac", + // "interface.0.ip.0.address": "fe00::100/64", + // "interface.0.route.0.gateway": "fe00::1", + // "interface.0.route.0.destination": "::", + // }, }, }, { @@ -113,17 +113,17 @@ func TestFetchMetadata(t *testing.T) { Hostname: "test host", PublicIPv4: net.ParseIP("10.0.0.101"), PrivateIPv4: net.ParseIP("10.0.0.102"), - NetworkConfig: map[string]string{ - "interface.0.name": "test name", - "interface.0.ip.0.address": "10.0.0.100/24", - "interface.0.ip.1.address": "10.0.0.101/24", - "interface.0.route.0.gateway": "10.0.0.1", - "interface.0.route.0.destination": "0.0.0.0", - "interface.1.mac": "test mac", - "interface.1.route.0.gateway": "10.0.0.2", - "interface.1.route.0.destination": "0.0.0.0", - "interface.1.ip.0.address": "10.0.0.102/24", - }, + // NetworkConfig: map[string]string{ + // "interface.0.name": "test name", + // "interface.0.ip.0.address": "10.0.0.100/24", + // "interface.0.ip.1.address": "10.0.0.101/24", + // "interface.0.route.0.gateway": "10.0.0.1", + // "interface.0.route.0.destination": "0.0.0.0", + // "interface.1.mac": "test mac", + // "interface.1.route.0.gateway": "10.0.0.2", + // "interface.1.route.0.destination": "0.0.0.0", + // "interface.1.ip.0.address": "10.0.0.102/24", + // }, }, }, } @@ -257,17 +257,17 @@ func TestOvfTransport(t *testing.T) { Hostname: "test host", PublicIPv4: net.ParseIP("10.0.0.101"), PrivateIPv4: net.ParseIP("10.0.0.102"), - NetworkConfig: map[string]string{ - "interface.0.name": "test name", - "interface.0.ip.0.address": "10.0.0.100/24", - "interface.0.ip.1.address": "10.0.0.101/24", - "interface.0.route.0.gateway": "10.0.0.1", - "interface.0.route.0.destination": "0.0.0.0", - "interface.1.mac": "test mac", - "interface.1.route.0.gateway": "10.0.0.2", - "interface.1.route.0.destination": "0.0.0.0", - "interface.1.ip.0.address": "10.0.0.102/24", - }, + //NetworkConfig: map[string]string{ + // "interface.0.name": "test name", + // "interface.0.ip.0.address": "10.0.0.100/24", + // "interface.0.ip.1.address": "10.0.0.101/24", + // "interface.0.route.0.gateway": "10.0.0.1", + // "interface.0.route.0.destination": "0.0.0.0", + // "interface.1.mac": "test mac", + // "interface.1.route.0.gateway": "10.0.0.2", + // "interface.1.route.0.destination": "0.0.0.0", + // "interface.1.ip.0.address": "10.0.0.102/24", + // }, }, userdata: []byte("test config"), }, diff --git a/config/cloudinit/network/packet.go b/config/cloudinit/network/packet.go old mode 100644 new mode 100755 index f8699532..c786fe90 --- a/config/cloudinit/network/packet.go +++ b/config/cloudinit/network/packet.go @@ -17,14 +17,15 @@ package network import ( "net" - "github.com/rancher/os/config/cloudinit/datasource/metadata/packet" + "github.com/rancher/os/netconf" ) -func ProcessPacketNetconf(netdata packet.NetworkData) ([]InterfaceGenerator, error) { +func ProcessPacketNetconf(netdata netconf.NetworkConfig) ([]InterfaceGenerator, error) { var nameservers []net.IP - if netdata.DNS != nil { - nameservers = netdata.DNS - } else { + for _, v := range netdata.DNS.Nameservers { + nameservers = append(nameservers, net.ParseIP(v)) + } + if len(nameservers) == 0 { nameservers = append(nameservers, net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")) } @@ -36,43 +37,44 @@ func ProcessPacketNetconf(netdata packet.NetworkData) ([]InterfaceGenerator, err return generators, nil } -func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]InterfaceGenerator, error) { +func parseNetwork(netdata netconf.NetworkConfig, nameservers []net.IP) ([]InterfaceGenerator, error) { var interfaces []InterfaceGenerator var addresses []net.IPNet var routes []route - for _, netblock := range netdata.Netblocks { - addresses = append(addresses, net.IPNet{ - IP: netblock.Address, - Mask: net.IPMask(netblock.Netmask), - }) - if netblock.Public == false { - routes = append(routes, route{ - destination: net.IPNet{ - IP: net.IPv4(10, 0, 0, 0), - Mask: net.IPv4Mask(255, 0, 0, 0), - }, - gateway: netblock.Gateway, + /* for _, netblock := range netdata.Netblocks { + addresses = append(addresses, net.IPNet{ + IP: netblock.Address, + Mask: net.IPMask(netblock.Netmask), }) - } else { - if netblock.AddressFamily == 4 { + if netblock.Public == false { routes = append(routes, route{ destination: net.IPNet{ - IP: net.IPv4zero, - Mask: net.IPMask(net.IPv4zero), + IP: net.IPv4(10, 0, 0, 0), + Mask: net.IPv4Mask(255, 0, 0, 0), }, gateway: netblock.Gateway, }) } else { - routes = append(routes, route{ - destination: net.IPNet{ - IP: net.IPv6zero, - Mask: net.IPMask(net.IPv6zero), - }, - gateway: netblock.Gateway, - }) + if netblock.AddressFamily == 4 { + routes = append(routes, route{ + destination: net.IPNet{ + IP: net.IPv4zero, + Mask: net.IPMask(net.IPv4zero), + }, + gateway: netblock.Gateway, + }) + } else { + routes = append(routes, route{ + destination: net.IPNet{ + IP: net.IPv6zero, + Mask: net.IPMask(net.IPv6zero), + }, + gateway: netblock.Gateway, + }) + } } } - } + */ bond := bondInterface{ logicalInterface: logicalInterface{ @@ -92,14 +94,15 @@ func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]Interface }, } - bond.hwaddr, _ = net.ParseMAC(netdata.Interfaces[0].Mac) + //bond.hwaddr, _ = net.ParseMAC(netdata.Interfaces[0].Mac) - for index, iface := range netdata.Interfaces { - bond.slaves = append(bond.slaves, iface.Name) + index := 0 + for name := range netdata.Interfaces { + bond.slaves = append(bond.slaves, name) interfaces = append(interfaces, &physicalInterface{ logicalInterface: logicalInterface{ - name: iface.Name, + name: name, config: configMethodStatic{ nameservers: nameservers, }, @@ -107,6 +110,7 @@ func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]Interface configDepth: index, }, }) + index = index + 1 } interfaces = append(interfaces, &bond) diff --git a/config/disk.go b/config/disk.go index 3e38de1a..2bc1e1a2 100644 --- a/config/disk.go +++ b/config/disk.go @@ -317,6 +317,7 @@ func readConfigFile(file string) ([]byte, error) { } func substituteVars(userDataBytes []byte, metadata datasource.Metadata) []byte { + // TODO: I think this currently does nothing - its hardcoded for COREOS env.. env := initialize.NewEnvironment("", "", "", "", metadata) userData := env.Apply(string(userDataBytes)) diff --git a/config/types.go b/config/types.go old mode 100644 new mode 100755 index 46ed2660..a1064df5 --- a/config/types.go +++ b/config/types.go @@ -8,6 +8,7 @@ import ( composeConfig "github.com/docker/libcompose/config" "github.com/rancher/os/config/cloudinit/config" "github.com/rancher/os/config/yaml" + "github.com/rancher/os/netconf" ) const ( @@ -113,8 +114,8 @@ type RancherConfig struct { Disable []string `yaml:"disable,omitempty"` ServicesInclude map[string]bool `yaml:"services_include,omitempty"` Modules []string `yaml:"modules,omitempty"` - Network NetworkConfig `yaml:"network,omitempty"` - DefaultNetwork NetworkConfig `yaml:"default_network,omitempty"` + Network netconf.NetworkConfig `yaml:"network,omitempty"` + DefaultNetwork netconf.NetworkConfig `yaml:"default_network,omitempty"` Repositories Repositories `yaml:"repositories,omitempty"` SSH SSHConfig `yaml:"ssh,omitempty"` State StateConfig `yaml:"state,omitempty"` @@ -170,39 +171,6 @@ type DockerConfig struct { Exec bool `yaml:"exec,omitempty"` } -type NetworkConfig struct { - PreCmds []string `yaml:"pre_cmds,omitempty"` - DNS DNSConfig `yaml:"dns,omitempty"` - Interfaces map[string]InterfaceConfig `yaml:"interfaces,omitempty"` - PostCmds []string `yaml:"post_cmds,omitempty"` - HTTPProxy string `yaml:"http_proxy,omitempty"` - HTTPSProxy string `yaml:"https_proxy,omitempty"` - NoProxy string `yaml:"no_proxy,omitempty"` -} - -type InterfaceConfig struct { - Match string `yaml:"match,omitempty"` - DHCP bool `yaml:"dhcp,omitempty"` - DHCPArgs string `yaml:"dhcp_args,omitempty"` - Address string `yaml:"address,omitempty"` - Addresses []string `yaml:"addresses,omitempty"` - IPV4LL bool `yaml:"ipv4ll,omitempty"` - Gateway string `yaml:"gateway,omitempty"` - GatewayIpv6 string `yaml:"gateway_ipv6,omitempty"` - MTU int `yaml:"mtu,omitempty"` - Bridge string `yaml:"bridge,omitempty"` - Bond string `yaml:"bond,omitempty"` - BondOpts map[string]string `yaml:"bond_opts,omitempty"` - PostUp []string `yaml:"post_up,omitempty"` - PreUp []string `yaml:"pre_up,omitempty"` - Vlans string `yaml:"vlans,omitempty"` -} - -type DNSConfig struct { - Nameservers []string `yaml:"nameservers,flow,omitempty"` - Search []string `yaml:"search,flow,omitempty"` -} - type SSHConfig struct { Keys map[string]string `yaml:"keys,omitempty"` } @@ -225,9 +193,9 @@ type CloudInit struct { } type Defaults struct { - Hostname string `yaml:"hostname,omitempty"` - Docker DockerConfig `yaml:"docker,omitempty"` - Network NetworkConfig `yaml:"network,omitempty"` + Hostname string `yaml:"hostname,omitempty"` + Docker DockerConfig `yaml:"docker,omitempty"` + Network netconf.NetworkConfig `yaml:"network,omitempty"` } func (r Repositories) ToArray() []string { diff --git a/dfs/scratch.go b/dfs/scratch.go index 7ba93acb..1881d92e 100644 --- a/dfs/scratch.go +++ b/dfs/scratch.go @@ -12,7 +12,6 @@ import ( "syscall" "github.com/docker/libnetwork/resolvconf" - "github.com/rancher/os/config" "github.com/rancher/os/log" "github.com/rancher/os/netconf" "github.com/rancher/os/selinux" @@ -46,7 +45,7 @@ type Config struct { Fork bool PidOne bool CommandName string - DNSConfig config.DNSConfig + DNSConfig netconf.DNSConfig BridgeName string BridgeAddress string BridgeMtu int @@ -359,8 +358,8 @@ ff02::2 ip6-allrouters if cfg.BridgeName != "" && cfg.BridgeName != "none" { log.Debugf("Creating bridge %s (%s)", cfg.BridgeName, cfg.BridgeAddress) - if err := netconf.ApplyNetworkConfigs(&config.NetworkConfig{ - Interfaces: map[string]config.InterfaceConfig{ + if err := netconf.ApplyNetworkConfigs(&netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ cfg.BridgeName: { Address: cfg.BridgeAddress, MTU: cfg.BridgeMtu, diff --git a/netconf/netconf_linux.go b/netconf/netconf_linux.go index 31474cd7..bc6951db 100644 --- a/netconf/netconf_linux.go +++ b/netconf/netconf_linux.go @@ -13,7 +13,6 @@ import ( "github.com/flynn/go-shlex" "github.com/rancher/os/log" - "github.com/rancher/os/config" "github.com/ryanuber/go-glob" "github.com/vishvananda/netlink" ) @@ -27,7 +26,7 @@ var ( defaultDhcpArgs = []string{"dhcpcd", "-MA4"} ) -func createInterfaces(netCfg *config.NetworkConfig) { +func createInterfaces(netCfg *NetworkConfig) { configured := map[string]bool{} for name, iface := range netCfg.Interfaces { @@ -65,7 +64,7 @@ func createInterfaces(netCfg *config.NetworkConfig) { } } -func createSlaveInterfaces(netCfg *config.NetworkConfig) { +func createSlaveInterfaces(netCfg *NetworkConfig) { links, err := netlink.LinkList() if err != nil { log.Errorf("Failed to list links: %v", err) @@ -92,9 +91,9 @@ func createSlaveInterfaces(netCfg *config.NetworkConfig) { } } -func findMatch(link netlink.Link, netCfg *config.NetworkConfig) (config.InterfaceConfig, bool) { +func findMatch(link netlink.Link, netCfg *NetworkConfig) (InterfaceConfig, bool) { linkName := link.Attrs().Name - var match config.InterfaceConfig + var match InterfaceConfig exactMatch := false found := false @@ -136,25 +135,25 @@ func findMatch(link netlink.Link, netCfg *config.NetworkConfig) (config.Interfac return match, exactMatch || found } -func populateDefault(netCfg *config.NetworkConfig) { +func populateDefault(netCfg *NetworkConfig) { if netCfg.Interfaces == nil { - netCfg.Interfaces = map[string]config.InterfaceConfig{} + netCfg.Interfaces = map[string]InterfaceConfig{} } if len(netCfg.Interfaces) == 0 { - netCfg.Interfaces["eth*"] = config.InterfaceConfig{ + netCfg.Interfaces["eth*"] = InterfaceConfig{ DHCP: true, } } if _, ok := netCfg.Interfaces["lo"]; !ok { - netCfg.Interfaces["lo"] = config.InterfaceConfig{ + netCfg.Interfaces["lo"] = InterfaceConfig{ Address: "127.0.0.1/8", } } } -func ApplyNetworkConfigs(netCfg *config.NetworkConfig) error { +func ApplyNetworkConfigs(netCfg *NetworkConfig) error { populateDefault(netCfg) log.Debugf("Config: %#v", netCfg) @@ -183,7 +182,7 @@ func ApplyNetworkConfigs(netCfg *config.NetworkConfig) error { return err } -func RunDhcp(netCfg *config.NetworkConfig, setHostname, setDNS bool) error { +func RunDhcp(netCfg *NetworkConfig, setHostname, setDNS bool) error { populateDefault(netCfg) links, err := netlink.LinkList() @@ -212,7 +211,7 @@ func RunDhcp(netCfg *config.NetworkConfig, setHostname, setDNS bool) error { return err } -func runDhcp(netCfg *config.NetworkConfig, iface string, argstr string, setHostname, setDNS bool) { +func runDhcp(netCfg *NetworkConfig, iface string, argstr string, setHostname, setDNS bool) { log.Infof("Running DHCP on %s", iface) args := []string{} if argstr != "" { @@ -243,7 +242,7 @@ func runDhcp(netCfg *config.NetworkConfig, iface string, argstr string, setHostn } } -func linkUp(link netlink.Link, netConf config.InterfaceConfig) error { +func linkUp(link netlink.Link, netConf InterfaceConfig) error { if err := netlink.LinkSetUp(link); err != nil { log.Errorf("failed to setup link: %v", err) return err @@ -252,7 +251,7 @@ func linkUp(link netlink.Link, netConf config.InterfaceConfig) error { return nil } -func applyAddress(address string, link netlink.Link, netConf config.InterfaceConfig) error { +func applyAddress(address string, link netlink.Link, netConf InterfaceConfig) error { addr, err := netlink.ParseAddr(address) if err != nil { return err @@ -294,7 +293,7 @@ func setGateway(gateway string) error { return nil } -func applyInterfaceConfig(link netlink.Link, netConf config.InterfaceConfig) error { +func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { if netConf.Bond != "" { if err := netlink.LinkSetDown(link); err != nil { return err diff --git a/netconf/types.go b/netconf/types.go new file mode 100755 index 00000000..f99faeba --- /dev/null +++ b/netconf/types.go @@ -0,0 +1,34 @@ +package netconf + +type NetworkConfig struct { + PreCmds []string `yaml:"pre_cmds,omitempty"` + DNS DNSConfig `yaml:"dns,omitempty"` + Interfaces map[string]InterfaceConfig `yaml:"interfaces,omitempty"` + PostCmds []string `yaml:"post_cmds,omitempty"` + HTTPProxy string `yaml:"http_proxy,omitempty"` + HTTPSProxy string `yaml:"https_proxy,omitempty"` + NoProxy string `yaml:"no_proxy,omitempty"` +} + +type InterfaceConfig struct { + Match string `yaml:"match,omitempty"` + DHCP bool `yaml:"dhcp,omitempty"` + DHCPArgs string `yaml:"dhcp_args,omitempty"` + Address string `yaml:"address,omitempty"` + Addresses []string `yaml:"addresses,omitempty"` + IPV4LL bool `yaml:"ipv4ll,omitempty"` + Gateway string `yaml:"gateway,omitempty"` + GatewayIpv6 string `yaml:"gateway_ipv6,omitempty"` + MTU int `yaml:"mtu,omitempty"` + Bridge string `yaml:"bridge,omitempty"` + Bond string `yaml:"bond,omitempty"` + BondOpts map[string]string `yaml:"bond_opts,omitempty"` + PostUp []string `yaml:"post_up,omitempty"` + PreUp []string `yaml:"pre_up,omitempty"` + Vlans string `yaml:"vlans,omitempty"` +} + +type DNSConfig struct { + Nameservers []string `yaml:"nameservers,flow,omitempty"` + Search []string `yaml:"search,flow,omitempty"` +} diff --git a/scripts/ros b/scripts/ros new file mode 100755 index 00000000..d6b30e23 --- /dev/null +++ b/scripts/ros @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +if [ ! -e "./build/initrd/usr/share/ros/os-config.yml" ]; then + ./.dapper release +else + echo "using existing build of ros" +# ./.dapper build-target +fi + +source ./scripts/version + +echo "---------------------------------" +echo "ln -s /usr/bin/ros /usr/bin/cloud-init-save" +echo "ros config set rancher.cloud_init.datasources [packet:https://metadata.packet.net/]" +#echo "ros config set rancher.cloud_init.datasources [packet:https://192.80.8.124/]" +echo "cloud-init-save" +echo "---------------------------------" + +docker run --rm -it \ + -v $(pwd)/build/initrd/usr/share:/usr/share \ + -v $(pwd)/bin/ros:/usr/bin/ros \ + -v /etc/ssl/certs:/etc/ssl/certs \ + -v /usr/share/ca-certificates:/usr/share/ca-certificates \ + -w /var/lib/rancher \ + --entrypoint sh \ + rancher/os-base:v0.8.1 From b5fdd63a85ea3108660af5654563c4db30f7296b Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Wed, 8 Mar 2017 22:03:08 +1000 Subject: [PATCH 03/13] fix the ip/cidr, and call updateNetwork Signed-off-by: Sven Dowideit --- cmd/cloudinitsave/cloudinitsave.go | 5 +++ .../metadata/digitalocean/metadata.go | 32 +++++++++++++------ .../metadata/digitalocean/metadata_test.go | 4 +-- .../datasource/metadata/packet/metadata.go | 7 ++++ config/cloudinit/network/packet.go | 1 + 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/cmd/cloudinitsave/cloudinitsave.go b/cmd/cloudinitsave/cloudinitsave.go index 5e7593b3..71415b6b 100755 --- a/cmd/cloudinitsave/cloudinitsave.go +++ b/cmd/cloudinitsave/cloudinitsave.go @@ -65,6 +65,11 @@ func Main() { if err := SaveCloudConfig(true); err != nil { log.Errorf("Failed to save cloud-config: %v", err) } + + // Apply any newly detected network config. + //consider putting this in a separate init phase... + cfg = rancherConfig.LoadConfig() + network.ApplyNetworkConfig(cfg) } func SaveCloudConfig(network bool) error { diff --git a/config/cloudinit/datasource/metadata/digitalocean/metadata.go b/config/cloudinit/datasource/metadata/digitalocean/metadata.go index 045b1044..cc55c899 100755 --- a/config/cloudinit/datasource/metadata/digitalocean/metadata.go +++ b/config/cloudinit/datasource/metadata/digitalocean/metadata.go @@ -76,6 +76,22 @@ func NewDatasource(root string) *MetadataService { return &MetadataService{Service: metadata.NewDatasource(root, apiVersion, userdataURL, metadataPath, nil)} } +// Parse IPv4 netmask written in IP form (e.g. "255.255.255.0"). +func ipmask(addr *Address) string { + ip := net.ParseIP(addr.IPAddress) + var mask net.IPMask + if addr.Netmask != "" { + mask = net.IPMask(net.ParseIP(addr.Netmask)) + } else { + mask = net.CIDRMask(addr.Cidr, 32) + } + ipnet := net.IPNet{ + IP: ip, + Mask: mask, + } + return ipnet.String() +} + func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err error) { var data []byte var m Metadata @@ -114,17 +130,16 @@ func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err er if eth.IPv4 != nil { network.Gateway = eth.IPv4.Gateway - network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.IPv4.IPAddress, eth.IPv4.Netmask)) + network.Addresses = append(network.Addresses, ipmask(eth.IPv4)) if metadata.PublicIPv4 == nil { metadata.PublicIPv4 = net.ParseIP(eth.IPv4.IPAddress) } } if eth.AnchorIPv4 != nil { - network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.AnchorIPv4.IPAddress, eth.AnchorIPv4.Netmask)) - + network.Addresses = append(network.Addresses, ipmask(eth.AnchorIPv4)) } if eth.IPv6 != nil { - network.Addresses = append(network.Addresses, eth.IPv6.IPAddress) + network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%d", eth.IPv6.IPAddress, eth.IPv6.Cidr)) network.GatewayIpv6 = eth.IPv6.Gateway if metadata.PublicIPv6 == nil { metadata.PublicIPv6 = net.ParseIP(eth.IPv6.IPAddress) @@ -139,17 +154,17 @@ func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err er if eth.IPv4 != nil { network.Gateway = eth.IPv4.Gateway - network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.IPv4.IPAddress, eth.IPv4.Netmask)) + network.Addresses = append(network.Addresses, ipmask(eth.IPv4)) + if metadata.PrivateIPv4 == nil { metadata.PrivateIPv4 = net.ParseIP(eth.IPv6.IPAddress) } } if eth.AnchorIPv4 != nil { - network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%s", eth.AnchorIPv4.IPAddress, eth.AnchorIPv4.Netmask)) - + network.Addresses = append(network.Addresses, ipmask(eth.AnchorIPv4)) } if eth.IPv6 != nil { - network.Address = eth.IPv6.IPAddress + network.Addresses = append(network.Addresses, fmt.Sprintf("%s/%d", eth.IPv6.IPAddress, eth.IPv6.Cidr)) network.GatewayIpv6 = eth.IPv6.Gateway if metadata.PrivateIPv6 == nil { metadata.PrivateIPv6 = net.ParseIP(eth.IPv6.IPAddress) @@ -166,7 +181,6 @@ func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err er for i, key := range m.PublicKeys { metadata.SSHPublicKeys[strconv.Itoa(i)] = key } - // metadata.NetworkConfig = m return } diff --git a/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go b/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go index bc0d36fa..335f1a85 100755 --- a/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go +++ b/config/cloudinit/datasource/metadata/digitalocean/metadata_test.go @@ -96,8 +96,8 @@ func TestFetchMetadata(t *testing.T) { Interfaces: map[string]netconf.InterfaceConfig{ "eth0": netconf.InterfaceConfig{ Addresses: []string{ - "192.168.1.2/255.255.255.0", - "fe00::", + "192.168.1.2/24", + "fe00::/126", }, //Netmask: "255.255.255.0", Gateway: "192.168.1.1", diff --git a/config/cloudinit/datasource/metadata/packet/metadata.go b/config/cloudinit/datasource/metadata/packet/metadata.go index 0ec92377..c246d494 100755 --- a/config/cloudinit/datasource/metadata/packet/metadata.go +++ b/config/cloudinit/datasource/metadata/packet/metadata.go @@ -15,6 +15,7 @@ package packet import ( + "bytes" "fmt" "net/http" "strconv" @@ -129,6 +130,12 @@ func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err er metadata.NetworkConfig = netCfg + // This is not really the right place - perhaps we should add a call-home function in each datasource to be called after the network is applied + //(see the original in cmd/cloudsave/packet) + if _, err = http.Post(m.PhoneHomeURL, "application/json", bytes.NewReader([]byte{})); err != nil { + log.Errorf("Failed to post to Packet phone home URL: %v", err) + } + return } diff --git a/config/cloudinit/network/packet.go b/config/cloudinit/network/packet.go index c786fe90..8bed1888 100755 --- a/config/cloudinit/network/packet.go +++ b/config/cloudinit/network/packet.go @@ -41,6 +41,7 @@ func parseNetwork(netdata netconf.NetworkConfig, nameservers []net.IP) ([]Interf var interfaces []InterfaceGenerator var addresses []net.IPNet var routes []route + // TODO: commented out because we don't use it - should combine with the code we do use... /* for _, netblock := range netdata.Netblocks { addresses = append(addresses, net.IPNet{ IP: netblock.Address, From e4c2271c6bb584667b16f11cdda055d5a0f2b1f2 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Wed, 8 Mar 2017 15:57:58 -0700 Subject: [PATCH 04/13] Don't make IPv4LL and Addresses mutally exclusive --- netconf/netconf_linux.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/netconf/netconf_linux.go b/netconf/netconf_linux.go index bc6951db..8fa5b360 100644 --- a/netconf/netconf_linux.go +++ b/netconf/netconf_linux.go @@ -325,22 +325,22 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { log.Errorf("IPV4LL set failed: %v", err) return err } - } else { - addresses := []string{} + } - if netConf.Address != "" { - addresses = append(addresses, netConf.Address) - } + addresses := []string{} - if len(netConf.Addresses) > 0 { - addresses = append(addresses, netConf.Addresses...) - } + if netConf.Address != "" { + addresses = append(addresses, netConf.Address) + } - for _, address := range addresses { - err := applyAddress(address, link, netConf) - if err != nil { - log.Errorf("Failed to apply address %s to %s: %v", address, link.Attrs().Name, err) - } + if len(netConf.Addresses) > 0 { + addresses = append(addresses, netConf.Addresses...) + } + + for _, address := range addresses { + err := applyAddress(address, link, netConf) + if err != nil { + log.Errorf("Failed to apply address %s to %s: %v", address, link.Attrs().Name, err) } } From 4126cdbba7a928970982822245293ea55eb0b14e Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 9 Mar 2017 09:59:47 +1000 Subject: [PATCH 05/13] copy the network.yml over the switchroot too Signed-off-by: Sven Dowideit --- init/init.go | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/init/init.go b/init/init.go index 4e86ec11..d5235f30 100755 --- a/init/init.go +++ b/init/init.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "os/exec" + "path/filepath" "strings" "syscall" @@ -219,8 +220,9 @@ func RunInit() error { boot2DockerEnvironment := false var shouldSwitchRoot bool - var cloudConfigBootFile []byte - var metadataFile []byte + + configFiles := make(map[string][]byte) + initFuncs := []config.CfgFunc{ func(c *config.CloudConfig) (*config.CloudConfig, error) { return c, dfs.PrepareFs(&mountConfig) @@ -300,14 +302,18 @@ func RunInit() error { return cfg, nil }, func(cfg *config.CloudConfig) (*config.CloudConfig, error) { - var err error - cloudConfigBootFile, err = ioutil.ReadFile(config.CloudConfigBootFile) - if err != nil { - log.Error(err) + filesToCopy := []string{ + config.CloudConfigBootFile, + config.CloudConfigNetworkFile, + config.MetaDataFile, } - metadataFile, err = ioutil.ReadFile(config.MetaDataFile) - if err != nil { - log.Error(err) + for _, name := range filesToCopy { + content, err := ioutil.ReadFile(name) + if err != nil { + log.Error(err) + continue + } + configFiles[name] = content } return cfg, nil }, @@ -323,14 +329,13 @@ func RunInit() error { }, mountOem, func(cfg *config.CloudConfig) (*config.CloudConfig, error) { - if err := os.MkdirAll(config.CloudConfigDir, os.ModeDir|0755); err != nil { - log.Error(err) - } - if err := util.WriteFileAtomic(config.CloudConfigBootFile, cloudConfigBootFile, 400); err != nil { - log.Error(err) - } - if err := util.WriteFileAtomic(config.MetaDataFile, metadataFile, 400); err != nil { - log.Error(err) + for name, content := range configFiles { + if err := os.MkdirAll(filepath.Dir(name), os.ModeDir|0755); err != nil { + log.Error(err) + } + if err := util.WriteFileAtomic(name, content, 400); err != nil { + log.Error(err) + } } return cfg, nil }, From 00af8545d6ed3ed7f8ccc0341902a44413cf5484 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Fri, 10 Mar 2017 13:40:15 +1000 Subject: [PATCH 06/13] remove the non-network cloud-init option Signed-off-by: Sven Dowideit --- cmd/cloudinitsave/cloudinitsave.go | 39 ++++++++-------------- cmd/cloudinitsave/packet.go | 52 ------------------------------ 2 files changed, 13 insertions(+), 78 deletions(-) delete mode 100755 cmd/cloudinitsave/packet.go diff --git a/cmd/cloudinitsave/cloudinitsave.go b/cmd/cloudinitsave/cloudinitsave.go index 71415b6b..887cfc48 100755 --- a/cmd/cloudinitsave/cloudinitsave.go +++ b/cmd/cloudinitsave/cloudinitsave.go @@ -62,7 +62,7 @@ func Main() { cfg := rancherConfig.LoadConfig() network.ApplyNetworkConfig(cfg) - if err := SaveCloudConfig(true); err != nil { + if err := SaveCloudConfig(); err != nil { log.Errorf("Failed to save cloud-config: %v", err) } @@ -72,11 +72,11 @@ func Main() { network.ApplyNetworkConfig(cfg) } -func SaveCloudConfig(network bool) error { +func SaveCloudConfig() error { log.Debugf("SaveCloudConfig") cfg := rancherConfig.LoadConfig() - dss := getDatasources(cfg, network) + dss := getDatasources(cfg) if len(dss) == 0 { log.Errorf("currentDatasource - none found") return nil @@ -88,6 +88,7 @@ func SaveCloudConfig(network bool) error { func RequiresNetwork(datasource string) bool { // TODO: move into the datasources (and metadatasources) + // and then we can enable that platforms defaults.. parts := strings.SplitN(datasource, ":", 2) requiresNetwork, ok := map[string]bool{ "ec2": true, @@ -208,7 +209,7 @@ func fetchAndSave(ds datasource.Datasource) error { // getDatasources creates a slice of possible Datasources for cloudinit based // on the different source command-line flags. -func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.Datasource { +func getDatasources(cfg *rancherConfig.CloudConfig) []datasource.Datasource { dss := make([]datasource.Datasource, 0, 5) for _, ds := range cfg.Rancher.CloudInit.Datasources { @@ -221,43 +222,29 @@ func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.D switch parts[0] { case "ec2": - if network { - dss = append(dss, ec2.NewDatasource(root)) - } + dss = append(dss, ec2.NewDatasource(root)) case "file": if root != "" { dss = append(dss, file.NewDatasource(root)) } case "url": - if network { - if root != "" { - dss = append(dss, url.NewDatasource(root)) - } + if root != "" { + dss = append(dss, url.NewDatasource(root)) } case "cmdline": - if network { - if len(parts) == 1 { - dss = append(dss, proccmdline.NewDatasource()) - } + if len(parts) == 1 { + dss = append(dss, proccmdline.NewDatasource()) } case "configdrive": if root != "" { dss = append(dss, configdrive.NewDatasource(root)) } case "digitalocean": - if network { - dss = append(dss, digitalocean.NewDatasource(root)) - } else { - enableDoLinkLocal() - } + // TODO: should we enableDoLinkLocal() - to avoid the need for the other kernel/oem options? + dss = append(dss, digitalocean.NewDatasource(root)) case "gce": - if network { - dss = append(dss, gce.NewDatasource(root)) - } + dss = append(dss, gce.NewDatasource(root)) case "packet": - if !network { - enablePacketNetwork(&cfg.Rancher) - } dss = append(dss, packet.NewDatasource(root)) } } diff --git a/cmd/cloudinitsave/packet.go b/cmd/cloudinitsave/packet.go deleted file mode 100755 index 5fe8a71c..00000000 --- a/cmd/cloudinitsave/packet.go +++ /dev/null @@ -1,52 +0,0 @@ -package cloudinitsave - -import ( - "github.com/rancher/os/log" - - "github.com/rancher/os/config" - "github.com/rancher/os/netconf" -) - -func enablePacketNetwork(cfg *config.RancherConfig) { - bootStrapped := false - for _, v := range cfg.Network.Interfaces { - if v.Address != "" { - if err := netconf.ApplyNetworkConfigs(&cfg.Network); err != nil { - log.Errorf("Failed to bootstrap network: %v", err) - return - } - bootStrapped = true - break - } - } - - if !bootStrapped { - return - } - - // Post to phone home URL on first boot - /* - // TODO: bring this back - if _, err = os.Stat(config.CloudConfigNetworkFile); err != nil { - if _, err = http.Post(m.PhoneHomeURL, "application/json", bytes.NewReader([]byte{})); err != nil { - log.Errorf("Failed to post to Packet phone home URL: %v", err) - } - } - */ - - /* - cc := config.CloudConfig{ - Rancher: config.RancherConfig{ - Network: netCfg, - }, - } - - if err := os.MkdirAll(path.Dir(config.CloudConfigNetworkFile), 0700); err != nil { - log.Errorf("Failed to create directory for file %s: %v", config.CloudConfigNetworkFile, err) - } - - if err := config.WriteToFile(cc, config.CloudConfigNetworkFile); err != nil { - log.Errorf("Failed to save config file %s: %v", config.CloudConfigNetworkFile, err) - } - */ -} From 17b3589782c270e21c650276e2978daec326ab06 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Fri, 10 Mar 2017 21:53:12 +1000 Subject: [PATCH 07/13] Add a network test that sets up 2 of 4 ehternet devices, and allows another to use dhcp Signed-off-by: Sven Dowideit --- config/cloudinit/datasource/datasource.go | 3 - netconf/ipv4ll_linux.go | 36 +- netconf/netconf_linux.go | 43 +- tests/assets/multi_nic/cloud-config.yml | 17 + tests/network_test.go | 52 ++ trash.conf | 3 +- .../github.com/vishvananda/netlink/Makefile | 6 +- .../github.com/vishvananda/netlink/README.md | 2 +- vendor/github.com/vishvananda/netlink/addr.go | 15 +- .../vishvananda/netlink/addr_linux.go | 225 +++++-- .../vishvananda/netlink/bpf_linux.go | 62 ++ .../github.com/vishvananda/netlink/class.go | 45 +- .../vishvananda/netlink/class_linux.go | 98 ++- .../github.com/vishvananda/netlink/filter.go | 238 +++++-- .../vishvananda/netlink/filter_linux.go | 348 ++++++++-- .../vishvananda/netlink/handle_linux.go | 111 ++++ .../vishvananda/netlink/handle_unspecified.go | 218 ++++++ vendor/github.com/vishvananda/netlink/link.go | 204 +++++- .../vishvananda/netlink/link_linux.go | 623 +++++++++++++++--- .../vishvananda/netlink/neigh_linux.go | 76 ++- .../github.com/vishvananda/netlink/netlink.go | 11 +- .../vishvananda/netlink/netlink_linux.go | 11 + .../netlink/netlink_unspecified.go | 112 +++- .../vishvananda/netlink/nl/link_linux.go | 70 +- .../vishvananda/netlink/nl/mpls_linux.go | 36 + .../vishvananda/netlink/nl/nl_linux.go | 297 ++++++++- .../vishvananda/netlink/nl/nl_unspecified.go | 11 + .../vishvananda/netlink/nl/route_linux.go | 38 ++ .../vishvananda/netlink/nl/syscall.go | 31 + .../vishvananda/netlink/nl/tc_linux.go | 126 ++-- .../vishvananda/netlink/nl/xfrm_linux.go | 90 ++- .../netlink/nl/xfrm_monitor_linux.go | 32 + .../netlink/nl/xfrm_state_linux.go | 125 +++- .../github.com/vishvananda/netlink/order.go | 32 + .../vishvananda/netlink/protinfo.go | 5 +- .../vishvananda/netlink/protinfo_linux.go | 48 +- .../github.com/vishvananda/netlink/qdisc.go | 84 +-- .../vishvananda/netlink/qdisc_linux.go | 138 +++- .../github.com/vishvananda/netlink/route.go | 96 ++- .../vishvananda/netlink/route_linux.go | 389 ++++++++++- .../vishvananda/netlink/route_unspecified.go | 11 + vendor/github.com/vishvananda/netlink/rule.go | 3 - .../vishvananda/netlink/rule_linux.go | 39 +- .../github.com/vishvananda/netlink/socket.go | 27 + .../vishvananda/netlink/socket_linux.go | 159 +++++ vendor/github.com/vishvananda/netlink/xfrm.go | 12 +- .../vishvananda/netlink/xfrm_monitor_linux.go | 98 +++ .../vishvananda/netlink/xfrm_policy.go | 15 + .../vishvananda/netlink/xfrm_policy_linux.go | 230 +++++-- .../vishvananda/netlink/xfrm_state.go | 61 +- .../vishvananda/netlink/xfrm_state_linux.go | 434 +++++++++--- vendor/github.com/vishvananda/netns/LICENSE | 192 ++++++ vendor/github.com/vishvananda/netns/README.md | 51 ++ vendor/github.com/vishvananda/netns/netns.go | 80 +++ .../vishvananda/netns/netns_linux.go | 222 +++++++ .../vishvananda/netns/netns_unspecified.go | 43 ++ 56 files changed, 5092 insertions(+), 792 deletions(-) mode change 100644 => 100755 netconf/ipv4ll_linux.go mode change 100644 => 100755 netconf/netconf_linux.go create mode 100644 tests/assets/multi_nic/cloud-config.yml mode change 100644 => 100755 tests/network_test.go create mode 100644 vendor/github.com/vishvananda/netlink/bpf_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/handle_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/handle_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/netlink_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/mpls_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/nl_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/order.go create mode 100644 vendor/github.com/vishvananda/netlink/route_unspecified.go create mode 100644 vendor/github.com/vishvananda/netlink/socket.go create mode 100644 vendor/github.com/vishvananda/netlink/socket_linux.go create mode 100644 vendor/github.com/vishvananda/netlink/xfrm_monitor_linux.go create mode 100644 vendor/github.com/vishvananda/netns/LICENSE create mode 100644 vendor/github.com/vishvananda/netns/README.md create mode 100644 vendor/github.com/vishvananda/netns/netns.go create mode 100644 vendor/github.com/vishvananda/netns/netns_linux.go create mode 100644 vendor/github.com/vishvananda/netns/netns_unspecified.go diff --git a/config/cloudinit/datasource/datasource.go b/config/cloudinit/datasource/datasource.go index bd0cb70d..2b86357c 100755 --- a/config/cloudinit/datasource/datasource.go +++ b/config/cloudinit/datasource/datasource.go @@ -39,9 +39,6 @@ type Metadata struct { SSHPublicKeys map[string]string NetworkConfig netconf.NetworkConfig - // probably unused, but its in the initialize.env code - // TODO: work out if there's any reason to keep it. - // Lets see if anyone notices when its not set. PublicIPv4 net.IP PublicIPv6 net.IP PrivateIPv4 net.IP diff --git a/netconf/ipv4ll_linux.go b/netconf/ipv4ll_linux.go old mode 100644 new mode 100755 index 2a1cf9f3..deb38b20 --- a/netconf/ipv4ll_linux.go +++ b/netconf/ipv4ll_linux.go @@ -10,19 +10,16 @@ import ( "github.com/j-keck/arping" "github.com/vishvananda/netlink" + "github.com/vishvananda/netlink/nl" ) func AssignLinkLocalIP(link netlink.Link) error { ifaceName := link.Attrs().Name - iface, err := net.InterfaceByName(ifaceName) + + addrs, err := getLinkAddrs(link) if err != nil { - log.Error("could not get information about interface") return err } - addrs, err := iface.Addrs() - if err != nil { - log.Error("Error fetching existing ip on interface") - } for _, addr := range addrs { if addr.String()[:7] == "169.254" { log.Info("Link Local IP already set on interface") @@ -62,6 +59,24 @@ func AssignLinkLocalIP(link netlink.Link) error { return fmt.Errorf("Could not find a suitable ipv4ll") } +func RemoveLinkLocalIP(link netlink.Link) error { + addrs, err := getLinkAddrs(link) + if err != nil { + return err + } + for _, addr := range addrs { + if addr.String()[:7] == "169.254" { + if err := netlink.AddrDel(link, &addr); err != nil { + log.Error("ipv4ll addr del failed") + return err + } + log.Infof("Removed LinkLocal %s from %s", addr.String(), link.Attrs().Name) + return nil + } + } + return nil +} + func getNewIPV4LLAddr(randomNum uint32) net.IP { byte1 := randomNum & 255 // use least significant 8 bits byte2 := randomNum >> 24 // use most significant 8 bits @@ -73,3 +88,12 @@ func getPseudoRandomGenerator(haAddr []byte) (*rand.Source, error) { src := rand.NewSource(seed) return &src, nil } + +func getLinkAddrs(link netlink.Link) ([]netlink.Addr, error) { + addrs, err := netlink.AddrList(link, nl.FAMILY_ALL) + if err != nil { + log.Error("Error fetching existing ip on interface, %s", err) + err = nil // atm, we ignore this, as the link may not have one? + } + return addrs, err +} diff --git a/netconf/netconf_linux.go b/netconf/netconf_linux.go old mode 100644 new mode 100755 index 8fa5b360..205c5f1f --- a/netconf/netconf_linux.go +++ b/netconf/netconf_linux.go @@ -10,10 +10,10 @@ import ( "sync" "syscall" - "github.com/flynn/go-shlex" + shlex "github.com/flynn/go-shlex" "github.com/rancher/os/log" - "github.com/ryanuber/go-glob" + glob "github.com/ryanuber/go-glob" "github.com/vishvananda/netlink" ) @@ -267,6 +267,19 @@ func applyAddress(address string, link netlink.Link, netConf InterfaceConfig) er return nil } +func removeAddress(addr netlink.Addr, link netlink.Link) error { + if err := netlink.AddrDel(link, &addr); err == syscall.EEXIST { + //Ignore this error + } else if err != nil { + log.Errorf("addr del failed: %v", err) + } else { + log.Infof("Removed %s from %s", addr.String(), link.Attrs().Name) + } + + return nil +} + +// setGateway will set _one_ gateway on an interface (ie, replace an existing one) func setGateway(gateway string) error { if gateway == "" { return nil @@ -282,7 +295,7 @@ func setGateway(gateway string) error { Gw: gatewayIP, } - if err := netlink.RouteAdd(&route); err == syscall.EEXIST { + if err := netlink.RouteReplace(&route); err == syscall.EEXIST { //Ignore this error } else if err != nil { log.Errorf("gateway set failed: %v", err) @@ -294,6 +307,11 @@ func setGateway(gateway string) error { } func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { + //TODO: skip doing anything if the settings are "default"? + //TODO: how do you undo a non-default with a default? + // ATM, this removes + + // TODO: undo if netConf.Bond != "" { if err := netlink.LinkSetDown(link); err != nil { return err @@ -309,6 +327,7 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { return nil } + //TODO: undo if netConf.Bridge != "" && netConf.Bridge != "true" { b, err := NewBridge(netConf.Bridge) if err != nil { @@ -325,6 +344,11 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { log.Errorf("IPV4LL set failed: %v", err) return err } + } else { + if err := RemoveLinkLocalIP(link); err != nil { + log.Errorf("IPV4LL del failed: %v", err) + return err + } } addresses := []string{} @@ -337,13 +361,24 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { addresses = append(addresses, netConf.Addresses...) } + existingAddrs, _ := getLinkAddrs(link) + addrMap := make(map[string]bool) for _, address := range addresses { + addrMap[address] = true + log.Infof("Applying %s to %s", address, link.Attrs().Name) err := applyAddress(address, link, netConf) if err != nil { log.Errorf("Failed to apply address %s to %s: %v", address, link.Attrs().Name, err) } } + for _, addr := range existingAddrs { + if _, ok := addrMap[addr.IPNet.String()]; !ok { + log.Infof("removing %s from %s", addr.String(), link.Attrs().Name) + removeAddress(addr, link) + } + } + // TODO: can we set to default? if netConf.MTU > 0 { if err := netlink.LinkSetMTU(link, netConf.MTU); err != nil { log.Errorf("set MTU Failed: %v", err) @@ -357,6 +392,7 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { return err } + // TODO: how to remove a GW? if err := setGateway(netConf.Gateway); err != nil { log.Errorf("Fail to set gateway %s", netConf.Gateway) } @@ -365,6 +401,7 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { log.Errorf("Fail to set gateway %s", netConf.GatewayIpv6) } + // TODO: how to remove a GW? runCmds(netConf.PostUp, link.Attrs().Name) return nil diff --git a/tests/assets/multi_nic/cloud-config.yml b/tests/assets/multi_nic/cloud-config.yml new file mode 100644 index 00000000..41f7bd05 --- /dev/null +++ b/tests/assets/multi_nic/cloud-config.yml @@ -0,0 +1,17 @@ +#cloud-config +rancher: + network: + interfaces: + eth0: + address: 10.1.0.41/24 + gateway: 10.1.0.1 + mtu: 1500 + dhcp: false + eth1: + address: 10.31.168.85/24 + gateway: 10.31.168.1 + gateway_ipv6: "fe00:1234::" + mtu: 1500 + dhcp: false +ssh_authorized_keys: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC85w9stZyiLQp/DkVO6fqwiShYcj1ClKdtCqgHtf+PLpJkFReSFu8y21y+ev09gsSMRRrjF7yt0pUHV6zncQhVeqsZtgc5WbELY2DOYUGmRn/CCvPbXovoBrQjSorqlBmpuPwsStYLr92Xn+VVsMNSUIegHY22DphGbDKG85vrKB8HxUxGIDxFBds/uE8FhSy+xsoyT/jUZDK6pgq2HnGl6D81ViIlKecpOpWlW3B+fea99ADNyZNVvDzbHE5pcI3VRw8u59WmpWOUgT6qacNVACl8GqpBvQk8sw7O/X9DSZHCKafeD9G5k+GYbAUz92fKWrx/lOXfUXPS3+c8dRIF diff --git a/tests/network_test.go b/tests/network_test.go old mode 100644 new mode 100755 index c54bd813..229600e1 --- a/tests/network_test.go +++ b/tests/network_test.go @@ -35,3 +35,55 @@ cat /etc/resolv.conf | grep "nameserver 208.67.220.123" SCRIPT sudo bash test-merge`) } + +func (s *QemuSuite) TestNetworkCfg(c *C) { + args := []string{"--cloud-config", "./tests/assets/multi_nic/cloud-config.yml"} + args = append(args, []string{"-net", "nic,vlan=1,model=virtio"}...) + args = append(args, []string{"-net", "nic,vlan=1,model=virtio"}...) + args = append(args, []string{"-net", "nic,vlan=0,model=virtio"}...) + s.RunQemu(c, args...) + + // TODO: work out why the ipv6 loopback isn't present + // inet6 ::1/128 scope host + // valid_lft forever preferred_lft forever + + // show ip a output without mac addresses + s.CheckOutput(c, `1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever +2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 + inet 10.1.0.41/24 scope global eth0 + valid_lft forever preferred_lft forever +3: eth1: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 + inet 10.31.168.85/24 scope global eth1 + valid_lft forever preferred_lft forever +4: eth2: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 + inet6 XX::XX:XX:XX:XX/64 scope link + valid_lft forever preferred_lft forever +5: eth3: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 + inet XX.XX.XX.XX/24 brd 10.0.2.255 scope global eth3 + valid_lft forever preferred_lft forever + inet6 XX::XX:XX:XX:XX/64 scope link + valid_lft forever preferred_lft forever +6: docker-sys: mtu 1500 qdisc noqueue state DOWN group default qlen 1000 + inet 172.18.42.2/16 scope global docker-sys + valid_lft forever preferred_lft forever + inet6 XX::XX:XX:XX:XX/64 scope link + valid_lft forever preferred_lft forever +8: docker0: mtu 1500 qdisc noqueue state DOWN group default + inet 172.17.0.1/16 scope global docker0 + valid_lft forever preferred_lft forever +`, Equals, "ip a | grep -v ether | sed 's/inet 10\\.0\\.2\\..*\\/24 brd/inet XX.XX.XX.XX\\/24 brd/' | sed 's/inet6 .*\\/64 scope/inet6 XX::XX:XX:XX:XX\\/64 scope/'") + + s.CheckOutput(c, `Kernel IP routing table +Destination Gateway Genmask Flags Metric Ref Use Iface +0.0.0.0 10.31.168.1 0.0.0.0 UG 0 0 0 eth1 +0.0.0.0 10.0.2.2 0.0.0.0 UG 205 0 0 eth3 +10.0.2.0 0.0.0.0 255.255.255.0 U 205 0 0 eth3 +10.1.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +10.31.168.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 +172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 +172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker-sys +`, Equals, "route -n") +} diff --git a/trash.conf b/trash.conf index f9f7ace0..b195b949 100644 --- a/trash.conf +++ b/trash.conf @@ -48,7 +48,8 @@ github.com/stretchr/testify a1f97990ddc16022ec7610326dd9bce31332c116 github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 github.com/tchap/go-patricia v2.1.0 github.com/vbatts/tar-split v0.9.11 -github.com/vishvananda/netlink 631962935bff4f3d20ff32a72e8944f6d2836a26 +github.com/vishvananda/netlink fe3b5664d23a11b52ba59bece4ff29c52772a56b +github.com/vishvananda/netns 54f0e4339ce73702a0607f49922aaa1e749b418d github.com/xeipuuv/gojsonpointer e0fe6f68307607d540ed8eac07a342c33fa1b54a github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45 github.com/xeipuuv/gojsonschema ac452913faa25c08bb78810d3e6f88b8a39f8f25 diff --git a/vendor/github.com/vishvananda/netlink/Makefile b/vendor/github.com/vishvananda/netlink/Makefile index 1b977de4..6c8413b1 100644 --- a/vendor/github.com/vishvananda/netlink/Makefile +++ b/vendor/github.com/vishvananda/netlink/Makefile @@ -11,17 +11,17 @@ goroot = $(addprefix ../../../,$(1)) unroot = $(subst ../../../,,$(1)) fmt = $(addprefix fmt-,$(1)) -all: fmt test +all: test $(call goroot,$(DEPS)): go get $(call unroot,$@) .PHONY: $(call testdirs,$(DIRS)) $(call testdirs,$(DIRS)): - sudo -E go test -v github.com/vishvananda/netlink/$@ + sudo -E go test -test.parallel 4 -timeout 60s -v github.com/vishvananda/netlink/$@ $(call fmt,$(call testdirs,$(DIRS))): - ! gofmt -l $(subst fmt-,,$@)/*.go | grep '' + ! gofmt -l $(subst fmt-,,$@)/*.go | grep -q . .PHONY: fmt fmt: $(call fmt,$(call testdirs,$(DIRS))) diff --git a/vendor/github.com/vishvananda/netlink/README.md b/vendor/github.com/vishvananda/netlink/README.md index 8cd50a93..2367fae0 100644 --- a/vendor/github.com/vishvananda/netlink/README.md +++ b/vendor/github.com/vishvananda/netlink/README.md @@ -8,7 +8,7 @@ the kernel. It can be used to add and remove interfaces, set ip addresses and routes, and configure ipsec. Netlink communication requires elevated privileges, so in most cases this code needs to be run as root. Since low-level netlink messages are inscrutable at best, the library attempts -to provide an api that is loosely modeled on the CLI provied by iproute2. +to provide an api that is loosely modeled on the CLI provided by iproute2. Actions like `ip link add` will be accomplished via a similarly named function like AddLink(). This library began its life as a fork of the netlink functionality in diff --git a/vendor/github.com/vishvananda/netlink/addr.go b/vendor/github.com/vishvananda/netlink/addr.go index 079fff3b..fe3e3d36 100644 --- a/vendor/github.com/vishvananda/netlink/addr.go +++ b/vendor/github.com/vishvananda/netlink/addr.go @@ -10,9 +10,11 @@ import ( // include a mask, so it stores the address as a net.IPNet. type Addr struct { *net.IPNet - Label string - Flags int - Scope int + Label string + Flags int + Scope int + Peer *net.IPNet + Broadcast net.IP } // String returns $ip/$netmask $label @@ -43,3 +45,10 @@ func (a Addr) Equal(x Addr) bool { // ignore label for comparison return a.IP.Equal(x.IP) && sizea == sizeb } + +func (a Addr) PeerEqual(x Addr) bool { + sizea, _ := a.Peer.Mask.Size() + sizeb, _ := x.Peer.Mask.Size() + // ignore label for comparison + return a.Peer.IP.Equal(x.Peer.IP) && sizea == sizeb +} diff --git a/vendor/github.com/vishvananda/netlink/addr_linux.go b/vendor/github.com/vishvananda/netlink/addr_linux.go index 9e4f62f1..5348e403 100644 --- a/vendor/github.com/vishvananda/netlink/addr_linux.go +++ b/vendor/github.com/vishvananda/netlink/addr_linux.go @@ -2,11 +2,13 @@ package netlink import ( "fmt" + "log" "net" "strings" "syscall" "github.com/vishvananda/netlink/nl" + "github.com/vishvananda/netns" ) // IFA_FLAGS is a u32 attribute. @@ -15,24 +17,35 @@ const IFA_FLAGS = 0x8 // AddrAdd will add an IP address to a link device. // Equivalent to: `ip addr add $addr dev $link` func AddrAdd(link Link, addr *Addr) error { + return pkgHandle.AddrAdd(link, addr) +} - req := nl.NewNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) - return addrHandle(link, addr, req) +// AddrAdd will add an IP address to a link device. +// Equivalent to: `ip addr add $addr dev $link` +func (h *Handle) AddrAdd(link Link, addr *Addr) error { + req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return h.addrHandle(link, addr, req) } // AddrDel will delete an IP address from a link device. // Equivalent to: `ip addr del $addr dev $link` func AddrDel(link Link, addr *Addr) error { - req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK) - return addrHandle(link, addr, req) + return pkgHandle.AddrDel(link, addr) } -func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { +// AddrDel will delete an IP address from a link device. +// Equivalent to: `ip addr del $addr dev $link` +func (h *Handle) AddrDel(link Link, addr *Addr) error { + req := h.newNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK) + return h.addrHandle(link, addr, req) +} + +func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { base := link.Attrs() if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { return fmt.Errorf("label must begin with interface name") } - ensureIndex(base) + h.ensureIndex(base) family := nl.GetIPFamily(addr.IP) @@ -43,24 +56,42 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { msg.Prefixlen = uint8(prefixlen) req.AddData(msg) - var addrData []byte + var localAddrData []byte if family == FAMILY_V4 { - addrData = addr.IP.To4() + localAddrData = addr.IP.To4() } else { - addrData = addr.IP.To16() + localAddrData = addr.IP.To16() } - localData := nl.NewRtAttr(syscall.IFA_LOCAL, addrData) + localData := nl.NewRtAttr(syscall.IFA_LOCAL, localAddrData) req.AddData(localData) + var peerAddrData []byte + if addr.Peer != nil { + if family == FAMILY_V4 { + peerAddrData = addr.Peer.IP.To4() + } else { + peerAddrData = addr.Peer.IP.To16() + } + } else { + peerAddrData = localAddrData + } - addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData) + addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, peerAddrData) req.AddData(addressData) if addr.Flags != 0 { - b := make([]byte, 4) - native.PutUint32(b, uint32(addr.Flags)) - flagsData := nl.NewRtAttr(IFA_FLAGS, b) - req.AddData(flagsData) + if addr.Flags <= 0xff { + msg.IfAddrmsg.Flags = uint8(addr.Flags) + } else { + b := make([]byte, 4) + native.PutUint32(b, uint32(addr.Flags)) + flagsData := nl.NewRtAttr(IFA_FLAGS, b) + req.AddData(flagsData) + } + } + + if addr.Broadcast != nil { + req.AddData(nl.NewRtAttr(syscall.IFA_BROADCAST, addr.Broadcast)) } if addr.Label != "" { @@ -76,7 +107,14 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { // Equivalent to: `ip addr show`. // The list can be filtered by link and ip family. func AddrList(link Link, family int) ([]Addr, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP) + return pkgHandle.AddrList(link, family) +} + +// AddrList gets a list of IP addresses in the system. +// Equivalent to: `ip addr show`. +// The list can be filtered by link and ip family. +func (h *Handle) AddrList(link Link, family int) ([]Addr, error) { + req := h.newNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP) msg := nl.NewIfInfomsg(family) req.AddData(msg) @@ -85,62 +123,137 @@ func AddrList(link Link, family int) ([]Addr, error) { return nil, err } - index := 0 + indexFilter := 0 if link != nil { base := link.Attrs() - ensureIndex(base) - index = base.Index + h.ensureIndex(base) + indexFilter = base.Index } var res []Addr for _, m := range msgs { - msg := nl.DeserializeIfAddrmsg(m) + addr, msgFamily, ifindex, err := parseAddr(m) + if err != nil { + return res, err + } - if link != nil && msg.Index != uint32(index) { + if link != nil && ifindex != indexFilter { // Ignore messages from other interfaces continue } - if family != FAMILY_ALL && msg.Family != uint8(family) { + if family != FAMILY_ALL && msgFamily != family { continue } - attrs, err := nl.ParseRouteAttr(m[msg.Len():]) - if err != nil { - return nil, err - } - - var local, dst *net.IPNet - var addr Addr - for _, attr := range attrs { - switch attr.Attr.Type { - case syscall.IFA_ADDRESS: - dst = &net.IPNet{ - IP: attr.Value, - Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), - } - case syscall.IFA_LOCAL: - local = &net.IPNet{ - IP: attr.Value, - Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), - } - case syscall.IFA_LABEL: - addr.Label = string(attr.Value[:len(attr.Value)-1]) - case IFA_FLAGS: - addr.Flags = int(native.Uint32(attr.Value[0:4])) - } - } - - // IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS - if local != nil { - addr.IPNet = local - } else { - addr.IPNet = dst - } - addr.Scope = int(msg.Scope) - res = append(res, addr) } return res, nil } + +func parseAddr(m []byte) (addr Addr, family, index int, err error) { + msg := nl.DeserializeIfAddrmsg(m) + + family = -1 + index = -1 + + attrs, err1 := nl.ParseRouteAttr(m[msg.Len():]) + if err1 != nil { + err = err1 + return + } + + family = int(msg.Family) + index = int(msg.Index) + + var local, dst *net.IPNet + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.IFA_ADDRESS: + dst = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), + } + addr.Peer = dst + case syscall.IFA_LOCAL: + local = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), + } + addr.IPNet = local + case syscall.IFA_LABEL: + addr.Label = string(attr.Value[:len(attr.Value)-1]) + case IFA_FLAGS: + addr.Flags = int(native.Uint32(attr.Value[0:4])) + } + } + + // IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS + if local != nil { + addr.IPNet = local + } else { + addr.IPNet = dst + } + addr.Scope = int(msg.Scope) + + return +} + +type AddrUpdate struct { + LinkAddress net.IPNet + LinkIndex int + NewAddr bool // true=added false=deleted +} + +// AddrSubscribe takes a chan down which notifications will be sent +// when addresses change. Close the 'done' chan to stop subscription. +func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { + return addrSubscribe(netns.None(), netns.None(), ch, done) +} + +// AddrSubscribeAt works like AddrSubscribe plus it allows the caller +// to choose the network namespace in which to subscribe (ns). +func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { + return addrSubscribe(ns, netns.None(), ch, done) +} + +func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { + s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR) + if err != nil { + return err + } + if done != nil { + go func() { + <-done + s.Close() + }() + } + go func() { + defer close(ch) + for { + msgs, err := s.Receive() + if err != nil { + log.Printf("netlink.AddrSubscribe: Receive() error: %v", err) + return + } + for _, m := range msgs { + msgType := m.Header.Type + if msgType != syscall.RTM_NEWADDR && msgType != syscall.RTM_DELADDR { + log.Printf("netlink.AddrSubscribe: bad message type: %d", msgType) + continue + } + + addr, _, ifindex, err := parseAddr(m.Data) + if err != nil { + log.Printf("netlink.AddrSubscribe: could not parse address: %v", err) + continue + } + + ch <- AddrUpdate{LinkAddress: *addr.IPNet, LinkIndex: ifindex, NewAddr: msgType == syscall.RTM_NEWADDR} + } + } + }() + + return nil +} diff --git a/vendor/github.com/vishvananda/netlink/bpf_linux.go b/vendor/github.com/vishvananda/netlink/bpf_linux.go new file mode 100644 index 00000000..53374398 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/bpf_linux.go @@ -0,0 +1,62 @@ +package netlink + +/* +#include +#include +#include +#include +#include +#include + +static int load_simple_bpf(int prog_type, int ret) { +#ifdef __NR_bpf + // { return ret; } + __u64 __attribute__((aligned(8))) insns[] = { + 0x00000000000000b7ull | ((__u64)ret<<32), + 0x0000000000000095ull, + }; + __u8 __attribute__((aligned(8))) license[] = "ASL2"; + // Copied from a header file since libc is notoriously slow to update. + // The call will succeed or fail and that will be our indication on + // whether or not it is supported. + struct { + __u32 prog_type; + __u32 insn_cnt; + __u64 insns; + __u64 license; + __u32 log_level; + __u32 log_size; + __u64 log_buf; + __u32 kern_version; + } __attribute__((aligned(8))) attr = { + .prog_type = prog_type, + .insn_cnt = 2, + .insns = (uintptr_t)&insns, + .license = (uintptr_t)&license, + }; + return syscall(__NR_bpf, 5, &attr, sizeof(attr)); +#else + errno = EINVAL; + return -1; +#endif +} +*/ +import "C" + +type BpfProgType C.int + +const ( + BPF_PROG_TYPE_UNSPEC BpfProgType = iota + BPF_PROG_TYPE_SOCKET_FILTER + BPF_PROG_TYPE_KPROBE + BPF_PROG_TYPE_SCHED_CLS + BPF_PROG_TYPE_SCHED_ACT + BPF_PROG_TYPE_TRACEPOINT + BPF_PROG_TYPE_XDP +) + +// loadSimpleBpf loads a trivial bpf program for testing purposes +func loadSimpleBpf(progType BpfProgType, ret int) (int, error) { + fd, err := C.load_simple_bpf(C.int(progType), C.int(ret)) + return int(fd), err +} diff --git a/vendor/github.com/vishvananda/netlink/class.go b/vendor/github.com/vishvananda/netlink/class.go index 264e3ad0..8ee13af4 100644 --- a/vendor/github.com/vishvananda/netlink/class.go +++ b/vendor/github.com/vishvananda/netlink/class.go @@ -9,7 +9,7 @@ type Class interface { Type() string } -// Class represents a netlink class. A filter is associated with a link, +// ClassAttrs represents a netlink class. A filter is associated with a link, // has a handle and a parent. The root filter of a device should have a // parent == HANDLE_ROOT. type ClassAttrs struct { @@ -20,7 +20,7 @@ type ClassAttrs struct { } func (q ClassAttrs) String() string { - return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Leaf: %s}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Leaf) + return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Leaf: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Leaf) } type HtbClassAttrs struct { @@ -38,7 +38,7 @@ func (q HtbClassAttrs) String() string { return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer) } -// Htb class +// HtbClass represents an Htb class type HtbClass struct { ClassAttrs Rate uint64 @@ -50,48 +50,15 @@ type HtbClass struct { Prio uint32 } -func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass { - mtu := 1600 - rate := cattrs.Rate / 8 - ceil := cattrs.Ceil / 8 - buffer := cattrs.Buffer - cbuffer := cattrs.Cbuffer - - if ceil == 0 { - ceil = rate - } - - if buffer == 0 { - buffer = uint32(float64(rate)/Hz() + float64(mtu)) - } - buffer = uint32(Xmittime(rate, buffer)) - - if cbuffer == 0 { - cbuffer = uint32(float64(ceil)/Hz() + float64(mtu)) - } - cbuffer = uint32(Xmittime(ceil, cbuffer)) - - return &HtbClass{ - ClassAttrs: attrs, - Rate: rate, - Ceil: ceil, - Buffer: buffer, - Cbuffer: cbuffer, - Quantum: 10, - Level: 0, - Prio: 0, - } -} - func (q HtbClass) String() string { return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer) } -func (class *HtbClass) Attrs() *ClassAttrs { - return &class.ClassAttrs +func (q *HtbClass) Attrs() *ClassAttrs { + return &q.ClassAttrs } -func (class *HtbClass) Type() string { +func (q *HtbClass) Type() string { return "htb" } diff --git a/vendor/github.com/vishvananda/netlink/class_linux.go b/vendor/github.com/vishvananda/netlink/class_linux.go index 4a52d2b9..91cd3883 100644 --- a/vendor/github.com/vishvananda/netlink/class_linux.go +++ b/vendor/github.com/vishvananda/netlink/class_linux.go @@ -7,18 +7,64 @@ import ( "github.com/vishvananda/netlink/nl" ) +// NOTE: function is in here because it uses other linux functions +func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass { + mtu := 1600 + rate := cattrs.Rate / 8 + ceil := cattrs.Ceil / 8 + buffer := cattrs.Buffer + cbuffer := cattrs.Cbuffer + + if ceil == 0 { + ceil = rate + } + + if buffer == 0 { + buffer = uint32(float64(rate)/Hz() + float64(mtu)) + } + buffer = uint32(Xmittime(rate, buffer)) + + if cbuffer == 0 { + cbuffer = uint32(float64(ceil)/Hz() + float64(mtu)) + } + cbuffer = uint32(Xmittime(ceil, cbuffer)) + + return &HtbClass{ + ClassAttrs: attrs, + Rate: rate, + Ceil: ceil, + Buffer: buffer, + Cbuffer: cbuffer, + Quantum: 10, + Level: 0, + Prio: 0, + } +} + // ClassDel will delete a class from the system. // Equivalent to: `tc class del $class` func ClassDel(class Class) error { - return classModify(syscall.RTM_DELTCLASS, 0, class) + return pkgHandle.ClassDel(class) +} + +// ClassDel will delete a class from the system. +// Equivalent to: `tc class del $class` +func (h *Handle) ClassDel(class Class) error { + return h.classModify(syscall.RTM_DELTCLASS, 0, class) } // ClassChange will change a class in place // Equivalent to: `tc class change $class` // The parent and handle MUST NOT be changed. - func ClassChange(class Class) error { - return classModify(syscall.RTM_NEWTCLASS, 0, class) + return pkgHandle.ClassChange(class) +} + +// ClassChange will change a class in place +// Equivalent to: `tc class change $class` +// The parent and handle MUST NOT be changed. +func (h *Handle) ClassChange(class Class) error { + return h.classModify(syscall.RTM_NEWTCLASS, 0, class) } // ClassReplace will replace a class to the system. @@ -27,21 +73,36 @@ func ClassChange(class Class) error { // If a class already exist with this parent/handle pair, the class is changed. // If a class does not already exist with this parent/handle, a new class is created. func ClassReplace(class Class) error { - return classModify(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE, class) + return pkgHandle.ClassReplace(class) +} + +// ClassReplace will replace a class to the system. +// quivalent to: `tc class replace $class` +// The handle MAY be changed. +// If a class already exist with this parent/handle pair, the class is changed. +// If a class does not already exist with this parent/handle, a new class is created. +func (h *Handle) ClassReplace(class Class) error { + return h.classModify(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE, class) } // ClassAdd will add a class to the system. // Equivalent to: `tc class add $class` func ClassAdd(class Class) error { - return classModify( + return pkgHandle.ClassAdd(class) +} + +// ClassAdd will add a class to the system. +// Equivalent to: `tc class add $class` +func (h *Handle) ClassAdd(class Class) error { + return h.classModify( syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL, class, ) } -func classModify(cmd, flags int, class Class) error { - req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK) +func (h *Handle) classModify(cmd, flags int, class Class) error { + req := h.newNetlinkRequest(cmd, flags|syscall.NLM_F_ACK) base := class.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, @@ -73,20 +134,20 @@ func classPayload(req *nl.NetlinkRequest, class Class) error { opt.Prio = htb.Prio // TODO: Handle Debug properly. For now default to 0 /* Calculate {R,C}Tab and set Rate and Ceil */ - cell_log := -1 - ccell_log := -1 + cellLog := -1 + ccellLog := -1 linklayer := nl.LINKLAYER_ETHERNET mtu := 1600 var rtab [256]uint32 var ctab [256]uint32 tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)} - if CalcRtable(&tcrate, rtab, cell_log, uint32(mtu), linklayer) < 0 { - return errors.New("HTB: failed to calculate rate table.") + if CalcRtable(&tcrate, rtab, cellLog, uint32(mtu), linklayer) < 0 { + return errors.New("HTB: failed to calculate rate table") } opt.Rate = tcrate tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)} - if CalcRtable(&tcceil, ctab, ccell_log, uint32(mtu), linklayer) < 0 { - return errors.New("HTB: failed to calculate ceil rate table.") + if CalcRtable(&tcceil, ctab, ccellLog, uint32(mtu), linklayer) < 0 { + return errors.New("HTB: failed to calculate ceil rate table") } opt.Ceil = tcceil nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize()) @@ -101,14 +162,21 @@ func classPayload(req *nl.NetlinkRequest, class Class) error { // Equivalent to: `tc class show`. // Generally returns nothing if link and parent are not specified. func ClassList(link Link, parent uint32) ([]Class, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP) + return pkgHandle.ClassList(link, parent) +} + +// ClassList gets a list of classes in the system. +// Equivalent to: `tc class show`. +// Generally returns nothing if link and parent are not specified. +func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) { + req := h.newNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP) msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Parent: parent, } if link != nil { base := link.Attrs() - ensureIndex(base) + h.ensureIndex(base) msg.Ifindex = int32(base.Index) } req.AddData(msg) diff --git a/vendor/github.com/vishvananda/netlink/filter.go b/vendor/github.com/vishvananda/netlink/filter.go index 80ef34de..bc8a1e96 100644 --- a/vendor/github.com/vishvananda/netlink/filter.go +++ b/vendor/github.com/vishvananda/netlink/filter.go @@ -1,17 +1,13 @@ package netlink -import ( - "errors" - "fmt" - "github.com/vishvananda/netlink/nl" -) +import "fmt" type Filter interface { Attrs() *FilterAttrs Type() string } -// Filter represents a netlink filter. A filter is associated with a link, +// FilterAttrs represents a netlink filter. A filter is associated with a link, // has a handle and a parent. The root filter of a device should have a // parent == HANDLE_ROOT. type FilterAttrs struct { @@ -26,11 +22,170 @@ func (q FilterAttrs) String() string { return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Priority: %d, Protocol: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Priority, q.Protocol) } +type TcAct int32 + +const ( + TC_ACT_UNSPEC TcAct = -1 + TC_ACT_OK TcAct = 0 + TC_ACT_RECLASSIFY TcAct = 1 + TC_ACT_SHOT TcAct = 2 + TC_ACT_PIPE TcAct = 3 + TC_ACT_STOLEN TcAct = 4 + TC_ACT_QUEUED TcAct = 5 + TC_ACT_REPEAT TcAct = 6 + TC_ACT_REDIRECT TcAct = 7 + TC_ACT_JUMP TcAct = 0x10000000 +) + +func (a TcAct) String() string { + switch a { + case TC_ACT_UNSPEC: + return "unspec" + case TC_ACT_OK: + return "ok" + case TC_ACT_RECLASSIFY: + return "reclassify" + case TC_ACT_SHOT: + return "shot" + case TC_ACT_PIPE: + return "pipe" + case TC_ACT_STOLEN: + return "stolen" + case TC_ACT_QUEUED: + return "queued" + case TC_ACT_REPEAT: + return "repeat" + case TC_ACT_REDIRECT: + return "redirect" + case TC_ACT_JUMP: + return "jump" + } + return fmt.Sprintf("0x%x", int32(a)) +} + +type TcPolAct int32 + +const ( + TC_POLICE_UNSPEC TcPolAct = TcPolAct(TC_ACT_UNSPEC) + TC_POLICE_OK TcPolAct = TcPolAct(TC_ACT_OK) + TC_POLICE_RECLASSIFY TcPolAct = TcPolAct(TC_ACT_RECLASSIFY) + TC_POLICE_SHOT TcPolAct = TcPolAct(TC_ACT_SHOT) + TC_POLICE_PIPE TcPolAct = TcPolAct(TC_ACT_PIPE) +) + +func (a TcPolAct) String() string { + switch a { + case TC_POLICE_UNSPEC: + return "unspec" + case TC_POLICE_OK: + return "ok" + case TC_POLICE_RECLASSIFY: + return "reclassify" + case TC_POLICE_SHOT: + return "shot" + case TC_POLICE_PIPE: + return "pipe" + } + return fmt.Sprintf("0x%x", int32(a)) +} + +type ActionAttrs struct { + Index int + Capab int + Action TcAct + Refcnt int + Bindcnt int +} + +func (q ActionAttrs) String() string { + return fmt.Sprintf("{Index: %d, Capab: %x, Action: %s, Refcnt: %d, Bindcnt: %d}", q.Index, q.Capab, q.Action.String(), q.Refcnt, q.Bindcnt) +} + +// Action represents an action in any supported filter. +type Action interface { + Attrs() *ActionAttrs + Type() string +} + +type GenericAction struct { + ActionAttrs +} + +func (action *GenericAction) Type() string { + return "generic" +} + +func (action *GenericAction) Attrs() *ActionAttrs { + return &action.ActionAttrs +} + +type BpfAction struct { + ActionAttrs + Fd int + Name string +} + +func (action *BpfAction) Type() string { + return "bpf" +} + +func (action *BpfAction) Attrs() *ActionAttrs { + return &action.ActionAttrs +} + +type MirredAct uint8 + +func (a MirredAct) String() string { + switch a { + case TCA_EGRESS_REDIR: + return "egress redir" + case TCA_EGRESS_MIRROR: + return "egress mirror" + case TCA_INGRESS_REDIR: + return "ingress redir" + case TCA_INGRESS_MIRROR: + return "ingress mirror" + } + return "unknown" +} + +const ( + TCA_EGRESS_REDIR MirredAct = 1 /* packet redirect to EGRESS*/ + TCA_EGRESS_MIRROR MirredAct = 2 /* mirror packet to EGRESS */ + TCA_INGRESS_REDIR MirredAct = 3 /* packet redirect to INGRESS*/ + TCA_INGRESS_MIRROR MirredAct = 4 /* mirror packet to INGRESS */ +) + +type MirredAction struct { + ActionAttrs + MirredAction MirredAct + Ifindex int +} + +func (action *MirredAction) Type() string { + return "mirred" +} + +func (action *MirredAction) Attrs() *ActionAttrs { + return &action.ActionAttrs +} + +func NewMirredAction(redirIndex int) *MirredAction { + return &MirredAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_STOLEN, + }, + MirredAction: TCA_EGRESS_REDIR, + Ifindex: redirIndex, + } +} + // U32 filters on many packet related properties type U32 struct { FilterAttrs - // Currently only supports redirecting to another interface + ClassId uint32 RedirIndex int + Actions []Action } func (filter *U32) Attrs() *FilterAttrs { @@ -52,78 +207,27 @@ type FilterFwAttrs struct { Rate uint32 AvRate uint32 PeakRate uint32 - Action int + Action TcPolAct Overhead uint16 LinkLayer int } -// FwFilter filters on firewall marks -type Fw struct { +type BpfFilter struct { FilterAttrs - ClassId uint32 - Police nl.TcPolice - InDev string - // TODO Action - Mask uint32 - AvRate uint32 - Rtab [256]uint32 - Ptab [256]uint32 + ClassId uint32 + Fd int + Name string + DirectAction bool } -func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) { - var rtab [256]uint32 - var ptab [256]uint32 - rcell_log := -1 - pcell_log := -1 - avrate := fattrs.AvRate / 8 - police := nl.TcPolice{} - police.Rate.Rate = fattrs.Rate / 8 - police.PeakRate.Rate = fattrs.PeakRate / 8 - buffer := fattrs.Buffer - linklayer := nl.LINKLAYER_ETHERNET - - if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC { - linklayer = fattrs.LinkLayer - } - - police.Action = int32(fattrs.Action) - if police.Rate.Rate != 0 { - police.Rate.Mpu = fattrs.Mpu - police.Rate.Overhead = fattrs.Overhead - if CalcRtable(&police.Rate, rtab, rcell_log, fattrs.Mtu, linklayer) < 0 { - return nil, errors.New("TBF: failed to calculate rate table.") - } - police.Burst = uint32(Xmittime(uint64(police.Rate.Rate), uint32(buffer))) - } - police.Mtu = fattrs.Mtu - if police.PeakRate.Rate != 0 { - police.PeakRate.Mpu = fattrs.Mpu - police.PeakRate.Overhead = fattrs.Overhead - if CalcRtable(&police.PeakRate, ptab, pcell_log, fattrs.Mtu, linklayer) < 0 { - return nil, errors.New("POLICE: failed to calculate peak rate table.") - } - } - - return &Fw{ - FilterAttrs: attrs, - ClassId: fattrs.ClassId, - InDev: fattrs.InDev, - Mask: fattrs.Mask, - Police: police, - AvRate: avrate, - Rtab: rtab, - Ptab: ptab, - }, nil +func (filter *BpfFilter) Type() string { + return "bpf" } -func (filter *Fw) Attrs() *FilterAttrs { +func (filter *BpfFilter) Attrs() *FilterAttrs { return &filter.FilterAttrs } -func (filter *Fw) Type() string { - return "fw" -} - // GenericFilter filters represent types that are not currently understood // by this netlink library. type GenericFilter struct { diff --git a/vendor/github.com/vishvananda/netlink/filter_linux.go b/vendor/github.com/vishvananda/netlink/filter_linux.go index 1dc688b1..d9aedca7 100644 --- a/vendor/github.com/vishvananda/netlink/filter_linux.go +++ b/vendor/github.com/vishvananda/netlink/filter_linux.go @@ -3,16 +3,93 @@ package netlink import ( "bytes" "encoding/binary" + "errors" "fmt" "syscall" "github.com/vishvananda/netlink/nl" ) +// Fw filter filters on firewall marks +// NOTE: this is in filter_linux because it refers to nl.TcPolice which +// is defined in nl/tc_linux.go +type Fw struct { + FilterAttrs + ClassId uint32 + // TODO remove nl type from interface + Police nl.TcPolice + InDev string + // TODO Action + Mask uint32 + AvRate uint32 + Rtab [256]uint32 + Ptab [256]uint32 +} + +func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) { + var rtab [256]uint32 + var ptab [256]uint32 + rcellLog := -1 + pcellLog := -1 + avrate := fattrs.AvRate / 8 + police := nl.TcPolice{} + police.Rate.Rate = fattrs.Rate / 8 + police.PeakRate.Rate = fattrs.PeakRate / 8 + buffer := fattrs.Buffer + linklayer := nl.LINKLAYER_ETHERNET + + if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC { + linklayer = fattrs.LinkLayer + } + + police.Action = int32(fattrs.Action) + if police.Rate.Rate != 0 { + police.Rate.Mpu = fattrs.Mpu + police.Rate.Overhead = fattrs.Overhead + if CalcRtable(&police.Rate, rtab, rcellLog, fattrs.Mtu, linklayer) < 0 { + return nil, errors.New("TBF: failed to calculate rate table") + } + police.Burst = uint32(Xmittime(uint64(police.Rate.Rate), uint32(buffer))) + } + police.Mtu = fattrs.Mtu + if police.PeakRate.Rate != 0 { + police.PeakRate.Mpu = fattrs.Mpu + police.PeakRate.Overhead = fattrs.Overhead + if CalcRtable(&police.PeakRate, ptab, pcellLog, fattrs.Mtu, linklayer) < 0 { + return nil, errors.New("POLICE: failed to calculate peak rate table") + } + } + + return &Fw{ + FilterAttrs: attrs, + ClassId: fattrs.ClassId, + InDev: fattrs.InDev, + Mask: fattrs.Mask, + Police: police, + AvRate: avrate, + Rtab: rtab, + Ptab: ptab, + }, nil +} + +func (filter *Fw) Attrs() *FilterAttrs { + return &filter.FilterAttrs +} + +func (filter *Fw) Type() string { + return "fw" +} + // FilterDel will delete a filter from the system. // Equivalent to: `tc filter del $filter` func FilterDel(filter Filter) error { - req := nl.NewNetlinkRequest(syscall.RTM_DELTFILTER, syscall.NLM_F_ACK) + return pkgHandle.FilterDel(filter) +} + +// FilterDel will delete a filter from the system. +// Equivalent to: `tc filter del $filter` +func (h *Handle) FilterDel(filter Filter) error { + req := h.newNetlinkRequest(syscall.RTM_DELTFILTER, syscall.NLM_F_ACK) base := filter.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, @@ -30,8 +107,14 @@ func FilterDel(filter Filter) error { // FilterAdd will add a filter to the system. // Equivalent to: `tc filter add $filter` func FilterAdd(filter Filter) error { + return pkgHandle.FilterAdd(filter) +} + +// FilterAdd will add a filter to the system. +// Equivalent to: `tc filter add $filter` +func (h *Handle) FilterAdd(filter Filter) error { native = nl.NativeEndian() - req := nl.NewNetlinkRequest(syscall.RTM_NEWTFILTER, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + req := h.newNetlinkRequest(syscall.RTM_NEWTFILTER, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) base := filter.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, @@ -52,17 +135,17 @@ func FilterAdd(filter Filter) error { } sel.Keys = append(sel.Keys, nl.TcU32Key{}) nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize()) - actions := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil) - table := nl.NewRtAttrChild(actions, nl.TCA_ACT_TAB, nil) - nl.NewRtAttrChild(table, nl.TCA_KIND, nl.ZeroTerminated("mirred")) - // redirect to other interface - mir := nl.TcMirred{ - Action: nl.TC_ACT_STOLEN, - Eaction: nl.TCA_EGRESS_REDIR, - Ifindex: uint32(u32.RedirIndex), + if u32.ClassId != 0 { + nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId)) + } + actionsAttr := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil) + // backwards compatibility + if u32.RedirIndex != 0 { + u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...) + } + if err := EncodeActions(actionsAttr, u32.Actions); err != nil { + return err } - aopts := nl.NewRtAttrChild(table, nl.TCA_OPTIONS, nil) - nl.NewRtAttrChild(aopts, nl.TCA_MIRRED_PARMS, mir.Serialize()) } else if fw, ok := filter.(*Fw); ok { if fw.Mask != 0 { b := make([]byte, 4) @@ -90,6 +173,21 @@ func FilterAdd(filter Filter) error { native.PutUint32(b, fw.ClassId) nl.NewRtAttrChild(options, nl.TCA_FW_CLASSID, b) } + } else if bpf, ok := filter.(*BpfFilter); ok { + var bpfFlags uint32 + if bpf.ClassId != 0 { + nl.NewRtAttrChild(options, nl.TCA_BPF_CLASSID, nl.Uint32Attr(bpf.ClassId)) + } + if bpf.Fd >= 0 { + nl.NewRtAttrChild(options, nl.TCA_BPF_FD, nl.Uint32Attr((uint32(bpf.Fd)))) + } + if bpf.Name != "" { + nl.NewRtAttrChild(options, nl.TCA_BPF_NAME, nl.ZeroTerminated(bpf.Name)) + } + if bpf.DirectAction { + bpfFlags |= nl.TCA_BPF_FLAG_ACT_DIRECT + } + nl.NewRtAttrChild(options, nl.TCA_BPF_FLAGS, nl.Uint32Attr(bpfFlags)) } req.AddData(options) @@ -99,16 +197,23 @@ func FilterAdd(filter Filter) error { // FilterList gets a list of filters in the system. // Equivalent to: `tc filter show`. -// Generally retunrs nothing if link and parent are not specified. +// Generally returns nothing if link and parent are not specified. func FilterList(link Link, parent uint32) ([]Filter, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETTFILTER, syscall.NLM_F_DUMP) + return pkgHandle.FilterList(link, parent) +} + +// FilterList gets a list of filters in the system. +// Equivalent to: `tc filter show`. +// Generally returns nothing if link and parent are not specified. +func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) { + req := h.newNetlinkRequest(syscall.RTM_GETTFILTER, syscall.NLM_F_DUMP) msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Parent: parent, } if link != nil { base := link.Attrs() - ensureIndex(base) + h.ensureIndex(base) msg.Ifindex = int32(base.Index) } req.AddData(msg) @@ -147,29 +252,34 @@ func FilterList(link Link, parent uint32) ([]Filter, error) { filter = &U32{} case "fw": filter = &Fw{} + case "bpf": + filter = &BpfFilter{} default: filter = &GenericFilter{FilterType: filterType} } case nl.TCA_OPTIONS: + data, err := nl.ParseRouteAttr(attr.Value) + if err != nil { + return nil, err + } switch filterType { case "u32": - data, err := nl.ParseRouteAttr(attr.Value) - if err != nil { - return nil, err - } detailed, err = parseU32Data(filter, data) if err != nil { return nil, err } case "fw": - data, err := nl.ParseRouteAttr(attr.Value) - if err != nil { - return nil, err - } detailed, err = parseFwData(filter, data) if err != nil { return nil, err } + case "bpf": + detailed, err = parseBpfData(filter, data) + if err != nil { + return nil, err + } + default: + detailed = true } } } @@ -183,6 +293,129 @@ func FilterList(link Link, parent uint32) ([]Filter, error) { return res, nil } +func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) { + tcgen.Index = uint32(attrs.Index) + tcgen.Capab = uint32(attrs.Capab) + tcgen.Action = int32(attrs.Action) + tcgen.Refcnt = int32(attrs.Refcnt) + tcgen.Bindcnt = int32(attrs.Bindcnt) +} + +func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) { + attrs.Index = int(tcgen.Index) + attrs.Capab = int(tcgen.Capab) + attrs.Action = TcAct(tcgen.Action) + attrs.Refcnt = int(tcgen.Refcnt) + attrs.Bindcnt = int(tcgen.Bindcnt) +} + +func EncodeActions(attr *nl.RtAttr, actions []Action) error { + tabIndex := int(nl.TCA_ACT_TAB) + + for _, action := range actions { + switch action := action.(type) { + default: + return fmt.Errorf("unknown action type %s", action.Type()) + case *MirredAction: + table := nl.NewRtAttrChild(attr, tabIndex, nil) + tabIndex++ + nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("mirred")) + aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil) + mirred := nl.TcMirred{ + Eaction: int32(action.MirredAction), + Ifindex: uint32(action.Ifindex), + } + toTcGen(action.Attrs(), &mirred.TcGen) + nl.NewRtAttrChild(aopts, nl.TCA_MIRRED_PARMS, mirred.Serialize()) + case *BpfAction: + table := nl.NewRtAttrChild(attr, tabIndex, nil) + tabIndex++ + nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("bpf")) + aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil) + gen := nl.TcGen{} + toTcGen(action.Attrs(), &gen) + nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_PARMS, gen.Serialize()) + nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_FD, nl.Uint32Attr(uint32(action.Fd))) + nl.NewRtAttrChild(aopts, nl.TCA_ACT_BPF_NAME, nl.ZeroTerminated(action.Name)) + case *GenericAction: + table := nl.NewRtAttrChild(attr, tabIndex, nil) + tabIndex++ + nl.NewRtAttrChild(table, nl.TCA_ACT_KIND, nl.ZeroTerminated("gact")) + aopts := nl.NewRtAttrChild(table, nl.TCA_ACT_OPTIONS, nil) + gen := nl.TcGen{} + toTcGen(action.Attrs(), &gen) + nl.NewRtAttrChild(aopts, nl.TCA_GACT_PARMS, gen.Serialize()) + } + } + return nil +} + +func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { + var actions []Action + for _, table := range tables { + var action Action + var actionType string + aattrs, err := nl.ParseRouteAttr(table.Value) + if err != nil { + return nil, err + } + nextattr: + for _, aattr := range aattrs { + switch aattr.Attr.Type { + case nl.TCA_KIND: + actionType = string(aattr.Value[:len(aattr.Value)-1]) + // only parse if the action is mirred or bpf + switch actionType { + case "mirred": + action = &MirredAction{} + case "bpf": + action = &BpfAction{} + case "gact": + action = &GenericAction{} + default: + break nextattr + } + case nl.TCA_OPTIONS: + adata, err := nl.ParseRouteAttr(aattr.Value) + if err != nil { + return nil, err + } + for _, adatum := range adata { + switch actionType { + case "mirred": + switch adatum.Attr.Type { + case nl.TCA_MIRRED_PARMS: + mirred := *nl.DeserializeTcMirred(adatum.Value) + toAttrs(&mirred.TcGen, action.Attrs()) + action.(*MirredAction).ActionAttrs = ActionAttrs{} + action.(*MirredAction).Ifindex = int(mirred.Ifindex) + action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction) + } + case "bpf": + switch adatum.Attr.Type { + case nl.TCA_ACT_BPF_PARMS: + gen := *nl.DeserializeTcGen(adatum.Value) + toAttrs(&gen, action.Attrs()) + case nl.TCA_ACT_BPF_FD: + action.(*BpfAction).Fd = int(native.Uint32(adatum.Value[0:4])) + case nl.TCA_ACT_BPF_NAME: + action.(*BpfAction).Name = string(adatum.Value[:len(adatum.Value)-1]) + } + case "gact": + switch adatum.Attr.Type { + case nl.TCA_GACT_PARMS: + gen := *nl.DeserializeTcGen(adatum.Value) + toAttrs(&gen, action.Attrs()) + } + } + } + } + } + actions = append(actions, action) + } + return actions, nil +} + func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { native = nl.NativeEndian() u32 := filter.(*U32) @@ -197,34 +430,17 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) return detailed, nil } case nl.TCA_U32_ACT: - table, err := nl.ParseRouteAttr(datum.Value) + tables, err := nl.ParseRouteAttr(datum.Value) if err != nil { return detailed, err } - if len(table) != 1 || table[0].Attr.Type != nl.TCA_ACT_TAB { - return detailed, fmt.Errorf("Action table not formed properly") + u32.Actions, err = parseActions(tables) + if err != nil { + return detailed, err } - aattrs, err := nl.ParseRouteAttr(table[0].Value) - for _, aattr := range aattrs { - switch aattr.Attr.Type { - case nl.TCA_KIND: - actionType := string(aattr.Value[:len(aattr.Value)-1]) - // only parse if the action is mirred - if actionType != "mirred" { - return detailed, nil - } - case nl.TCA_OPTIONS: - adata, err := nl.ParseRouteAttr(aattr.Value) - if err != nil { - return detailed, err - } - for _, adatum := range adata { - switch adatum.Attr.Type { - case nl.TCA_MIRRED_PARMS: - mir := nl.DeserializeTcMirred(adatum.Value) - u32.RedirIndex = int(mir.Ifindex) - } - } + for _, action := range u32.Actions { + if action, ok := action.(*MirredAction); ok { + u32.RedirIndex = int(action.Ifindex) } } } @@ -261,6 +477,28 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { return detailed, nil } +func parseBpfData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) { + native = nl.NativeEndian() + bpf := filter.(*BpfFilter) + detailed := true + for _, datum := range data { + switch datum.Attr.Type { + case nl.TCA_BPF_FD: + bpf.Fd = int(native.Uint32(datum.Value[0:4])) + case nl.TCA_BPF_NAME: + bpf.Name = string(datum.Value[:len(datum.Value)-1]) + case nl.TCA_BPF_CLASSID: + bpf.ClassId = native.Uint32(datum.Value[0:4]) + case nl.TCA_BPF_FLAGS: + flags := native.Uint32(datum.Value[0:4]) + if (flags & nl.TCA_BPF_FLAG_ACT_DIRECT) != 0 { + bpf.DirectAction = true + } + } + } + return detailed, nil +} + func AlignToAtm(size uint) uint { var linksize, cells int cells = int(size / nl.ATM_CELL_PAYLOAD) @@ -283,27 +521,27 @@ func AdjustSize(sz uint, mpu uint, linklayer int) uint { } } -func CalcRtable(rate *nl.TcRateSpec, rtab [256]uint32, cell_log int, mtu uint32, linklayer int) int { +func CalcRtable(rate *nl.TcRateSpec, rtab [256]uint32, cellLog int, mtu uint32, linklayer int) int { bps := rate.Rate mpu := rate.Mpu var sz uint if mtu == 0 { mtu = 2047 } - if cell_log < 0 { - cell_log = 0 - for (mtu >> uint(cell_log)) > 255 { - cell_log++ + if cellLog < 0 { + cellLog = 0 + for (mtu >> uint(cellLog)) > 255 { + cellLog++ } } for i := 0; i < 256; i++ { - sz = AdjustSize(uint((i+1)< 0 { - nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT, nl.Uint16Attr(uint16(vxlan.Port))) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT, htons(uint16(vxlan.Port))) } if vxlan.PortLow > 0 || vxlan.PortHigh > 0 { pr := vxlanPortRange{uint16(vxlan.PortLow), uint16(vxlan.PortHigh)} @@ -434,9 +624,16 @@ func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) { } // LinkAdd adds a new link device. The type and features of the device -// are taken fromt the parameters in the link object. +// are taken from the parameters in the link object. // Equivalent to: `ip link add $link` func LinkAdd(link Link) error { + return pkgHandle.LinkAdd(link) +} + +// LinkAdd adds a new link device. The type and features of the device +// are taken fromt the parameters in the link object. +// Equivalent to: `ip link add $link` +func (h *Handle) LinkAdd(link Link) error { // TODO: set mtu and hardware address // TODO: support extra data for macvlan base := link.Attrs() @@ -448,9 +645,7 @@ func LinkAdd(link Link) error { if tuntap, ok := link.(*Tuntap); ok { // TODO: support user // TODO: support group - // TODO: support non- one_queue - // TODO: support pi | vnet_hdr | multi_queue - // TODO: support non- exclusive + // TODO: multi_queue // TODO: support non- persistent if tuntap.Mode < syscall.IFF_TUN || tuntap.Mode > syscall.IFF_TAP { return fmt.Errorf("Tuntap.Mode %v unknown!", tuntap.Mode) @@ -461,10 +656,13 @@ func LinkAdd(link Link) error { } defer file.Close() var req ifReq - req.Flags |= syscall.IFF_ONE_QUEUE - req.Flags |= syscall.IFF_TUN_EXCL - copy(req.Name[:15], base.Name) + if tuntap.Flags == 0 { + req.Flags = uint16(TUNTAP_DEFAULTS) + } else { + req.Flags = uint16(tuntap.Flags) + } req.Flags |= uint16(tuntap.Mode) + copy(req.Name[:15], base.Name) _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&req))) if errno != 0 { return fmt.Errorf("Tuntap IOCTL TUNSETIFF failed, errno %v", errno) @@ -473,17 +671,17 @@ func LinkAdd(link Link) error { if errno != 0 { return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno) } - ensureIndex(base) + h.ensureIndex(base) // can't set master during create, so set it afterwards if base.MasterIndex != 0 { // TODO: verify MasterIndex is actually a bridge? - return LinkSetMasterByIndex(link, base.MasterIndex) + return h.LinkSetMasterByIndex(link, base.MasterIndex) } return nil } - req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + req := h.newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) // TODO: make it shorter @@ -545,6 +743,10 @@ func LinkAdd(link Link) error { req.AddData(attr) } + if base.Xdp != nil { + addXdpAttrs(base.Xdp, req) + } + linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil) nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) @@ -577,8 +779,19 @@ func LinkAdd(link Link) error { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode])) } + } else if macv, ok := link.(*Macvtap); ok { + if macv.Mode != MACVLAN_MODE_DEFAULT { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode])) + } } else if gretap, ok := link.(*Gretap); ok { addGretapAttrs(gretap, linkInfo) + } else if iptun, ok := link.(*Iptun); ok { + addIptunAttrs(iptun, linkInfo) + } else if vti, ok := link.(*Vti); ok { + addVtiAttrs(vti, linkInfo) + } else if vrf, ok := link.(*Vrf); ok { + addVrfAttrs(vrf, linkInfo) } req.AddData(linkInfo) @@ -588,12 +801,12 @@ func LinkAdd(link Link) error { return err } - ensureIndex(base) + h.ensureIndex(base) // can't set master during create, so set it afterwards if base.MasterIndex != 0 { // TODO: verify MasterIndex is actually a bridge? - return LinkSetMasterByIndex(link, base.MasterIndex) + return h.LinkSetMasterByIndex(link, base.MasterIndex) } return nil } @@ -602,11 +815,18 @@ func LinkAdd(link Link) error { // the link object for it to be deleted. The other values are ignored. // Equivalent to: `ip link del $link` func LinkDel(link Link) error { + return pkgHandle.LinkDel(link) +} + +// LinkDel deletes link device. Either Index or Name must be set in +// the link object for it to be deleted. The other values are ignored. +// Equivalent to: `ip link del $link` +func (h *Handle) LinkDel(link Link) error { base := link.Attrs() - ensureIndex(base) + h.ensureIndex(base) - req := nl.NewNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) + req := h.newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Index = int32(base.Index) @@ -616,8 +836,8 @@ func LinkDel(link Link) error { return err } -func linkByNameDump(name string) (Link, error) { - links, err := LinkList() +func (h *Handle) linkByNameDump(name string) (Link, error) { + links, err := h.LinkList() if err != nil { return nil, err } @@ -630,8 +850,8 @@ func linkByNameDump(name string) (Link, error) { return nil, fmt.Errorf("Link %s not found", name) } -func linkByAliasDump(alias string) (Link, error) { - links, err := LinkList() +func (h *Handle) linkByAliasDump(alias string) (Link, error) { + links, err := h.LinkList() if err != nil { return nil, err } @@ -646,11 +866,16 @@ func linkByAliasDump(alias string) (Link, error) { // LinkByName finds a link by name and returns a pointer to the object. func LinkByName(name string) (Link, error) { - if lookupByDump { - return linkByNameDump(name) + return pkgHandle.LinkByName(name) +} + +// LinkByName finds a link by name and returns a pointer to the object. +func (h *Handle) LinkByName(name string) (Link, error) { + if h.lookupByDump { + return h.linkByNameDump(name) } - req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) + req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) req.AddData(msg) @@ -662,8 +887,8 @@ func LinkByName(name string) (Link, error) { if err == syscall.EINVAL { // older kernels don't support looking up via IFLA_IFNAME // so fall back to dumping all links - lookupByDump = true - return linkByNameDump(name) + h.lookupByDump = true + return h.linkByNameDump(name) } return link, err @@ -672,11 +897,17 @@ func LinkByName(name string) (Link, error) { // LinkByAlias finds a link by its alias and returns a pointer to the object. // If there are multiple links with the alias it returns the first one func LinkByAlias(alias string) (Link, error) { - if lookupByDump { - return linkByAliasDump(alias) + return pkgHandle.LinkByAlias(alias) +} + +// LinkByAlias finds a link by its alias and returns a pointer to the object. +// If there are multiple links with the alias it returns the first one +func (h *Handle) LinkByAlias(alias string) (Link, error) { + if h.lookupByDump { + return h.linkByAliasDump(alias) } - req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) + req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) req.AddData(msg) @@ -688,8 +919,8 @@ func LinkByAlias(alias string) (Link, error) { if err == syscall.EINVAL { // older kernels don't support looking up via IFLA_IFALIAS // so fall back to dumping all links - lookupByDump = true - return linkByAliasDump(alias) + h.lookupByDump = true + return h.linkByAliasDump(alias) } return link, err @@ -697,7 +928,12 @@ func LinkByAlias(alias string) (Link, error) { // LinkByIndex finds a link by index and returns a pointer to the object. func LinkByIndex(index int) (Link, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) + return pkgHandle.LinkByIndex(index) +} + +// LinkByIndex finds a link by index and returns a pointer to the object. +func (h *Handle) LinkByIndex(index int) (Link, error) { + req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Index = int32(index) @@ -722,7 +958,7 @@ func execGetLink(req *nl.NetlinkRequest) (Link, error) { return nil, fmt.Errorf("Link not found") case len(msgs) == 1: - return linkDeserialize(msgs[0]) + return LinkDeserialize(nil, msgs[0]) default: return nil, fmt.Errorf("More than one link found") @@ -731,7 +967,7 @@ func execGetLink(req *nl.NetlinkRequest) (Link, error) { // linkDeserialize deserializes a raw message received from netlink into // a link object. -func linkDeserialize(m []byte) (Link, error) { +func LinkDeserialize(hdr *syscall.NlMsghdr, m []byte) (Link, error) { msg := nl.DeserializeIfInfomsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) @@ -739,9 +975,16 @@ func linkDeserialize(m []byte) (Link, error) { return nil, err } - base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)} - var link Link - linkType := "" + base := LinkAttrs{Index: int(msg.Index), RawFlags: msg.Flags, Flags: linkFlags(msg.Flags), EncapType: msg.EncapType()} + if msg.Flags&syscall.IFF_PROMISC != 0 { + base.Promisc = 1 + } + var ( + link Link + stats32 []byte + stats64 []byte + linkType string + ) for _, attr := range attrs { switch attr.Attr.Type { case syscall.IFLA_LINKINFO: @@ -776,6 +1019,12 @@ func linkDeserialize(m []byte) (Link, error) { link = &Macvtap{} case "gretap": link = &Gretap{} + case "ipip": + link = &Iptun{} + case "vti": + link = &Vti{} + case "vrf": + link = &Vrf{} default: link = &GenericLink{LinkType: linkType} } @@ -799,6 +1048,12 @@ func linkDeserialize(m []byte) (Link, error) { parseMacvtapData(link, data) case "gretap": parseGretapData(link, data) + case "ipip": + parseIptunData(link, data) + case "vti": + parseVtiData(link, data) + case "vrf": + parseVrfData(link, data) } } } @@ -824,8 +1079,36 @@ func linkDeserialize(m []byte) (Link, error) { base.TxQLen = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_IFALIAS: base.Alias = string(attr.Value[:len(attr.Value)-1]) + case syscall.IFLA_STATS: + stats32 = attr.Value[:] + case IFLA_STATS64: + stats64 = attr.Value[:] + case nl.IFLA_XDP: + xdp, err := parseLinkXdp(attr.Value[:]) + if err != nil { + return nil, err + } + base.Xdp = xdp + case syscall.IFLA_PROTINFO | syscall.NLA_F_NESTED: + if hdr != nil && hdr.Type == syscall.RTM_NEWLINK && + msg.Family == syscall.AF_BRIDGE { + attrs, err := nl.ParseRouteAttr(attr.Value[:]) + if err != nil { + return nil, err + } + base.Protinfo = parseProtinfo(attrs) + } + case syscall.IFLA_OPERSTATE: + base.OperState = LinkOperState(uint8(attr.Value[0])) } } + + if stats64 != nil { + base.Statistics = parseLinkStats64(stats64) + } else if stats32 != nil { + base.Statistics = parseLinkStats32(stats32) + } + // Links that don't have IFLA_INFO_KIND are hardware devices if link == nil { link = &Device{} @@ -838,9 +1121,15 @@ func linkDeserialize(m []byte) (Link, error) { // LinkList gets a list of link devices. // Equivalent to: `ip link show` func LinkList() ([]Link, error) { + return pkgHandle.LinkList() +} + +// LinkList gets a list of link devices. +// Equivalent to: `ip link show` +func (h *Handle) LinkList() ([]Link, error) { // NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need // to get the message ourselves to parse link type. - req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) + req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) req.AddData(msg) @@ -852,7 +1141,7 @@ func LinkList() ([]Link, error) { var res []Link for _, m := range msgs { - link, err := linkDeserialize(m) + link, err := LinkDeserialize(nil, m) if err != nil { return nil, err } @@ -872,7 +1161,17 @@ type LinkUpdate struct { // LinkSubscribe takes a chan down which notifications will be sent // when links change. Close the 'done' chan to stop subscription. func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { - s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK) + return linkSubscribe(netns.None(), netns.None(), ch, done) +} + +// LinkSubscribeAt works like LinkSubscribe plus it allows the caller +// to choose the network namespace in which to subscribe (ns). +func LinkSubscribeAt(ns netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { + return linkSubscribe(ns, netns.None(), ch, done) +} + +func linkSubscribe(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { + s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK) if err != nil { return err } @@ -891,7 +1190,7 @@ func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { } for _, m := range msgs { ifmsg := nl.DeserializeIfInfomsg(m.Data) - link, err := linkDeserialize(m.Data) + link, err := LinkDeserialize(&m.Header, m.Data) if err != nil { return } @@ -904,33 +1203,57 @@ func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { } func LinkSetHairpin(link Link, mode bool) error { - return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE) + return pkgHandle.LinkSetHairpin(link, mode) +} + +func (h *Handle) LinkSetHairpin(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE) } func LinkSetGuard(link Link, mode bool) error { - return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_GUARD) + return pkgHandle.LinkSetGuard(link, mode) +} + +func (h *Handle) LinkSetGuard(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_GUARD) } func LinkSetFastLeave(link Link, mode bool) error { - return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_FAST_LEAVE) + return pkgHandle.LinkSetFastLeave(link, mode) +} + +func (h *Handle) LinkSetFastLeave(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_FAST_LEAVE) } func LinkSetLearning(link Link, mode bool) error { - return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING) + return pkgHandle.LinkSetLearning(link, mode) +} + +func (h *Handle) LinkSetLearning(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING) } func LinkSetRootBlock(link Link, mode bool) error { - return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROTECT) + return pkgHandle.LinkSetRootBlock(link, mode) +} + +func (h *Handle) LinkSetRootBlock(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROTECT) } func LinkSetFlood(link Link, mode bool) error { - return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD) + return pkgHandle.LinkSetFlood(link, mode) } -func setProtinfoAttr(link Link, mode bool, attr int) error { +func (h *Handle) LinkSetFlood(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD) +} + +func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error { base := link.Attrs() - ensureIndex(base) - req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + h.ensureIndex(base) + req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) msg.Index = int32(base.Index) @@ -989,14 +1312,14 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { case nl.IFLA_VXLAN_UDP_CSUM: vxlan.UDPCSum = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_GBP: - vxlan.GBP = int8(datum.Value[0]) != 0 + vxlan.GBP = true case nl.IFLA_VXLAN_AGEING: vxlan.Age = int(native.Uint32(datum.Value[0:4])) vxlan.NoAge = vxlan.Age == 0 case nl.IFLA_VXLAN_LIMIT: vxlan.Limit = int(native.Uint32(datum.Value[0:4])) case nl.IFLA_VXLAN_PORT: - vxlan.Port = int(native.Uint16(datum.Value[0:2])) + vxlan.Port = int(ntohs(datum.Value[0:2])) case nl.IFLA_VXLAN_PORT_RANGE: buf := bytes.NewBuffer(datum.Value[0:4]) var pr vxlanPortRange @@ -1119,26 +1442,6 @@ func linkFlags(rawFlags uint32) net.Flags { return f } -func htonl(val uint32) []byte { - bytes := make([]byte, 4) - binary.BigEndian.PutUint32(bytes, val) - return bytes -} - -func htons(val uint16) []byte { - bytes := make([]byte, 2) - binary.BigEndian.PutUint16(bytes, val) - return bytes -} - -func ntohl(buf []byte) uint32 { - return binary.BigEndian.Uint32(buf) -} - -func ntohs(buf []byte) uint16 { - return binary.BigEndian.Uint16(buf) -} - func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) @@ -1211,3 +1514,129 @@ func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) { } } } + +func parseLinkStats32(data []byte) *LinkStatistics { + return (*LinkStatistics)((*LinkStatistics32)(unsafe.Pointer(&data[0:SizeofLinkStats32][0])).to64()) +} + +func parseLinkStats64(data []byte) *LinkStatistics { + return (*LinkStatistics)((*LinkStatistics64)(unsafe.Pointer(&data[0:SizeofLinkStats64][0]))) +} + +func addXdpAttrs(xdp *LinkXdp, req *nl.NetlinkRequest) { + attrs := nl.NewRtAttr(nl.IFLA_XDP|syscall.NLA_F_NESTED, nil) + b := make([]byte, 4) + native.PutUint32(b, uint32(xdp.Fd)) + nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FD, b) + req.AddData(attrs) +} + +func parseLinkXdp(data []byte) (*LinkXdp, error) { + attrs, err := nl.ParseRouteAttr(data) + if err != nil { + return nil, err + } + xdp := &LinkXdp{} + for _, attr := range attrs { + switch attr.Attr.Type { + case nl.IFLA_XDP_FD: + xdp.Fd = int(native.Uint32(attr.Value[0:4])) + case nl.IFLA_XDP_ATTACHED: + xdp.Attached = attr.Value[0] != 0 + } + } + return xdp, nil +} + +func addIptunAttrs(iptun *Iptun, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + + ip := iptun.Local.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_LOCAL, []byte(ip)) + } + + ip = iptun.Remote.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_REMOTE, []byte(ip)) + } + + if iptun.Link != 0 { + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_LINK, nl.Uint32Attr(iptun.Link)) + } + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_PMTUDISC, nl.Uint8Attr(iptun.PMtuDisc)) + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_TTL, nl.Uint8Attr(iptun.Ttl)) + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_TOS, nl.Uint8Attr(iptun.Tos)) +} + +func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) { + iptun := link.(*Iptun) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_IPTUN_LOCAL: + iptun.Local = net.IP(datum.Value[0:4]) + case nl.IFLA_IPTUN_REMOTE: + iptun.Remote = net.IP(datum.Value[0:4]) + case nl.IFLA_IPTUN_TTL: + iptun.Ttl = uint8(datum.Value[0]) + case nl.IFLA_IPTUN_TOS: + iptun.Tos = uint8(datum.Value[0]) + case nl.IFLA_IPTUN_PMTUDISC: + iptun.PMtuDisc = uint8(datum.Value[0]) + } + } +} + +func addVtiAttrs(vti *Vti, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + + ip := vti.Local.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_VTI_LOCAL, []byte(ip)) + } + + ip = vti.Remote.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_VTI_REMOTE, []byte(ip)) + } + + if vti.Link != 0 { + nl.NewRtAttrChild(data, nl.IFLA_VTI_LINK, nl.Uint32Attr(vti.Link)) + } + + nl.NewRtAttrChild(data, nl.IFLA_VTI_IKEY, htonl(vti.IKey)) + nl.NewRtAttrChild(data, nl.IFLA_VTI_OKEY, htonl(vti.OKey)) +} + +func parseVtiData(link Link, data []syscall.NetlinkRouteAttr) { + vti := link.(*Vti) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_VTI_LOCAL: + vti.Local = net.IP(datum.Value[0:4]) + case nl.IFLA_VTI_REMOTE: + vti.Remote = net.IP(datum.Value[0:4]) + case nl.IFLA_VTI_IKEY: + vti.IKey = ntohl(datum.Value[0:4]) + case nl.IFLA_VTI_OKEY: + vti.OKey = ntohl(datum.Value[0:4]) + } + } +} + +func addVrfAttrs(vrf *Vrf, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + b := make([]byte, 4) + native.PutUint32(b, uint32(vrf.Table)) + nl.NewRtAttrChild(data, nl.IFLA_VRF_TABLE, b) +} + +func parseVrfData(link Link, data []syscall.NetlinkRouteAttr) { + vrf := link.(*Vrf) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_VRF_TABLE: + vrf.Table = native.Uint32(datum.Value[0:4]) + } + } +} diff --git a/vendor/github.com/vishvananda/netlink/neigh_linux.go b/vendor/github.com/vishvananda/netlink/neigh_linux.go index 2af693ba..f069db25 100644 --- a/vendor/github.com/vishvananda/netlink/neigh_linux.go +++ b/vendor/github.com/vishvananda/netlink/neigh_linux.go @@ -67,30 +67,62 @@ func (msg *Ndmsg) Len() int { // NeighAdd will add an IP to MAC mapping to the ARP table // Equivalent to: `ip neigh add ....` func NeighAdd(neigh *Neigh) error { - return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) + return pkgHandle.NeighAdd(neigh) +} + +// NeighAdd will add an IP to MAC mapping to the ARP table +// Equivalent to: `ip neigh add ....` +func (h *Handle) NeighAdd(neigh *Neigh) error { + return h.neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) } // NeighSet will add or replace an IP to MAC mapping to the ARP table // Equivalent to: `ip neigh replace....` func NeighSet(neigh *Neigh) error { - return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE) + return pkgHandle.NeighSet(neigh) +} + +// NeighSet will add or replace an IP to MAC mapping to the ARP table +// Equivalent to: `ip neigh replace....` +func (h *Handle) NeighSet(neigh *Neigh) error { + return h.neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE) } // NeighAppend will append an entry to FDB // Equivalent to: `bridge fdb append...` func NeighAppend(neigh *Neigh) error { - return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_APPEND) + return pkgHandle.NeighAppend(neigh) } +// NeighAppend will append an entry to FDB +// Equivalent to: `bridge fdb append...` +func (h *Handle) NeighAppend(neigh *Neigh) error { + return h.neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_APPEND) +} + +// NeighAppend will append an entry to FDB +// Equivalent to: `bridge fdb append...` func neighAdd(neigh *Neigh, mode int) error { - req := nl.NewNetlinkRequest(syscall.RTM_NEWNEIGH, mode|syscall.NLM_F_ACK) + return pkgHandle.neighAdd(neigh, mode) +} + +// NeighAppend will append an entry to FDB +// Equivalent to: `bridge fdb append...` +func (h *Handle) neighAdd(neigh *Neigh, mode int) error { + req := h.newNetlinkRequest(syscall.RTM_NEWNEIGH, mode|syscall.NLM_F_ACK) return neighHandle(neigh, req) } // NeighDel will delete an IP address from a link device. // Equivalent to: `ip addr del $addr dev $link` func NeighDel(neigh *Neigh) error { - req := nl.NewNetlinkRequest(syscall.RTM_DELNEIGH, syscall.NLM_F_ACK) + return pkgHandle.NeighDel(neigh) +} + +// NeighDel will delete an IP address from a link device. +// Equivalent to: `ip addr del $addr dev $link` +func (h *Handle) NeighDel(neigh *Neigh) error { + req := h.newNetlinkRequest(syscall.RTM_DELNEIGH, syscall.NLM_F_ACK) return neighHandle(neigh, req) } @@ -119,8 +151,10 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { dstData := nl.NewRtAttr(NDA_DST, ipData) req.AddData(dstData) - hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) - req.AddData(hwData) + if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil { + hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) + req.AddData(hwData) + } _, err := req.Execute(syscall.NETLINK_ROUTE, 0) return err @@ -130,10 +164,36 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { // Equivalent to: `ip neighbor show`. // The list can be filtered by link and ip family. func NeighList(linkIndex, family int) ([]Neigh, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP) + return pkgHandle.NeighList(linkIndex, family) +} + +// NeighProxyList gets a list of neighbor proxies in the system. +// Equivalent to: `ip neighbor show proxy`. +// The list can be filtered by link and ip family. +func NeighProxyList(linkIndex, family int) ([]Neigh, error) { + return pkgHandle.NeighProxyList(linkIndex, family) +} + +// NeighList gets a list of IP-MAC mappings in the system (ARP table). +// Equivalent to: `ip neighbor show`. +// The list can be filtered by link and ip family. +func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) { + return h.neighList(linkIndex, family, 0) +} + +// NeighProxyList gets a list of neighbor proxies in the system. +// Equivalent to: `ip neighbor show proxy`. +// The list can be filtered by link, ip family. +func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) { + return h.neighList(linkIndex, family, NTF_PROXY) +} + +func (h *Handle) neighList(linkIndex, family, flags int) ([]Neigh, error) { + req := h.newNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP) msg := Ndmsg{ Family: uint8(family), Index: uint32(linkIndex), + Flags: uint8(flags), } req.AddData(&msg) diff --git a/vendor/github.com/vishvananda/netlink/netlink.go b/vendor/github.com/vishvananda/netlink/netlink.go index 687d8760..fb159526 100644 --- a/vendor/github.com/vishvananda/netlink/netlink.go +++ b/vendor/github.com/vishvananda/netlink/netlink.go @@ -9,16 +9,13 @@ package netlink import ( + "errors" "net" - - "github.com/vishvananda/netlink/nl" ) -const ( - // Family type definitions - FAMILY_ALL = nl.FAMILY_ALL - FAMILY_V4 = nl.FAMILY_V4 - FAMILY_V6 = nl.FAMILY_V6 +var ( + // ErrNotImplemented is returned when a requested feature is not implemented. + ErrNotImplemented = errors.New("not implemented") ) // ParseIPNet parses a string in ip/net format and returns a net.IPNet. diff --git a/vendor/github.com/vishvananda/netlink/netlink_linux.go b/vendor/github.com/vishvananda/netlink/netlink_linux.go new file mode 100644 index 00000000..a20d293d --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/netlink_linux.go @@ -0,0 +1,11 @@ +package netlink + +import "github.com/vishvananda/netlink/nl" + +// Family type definitions +const ( + FAMILY_ALL = nl.FAMILY_ALL + FAMILY_V4 = nl.FAMILY_V4 + FAMILY_V6 = nl.FAMILY_V6 + FAMILY_MPLS = nl.FAMILY_MPLS +) diff --git a/vendor/github.com/vishvananda/netlink/netlink_unspecified.go b/vendor/github.com/vishvananda/netlink/netlink_unspecified.go index 10c49c1b..fa421e4e 100644 --- a/vendor/github.com/vishvananda/netlink/netlink_unspecified.go +++ b/vendor/github.com/vishvananda/netlink/netlink_unspecified.go @@ -2,43 +2,109 @@ package netlink -import ( - "errors" -) +import "net" -var ( - ErrNotImplemented = errors.New("not implemented") -) - -func LinkSetUp(link *Link) error { +func LinkSetUp(link Link) error { return ErrNotImplemented } -func LinkSetDown(link *Link) error { +func LinkSetDown(link Link) error { return ErrNotImplemented } -func LinkSetMTU(link *Link, mtu int) error { +func LinkSetMTU(link Link, mtu int) error { return ErrNotImplemented } -func LinkSetMaster(link *Link, master *Link) error { +func LinkSetMaster(link Link, master *Link) error { return ErrNotImplemented } -func LinkSetNsPid(link *Link, nspid int) error { +func LinkSetNsPid(link Link, nspid int) error { return ErrNotImplemented } -func LinkSetNsFd(link *Link, fd int) error { +func LinkSetNsFd(link Link, fd int) error { return ErrNotImplemented } -func LinkAdd(link *Link) error { +func LinkSetName(link Link, name string) error { return ErrNotImplemented } -func LinkDel(link *Link) error { +func LinkSetAlias(link Link, name string) error { + return ErrNotImplemented +} + +func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { + return ErrNotImplemented +} + +func LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error { + return ErrNotImplemented +} + +func LinkSetVfVlan(link Link, vf, vlan int) error { + return ErrNotImplemented +} + +func LinkSetVfTxRate(link Link, vf, rate int) error { + return ErrNotImplemented +} + +func LinkSetNoMaster(link Link) error { + return ErrNotImplemented +} + +func LinkSetMasterByIndex(link Link, masterIndex int) error { + return ErrNotImplemented +} + +func LinkSetXdpFd(link Link, fd int) error { + return ErrNotImplemented +} + +func LinkByName(name string) (Link, error) { + return nil, ErrNotImplemented +} + +func LinkByAlias(alias string) (Link, error) { + return nil, ErrNotImplemented +} + +func LinkByIndex(index int) (Link, error) { + return nil, ErrNotImplemented +} + +func LinkSetHairpin(link Link, mode bool) error { + return ErrNotImplemented +} + +func LinkSetGuard(link Link, mode bool) error { + return ErrNotImplemented +} + +func LinkSetFastLeave(link Link, mode bool) error { + return ErrNotImplemented +} + +func LinkSetLearning(link Link, mode bool) error { + return ErrNotImplemented +} + +func LinkSetRootBlock(link Link, mode bool) error { + return ErrNotImplemented +} + +func LinkSetFlood(link Link, mode bool) error { + return ErrNotImplemented +} + +func LinkAdd(link Link) error { + return ErrNotImplemented +} + +func LinkDel(link Link) error { return ErrNotImplemented } @@ -70,15 +136,15 @@ func LinkList() ([]Link, error) { return nil, ErrNotImplemented } -func AddrAdd(link *Link, addr *Addr) error { +func AddrAdd(link Link, addr *Addr) error { return ErrNotImplemented } -func AddrDel(link *Link, addr *Addr) error { +func AddrDel(link Link, addr *Addr) error { return ErrNotImplemented } -func AddrList(link *Link, family int) ([]Addr, error) { +func AddrList(link Link, family int) ([]Addr, error) { return nil, ErrNotImplemented } @@ -90,7 +156,7 @@ func RouteDel(route *Route) error { return ErrNotImplemented } -func RouteList(link *Link, family int) ([]Route, error) { +func RouteList(link Link, family int) ([]Route, error) { return nil, ErrNotImplemented } @@ -138,6 +204,10 @@ func NeighList(linkIndex, family int) ([]Neigh, error) { return nil, ErrNotImplemented } -func NeighDeserialize(m []byte) (*Ndmsg, *Neigh, error) { - return nil, nil, ErrNotImplemented +func NeighDeserialize(m []byte) (*Neigh, error) { + return nil, ErrNotImplemented +} + +func SocketGet(local, remote net.Addr) (*Socket, error) { + return nil, ErrNotImplemented } diff --git a/vendor/github.com/vishvananda/netlink/nl/link_linux.go b/vendor/github.com/vishvananda/netlink/nl/link_linux.go index b7f50646..6d9af569 100644 --- a/vendor/github.com/vishvananda/netlink/nl/link_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/link_linux.go @@ -1,13 +1,35 @@ package nl import ( + "syscall" "unsafe" ) const ( DEFAULT_CHANGE = 0xFFFFFFFF // doesn't exist in syscall - IFLA_VFINFO_LIST = 0x16 + IFLA_VFINFO_LIST = syscall.IFLA_IFALIAS + 1 + iota + IFLA_STATS64 + IFLA_VF_PORTS + IFLA_PORT_SELF + IFLA_AF_SPEC + IFLA_GROUP + IFLA_NET_NS_FD + IFLA_EXT_MASK + IFLA_PROMISCUITY + IFLA_NUM_TX_QUEUES + IFLA_NUM_RX_QUEUES + IFLA_CARRIER + IFLA_PHYS_PORT_ID + IFLA_CARRIER_CHANGES + IFLA_PHYS_SWITCH_ID + IFLA_LINK_NETNSID + IFLA_PHYS_PORT_NAME + IFLA_PROTO_DOWN + IFLA_GSO_MAX_SEGS + IFLA_GSO_MAX_SIZE + IFLA_PAD + IFLA_XDP ) const ( @@ -89,11 +111,6 @@ const ( IFLA_IPVLAN_MAX = IFLA_IPVLAN_MODE ) -const ( - // not defined in syscall - IFLA_NET_NS_FD = 28 -) - const ( IFLA_MACVLAN_UNSPEC = iota IFLA_MACVLAN_MODE @@ -394,3 +411,44 @@ func DeserializeVfRssQueryEn(b []byte) *VfRssQueryEn { func (msg *VfRssQueryEn) Serialize() []byte { return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:] } + +const ( + IFLA_XDP_UNSPEC = iota + IFLA_XDP_FD /* fd of xdp program to attach, or -1 to remove */ + IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */ + IFLA_XDP_MAX = IFLA_XDP_ATTACHED +) + +const ( + IFLA_IPTUN_UNSPEC = iota + IFLA_IPTUN_LINK + IFLA_IPTUN_LOCAL + IFLA_IPTUN_REMOTE + IFLA_IPTUN_TTL + IFLA_IPTUN_TOS + IFLA_IPTUN_ENCAP_LIMIT + IFLA_IPTUN_FLOWINFO + IFLA_IPTUN_FLAGS + IFLA_IPTUN_PROTO + IFLA_IPTUN_PMTUDISC + IFLA_IPTUN_6RD_PREFIX + IFLA_IPTUN_6RD_RELAY_PREFIX + IFLA_IPTUN_6RD_PREFIXLEN + IFLA_IPTUN_6RD_RELAY_PREFIXLEN + IFLA_IPTUN_MAX = IFLA_IPTUN_6RD_RELAY_PREFIXLEN +) + +const ( + IFLA_VTI_UNSPEC = iota + IFLA_VTI_LINK + IFLA_VTI_IKEY + IFLA_VTI_OKEY + IFLA_VTI_LOCAL + IFLA_VTI_REMOTE + IFLA_VTI_MAX = IFLA_VTI_REMOTE +) + +const ( + IFLA_VRF_UNSPEC = iota + IFLA_VRF_TABLE +) diff --git a/vendor/github.com/vishvananda/netlink/nl/mpls_linux.go b/vendor/github.com/vishvananda/netlink/nl/mpls_linux.go new file mode 100644 index 00000000..3915b7ee --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/mpls_linux.go @@ -0,0 +1,36 @@ +package nl + +import "encoding/binary" + +const ( + MPLS_LS_LABEL_SHIFT = 12 + MPLS_LS_S_SHIFT = 8 +) + +func EncodeMPLSStack(labels ...int) []byte { + b := make([]byte, 4*len(labels)) + for idx, label := range labels { + l := label << MPLS_LS_LABEL_SHIFT + if idx == len(labels)-1 { + l |= 1 << MPLS_LS_S_SHIFT + } + binary.BigEndian.PutUint32(b[idx*4:], uint32(l)) + } + return b +} + +func DecodeMPLSStack(buf []byte) []int { + if len(buf)%4 != 0 { + return nil + } + stack := make([]int, 0, len(buf)/4) + for len(buf) > 0 { + l := binary.BigEndian.Uint32(buf[:4]) + buf = buf[4:] + stack = append(stack, int(l)>>MPLS_LS_LABEL_SHIFT) + if (l>>MPLS_LS_S_SHIFT)&1 > 0 { + break + } + } + return stack +} diff --git a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go index e3afb5c2..fb9031e3 100644 --- a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go @@ -6,18 +6,26 @@ import ( "encoding/binary" "fmt" "net" + "runtime" + "sync" "sync/atomic" "syscall" "unsafe" + + "github.com/vishvananda/netns" ) const ( // Family type definitions - FAMILY_ALL = syscall.AF_UNSPEC - FAMILY_V4 = syscall.AF_INET - FAMILY_V6 = syscall.AF_INET6 + FAMILY_ALL = syscall.AF_UNSPEC + FAMILY_V4 = syscall.AF_INET + FAMILY_V6 = syscall.AF_INET6 + FAMILY_MPLS = AF_MPLS ) +// SupportedNlFamilies contains the list of netlink families this netlink package supports +var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM} + var nextSeqNr uint32 // GetIPFamily returns the family type of a net.IP. @@ -93,6 +101,147 @@ func (msg *IfInfomsg) Len() int { return syscall.SizeofIfInfomsg } +func (msg *IfInfomsg) EncapType() string { + switch msg.Type { + case 0: + return "generic" + case syscall.ARPHRD_ETHER: + return "ether" + case syscall.ARPHRD_EETHER: + return "eether" + case syscall.ARPHRD_AX25: + return "ax25" + case syscall.ARPHRD_PRONET: + return "pronet" + case syscall.ARPHRD_CHAOS: + return "chaos" + case syscall.ARPHRD_IEEE802: + return "ieee802" + case syscall.ARPHRD_ARCNET: + return "arcnet" + case syscall.ARPHRD_APPLETLK: + return "atalk" + case syscall.ARPHRD_DLCI: + return "dlci" + case syscall.ARPHRD_ATM: + return "atm" + case syscall.ARPHRD_METRICOM: + return "metricom" + case syscall.ARPHRD_IEEE1394: + return "ieee1394" + case syscall.ARPHRD_INFINIBAND: + return "infiniband" + case syscall.ARPHRD_SLIP: + return "slip" + case syscall.ARPHRD_CSLIP: + return "cslip" + case syscall.ARPHRD_SLIP6: + return "slip6" + case syscall.ARPHRD_CSLIP6: + return "cslip6" + case syscall.ARPHRD_RSRVD: + return "rsrvd" + case syscall.ARPHRD_ADAPT: + return "adapt" + case syscall.ARPHRD_ROSE: + return "rose" + case syscall.ARPHRD_X25: + return "x25" + case syscall.ARPHRD_HWX25: + return "hwx25" + case syscall.ARPHRD_PPP: + return "ppp" + case syscall.ARPHRD_HDLC: + return "hdlc" + case syscall.ARPHRD_LAPB: + return "lapb" + case syscall.ARPHRD_DDCMP: + return "ddcmp" + case syscall.ARPHRD_RAWHDLC: + return "rawhdlc" + case syscall.ARPHRD_TUNNEL: + return "ipip" + case syscall.ARPHRD_TUNNEL6: + return "tunnel6" + case syscall.ARPHRD_FRAD: + return "frad" + case syscall.ARPHRD_SKIP: + return "skip" + case syscall.ARPHRD_LOOPBACK: + return "loopback" + case syscall.ARPHRD_LOCALTLK: + return "ltalk" + case syscall.ARPHRD_FDDI: + return "fddi" + case syscall.ARPHRD_BIF: + return "bif" + case syscall.ARPHRD_SIT: + return "sit" + case syscall.ARPHRD_IPDDP: + return "ip/ddp" + case syscall.ARPHRD_IPGRE: + return "gre" + case syscall.ARPHRD_PIMREG: + return "pimreg" + case syscall.ARPHRD_HIPPI: + return "hippi" + case syscall.ARPHRD_ASH: + return "ash" + case syscall.ARPHRD_ECONET: + return "econet" + case syscall.ARPHRD_IRDA: + return "irda" + case syscall.ARPHRD_FCPP: + return "fcpp" + case syscall.ARPHRD_FCAL: + return "fcal" + case syscall.ARPHRD_FCPL: + return "fcpl" + case syscall.ARPHRD_FCFABRIC: + return "fcfb0" + case syscall.ARPHRD_FCFABRIC + 1: + return "fcfb1" + case syscall.ARPHRD_FCFABRIC + 2: + return "fcfb2" + case syscall.ARPHRD_FCFABRIC + 3: + return "fcfb3" + case syscall.ARPHRD_FCFABRIC + 4: + return "fcfb4" + case syscall.ARPHRD_FCFABRIC + 5: + return "fcfb5" + case syscall.ARPHRD_FCFABRIC + 6: + return "fcfb6" + case syscall.ARPHRD_FCFABRIC + 7: + return "fcfb7" + case syscall.ARPHRD_FCFABRIC + 8: + return "fcfb8" + case syscall.ARPHRD_FCFABRIC + 9: + return "fcfb9" + case syscall.ARPHRD_FCFABRIC + 10: + return "fcfb10" + case syscall.ARPHRD_FCFABRIC + 11: + return "fcfb11" + case syscall.ARPHRD_FCFABRIC + 12: + return "fcfb12" + case syscall.ARPHRD_IEEE802_TR: + return "tr" + case syscall.ARPHRD_IEEE80211: + return "ieee802.11" + case syscall.ARPHRD_IEEE80211_PRISM: + return "ieee802.11/prism" + case syscall.ARPHRD_IEEE80211_RADIOTAP: + return "ieee802.11/radiotap" + case syscall.ARPHRD_IEEE802154: + return "ieee802.15.4" + + case 65534: + return "none" + case 65535: + return "void" + } + return fmt.Sprintf("unknown%d", msg.Type) +} + func rtaAlignOf(attrlen int) int { return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) } @@ -171,7 +320,8 @@ func (a *RtAttr) Serialize() []byte { type NetlinkRequest struct { syscall.NlMsghdr - Data []NetlinkRequestData + Data []NetlinkRequestData + Sockets map[int]*SocketHandle } // Serialize the Netlink Request into a byte array @@ -203,14 +353,32 @@ func (req *NetlinkRequest) AddData(data NetlinkRequestData) { } // Execute the request against a the given sockType. -// Returns a list of netlink messages in seriaized format, optionally filtered +// Returns a list of netlink messages in serialized format, optionally filtered // by resType. func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) { - s, err := getNetlinkSocket(sockType) - if err != nil { - return nil, err + var ( + s *NetlinkSocket + err error + ) + + if req.Sockets != nil { + if sh, ok := req.Sockets[sockType]; ok { + s = sh.Socket + req.Seq = atomic.AddUint32(&sh.Seq, 1) + } + } + sharedSocket := s != nil + + if s == nil { + s, err = getNetlinkSocket(sockType) + if err != nil { + return nil, err + } + defer s.Close() + } else { + s.Lock() + defer s.Unlock() } - defer s.Close() if err := s.Send(req); err != nil { return nil, err @@ -231,7 +399,10 @@ done: } for _, m := range msgs { if m.Header.Seq != req.Seq { - return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq) + if sharedSocket { + continue + } + return nil, fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, req.Seq) } if m.Header.Pid != pid { return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid) @@ -276,6 +447,7 @@ func NewNetlinkRequest(proto, flags int) *NetlinkRequest { type NetlinkSocket struct { fd int lsa syscall.SockaddrNetlink + sync.Mutex } func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { @@ -295,6 +467,71 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { return s, nil } +// GetNetlinkSocketAt opens a netlink socket in the network namespace newNs +// and positions the thread back into the network namespace specified by curNs, +// when done. If curNs is close, the function derives the current namespace and +// moves back into it when done. If newNs is close, the socket will be opened +// in the current network namespace. +func GetNetlinkSocketAt(newNs, curNs netns.NsHandle, protocol int) (*NetlinkSocket, error) { + c, err := executeInNetns(newNs, curNs) + if err != nil { + return nil, err + } + defer c() + return getNetlinkSocket(protocol) +} + +// executeInNetns sets execution of the code following this call to the +// network namespace newNs, then moves the thread back to curNs if open, +// otherwise to the current netns at the time the function was invoked +// In case of success, the caller is expected to execute the returned function +// at the end of the code that needs to be executed in the network namespace. +// Example: +// func jobAt(...) error { +// d, err := executeInNetns(...) +// if err != nil { return err} +// defer d() +// < code which needs to be executed in specific netns> +// } +// TODO: his function probably belongs to netns pkg. +func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) { + var ( + err error + moveBack func(netns.NsHandle) error + closeNs func() error + unlockThd func() + ) + restore := func() { + // order matters + if moveBack != nil { + moveBack(curNs) + } + if closeNs != nil { + closeNs() + } + if unlockThd != nil { + unlockThd() + } + } + if newNs.IsOpen() { + runtime.LockOSThread() + unlockThd = runtime.UnlockOSThread + if !curNs.IsOpen() { + if curNs, err = netns.Get(); err != nil { + restore() + return nil, fmt.Errorf("could not get current namespace while creating netlink socket: %v", err) + } + closeNs = curNs.Close + } + if err := netns.Set(newNs); err != nil { + restore() + return nil, fmt.Errorf("failed to set into network namespace %d while creating netlink socket: %v", newNs, err) + } + moveBack = netns.Set + } + return restore, nil +} + // Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE) // and subscribe it to multicast groups passed in variable argument list. // Returns the netlink socket on which Receive() method can be called @@ -321,8 +558,21 @@ func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) { return s, nil } +// SubscribeAt works like Subscribe plus let's the caller choose the network +// namespace in which the socket would be opened (newNs). Then control goes back +// to curNs if open, otherwise to the netns at the time this function was called. +func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*NetlinkSocket, error) { + c, err := executeInNetns(newNs, curNs) + if err != nil { + return nil, err + } + defer c() + return Subscribe(protocol, groups...) +} + func (s *NetlinkSocket) Close() { syscall.Close(s.fd) + s.fd = -1 } func (s *NetlinkSocket) GetFd() int { @@ -330,6 +580,9 @@ func (s *NetlinkSocket) GetFd() int { } func (s *NetlinkSocket) Send(request *NetlinkRequest) error { + if s.fd < 0 { + return fmt.Errorf("Send called on a closed socket") + } if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil { return err } @@ -337,6 +590,9 @@ func (s *NetlinkSocket) Send(request *NetlinkRequest) error { } func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) { + if s.fd < 0 { + return nil, fmt.Errorf("Receive called on a closed socket") + } rb := make([]byte, syscall.Getpagesize()) nr, _, err := syscall.Recvfrom(s.fd, rb, 0) if err != nil { @@ -401,6 +657,13 @@ func Uint32Attr(v uint32) []byte { return bytes } +func Uint64Attr(v uint64) []byte { + native := NativeEndian() + bytes := make([]byte, 8) + native.PutUint64(bytes, v) + return bytes +} + func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) { var attrs []syscall.NetlinkRouteAttr for len(b) >= syscall.SizeofRtAttr { @@ -422,3 +685,17 @@ func netlinkRouteAttrAndValue(b []byte) (*syscall.RtAttr, []byte, int, error) { } return a, b[syscall.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil } + +// SocketHandle contains the netlink socket and the associated +// sequence counter for a specific netlink family +type SocketHandle struct { + Seq uint32 + Socket *NetlinkSocket +} + +// Close closes the netlink socket +func (sh *SocketHandle) Close() { + if sh.Socket != nil { + sh.Socket.Close() + } +} diff --git a/vendor/github.com/vishvananda/netlink/nl/nl_unspecified.go b/vendor/github.com/vishvananda/netlink/nl/nl_unspecified.go new file mode 100644 index 00000000..dfc0be66 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/nl_unspecified.go @@ -0,0 +1,11 @@ +// +build !linux + +package nl + +import "encoding/binary" + +var SupportedNlFamilies = []int{} + +func NativeEndian() binary.ByteOrder { + return nil +} diff --git a/vendor/github.com/vishvananda/netlink/nl/route_linux.go b/vendor/github.com/vishvananda/netlink/nl/route_linux.go index 447e83e5..1a064d65 100644 --- a/vendor/github.com/vishvananda/netlink/nl/route_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/route_linux.go @@ -40,3 +40,41 @@ func DeserializeRtMsg(b []byte) *RtMsg { func (msg *RtMsg) Serialize() []byte { return (*(*[syscall.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:] } + +type RtNexthop struct { + syscall.RtNexthop + Children []NetlinkRequestData +} + +func DeserializeRtNexthop(b []byte) *RtNexthop { + return (*RtNexthop)(unsafe.Pointer(&b[0:syscall.SizeofRtNexthop][0])) +} + +func (msg *RtNexthop) Len() int { + if len(msg.Children) == 0 { + return syscall.SizeofRtNexthop + } + + l := 0 + for _, child := range msg.Children { + l += rtaAlignOf(child.Len()) + } + l += syscall.SizeofRtNexthop + return rtaAlignOf(l) +} + +func (msg *RtNexthop) Serialize() []byte { + length := msg.Len() + msg.RtNexthop.Len = uint16(length) + buf := make([]byte, length) + copy(buf, (*(*[syscall.SizeofRtNexthop]byte)(unsafe.Pointer(msg)))[:]) + next := rtaAlignOf(syscall.SizeofRtNexthop) + if len(msg.Children) > 0 { + for _, child := range msg.Children { + childBuf := child.Serialize() + copy(buf[next:], childBuf) + next += rtaAlignOf(len(childBuf)) + } + } + return buf +} diff --git a/vendor/github.com/vishvananda/netlink/nl/syscall.go b/vendor/github.com/vishvananda/netlink/nl/syscall.go index 47aa6322..3473e536 100644 --- a/vendor/github.com/vishvananda/netlink/nl/syscall.go +++ b/vendor/github.com/vishvananda/netlink/nl/syscall.go @@ -35,3 +35,34 @@ const ( FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */ FR_ACT_PROHIBIT /* Drop with EACCES */ ) + +// socket diags related +const ( + SOCK_DIAG_BY_FAMILY = 20 /* linux.sock_diag.h */ + TCPDIAG_NOCOOKIE = 0xFFFFFFFF /* TCPDIAG_NOCOOKIE in net/ipv4/tcp_diag.h*/ +) + +const ( + AF_MPLS = 28 +) + +const ( + RTA_NEWDST = 0x13 + RTA_ENCAP_TYPE = 0x15 + RTA_ENCAP = 0x16 +) + +// RTA_ENCAP subtype +const ( + MPLS_IPTUNNEL_UNSPEC = iota + MPLS_IPTUNNEL_DST +) + +// light weight tunnel encap types +const ( + LWTUNNEL_ENCAP_NONE = iota + LWTUNNEL_ENCAP_MPLS + LWTUNNEL_ENCAP_IP + LWTUNNEL_ENCAP_ILA + LWTUNNEL_ENCAP_IP6 +) diff --git a/vendor/github.com/vishvananda/netlink/nl/tc_linux.go b/vendor/github.com/vishvananda/netlink/nl/tc_linux.go index aa590057..e91fb21c 100644 --- a/vendor/github.com/vishvananda/netlink/nl/tc_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/tc_linux.go @@ -49,6 +49,15 @@ const ( TCAA_MAX = 1 ) +const ( + TCA_ACT_UNSPEC = iota + TCA_ACT_KIND + TCA_ACT_OPTIONS + TCA_ACT_INDEX + TCA_ACT_STATS + TCA_ACT_MAX +) + const ( TCA_PRIO_UNSPEC = iota TCA_PRIO_MQ @@ -69,7 +78,8 @@ const ( SizeofTcHtbGlob = 0x14 SizeofTcU32Key = 0x10 SizeofTcU32Sel = 0x10 // without keys - SizeofTcMirred = 0x1c + SizeofTcGen = 0x14 + SizeofTcMirred = SizeofTcGen + 0x08 SizeofTcPolice = 2*SizeofTcRateSpec + 0x20 ) @@ -506,6 +516,81 @@ func (x *TcU32Sel) Serialize() []byte { return buf } +type TcGen struct { + Index uint32 + Capab uint32 + Action int32 + Refcnt int32 + Bindcnt int32 +} + +func (msg *TcGen) Len() int { + return SizeofTcGen +} + +func DeserializeTcGen(b []byte) *TcGen { + return (*TcGen)(unsafe.Pointer(&b[0:SizeofTcGen][0])) +} + +func (x *TcGen) Serialize() []byte { + return (*(*[SizeofTcGen]byte)(unsafe.Pointer(x)))[:] +} + +// #define tc_gen \ +// __u32 index; \ +// __u32 capab; \ +// int action; \ +// int refcnt; \ +// int bindcnt + +const ( + TCA_ACT_GACT = 5 +) + +const ( + TCA_GACT_UNSPEC = iota + TCA_GACT_TM + TCA_GACT_PARMS + TCA_GACT_PROB + TCA_GACT_MAX = TCA_GACT_PROB +) + +type TcGact TcGen + +const ( + TCA_ACT_BPF = 13 +) + +const ( + TCA_ACT_BPF_UNSPEC = iota + TCA_ACT_BPF_TM + TCA_ACT_BPF_PARMS + TCA_ACT_BPF_OPS_LEN + TCA_ACT_BPF_OPS + TCA_ACT_BPF_FD + TCA_ACT_BPF_NAME + TCA_ACT_BPF_MAX = TCA_ACT_BPF_NAME +) + +const ( + TCA_BPF_FLAG_ACT_DIRECT uint32 = 1 << iota +) + +const ( + TCA_BPF_UNSPEC = iota + TCA_BPF_ACT + TCA_BPF_POLICE + TCA_BPF_CLASSID + TCA_BPF_OPS_LEN + TCA_BPF_OPS + TCA_BPF_FD + TCA_BPF_NAME + TCA_BPF_FLAGS + TCA_BPF_MAX = TCA_BPF_FLAGS +) + +type TcBpf TcGen + const ( TCA_ACT_MIRRED = 8 ) @@ -517,31 +602,6 @@ const ( TCA_MIRRED_MAX = TCA_MIRRED_PARMS ) -const ( - TCA_EGRESS_REDIR = 1 /* packet redirect to EGRESS*/ - TCA_EGRESS_MIRROR = 2 /* mirror packet to EGRESS */ - TCA_INGRESS_REDIR = 3 /* packet redirect to INGRESS*/ - TCA_INGRESS_MIRROR = 4 /* mirror packet to INGRESS */ -) - -const ( - TC_ACT_UNSPEC = int32(-1) - TC_ACT_OK = 0 - TC_ACT_RECLASSIFY = 1 - TC_ACT_SHOT = 2 - TC_ACT_PIPE = 3 - TC_ACT_STOLEN = 4 - TC_ACT_QUEUED = 5 - TC_ACT_REPEAT = 6 - TC_ACT_JUMP = 0x10000000 -) - -// #define tc_gen \ -// __u32 index; \ -// __u32 capab; \ -// int action; \ -// int refcnt; \ -// int bindcnt // struct tc_mirred { // tc_gen; // int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ @@ -549,11 +609,7 @@ const ( // }; type TcMirred struct { - Index uint32 - Capab uint32 - Action int32 - Refcnt int32 - Bindcnt int32 + TcGen Eaction int32 Ifindex uint32 } @@ -570,14 +626,6 @@ func (x *TcMirred) Serialize() []byte { return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:] } -const ( - TC_POLICE_UNSPEC = TC_ACT_UNSPEC - TC_POLICE_OK = TC_ACT_OK - TC_POLICE_RECLASSIFY = TC_ACT_RECLASSIFY - TC_POLICE_SHOT = TC_ACT_SHOT - TC_POLICE_PIPE = TC_ACT_PIPE -) - // struct tc_police { // __u32 index; // int action; diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_linux.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_linux.go index d24637d2..09a2ffa1 100644 --- a/vendor/github.com/vishvananda/netlink/nl/xfrm_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/xfrm_linux.go @@ -11,34 +11,40 @@ const ( XFRM_INF = ^uint64(0) ) +type XfrmMsgType uint8 + +type XfrmMsg interface { + Type() XfrmMsgType +} + // Message Types const ( - XFRM_MSG_BASE = 0x10 - XFRM_MSG_NEWSA = 0x10 - XFRM_MSG_DELSA = 0x11 - XFRM_MSG_GETSA = 0x12 - XFRM_MSG_NEWPOLICY = 0x13 - XFRM_MSG_DELPOLICY = 0x14 - XFRM_MSG_GETPOLICY = 0x15 - XFRM_MSG_ALLOCSPI = 0x16 - XFRM_MSG_ACQUIRE = 0x17 - XFRM_MSG_EXPIRE = 0x18 - XFRM_MSG_UPDPOLICY = 0x19 - XFRM_MSG_UPDSA = 0x1a - XFRM_MSG_POLEXPIRE = 0x1b - XFRM_MSG_FLUSHSA = 0x1c - XFRM_MSG_FLUSHPOLICY = 0x1d - XFRM_MSG_NEWAE = 0x1e - XFRM_MSG_GETAE = 0x1f - XFRM_MSG_REPORT = 0x20 - XFRM_MSG_MIGRATE = 0x21 - XFRM_MSG_NEWSADINFO = 0x22 - XFRM_MSG_GETSADINFO = 0x23 - XFRM_MSG_NEWSPDINFO = 0x24 - XFRM_MSG_GETSPDINFO = 0x25 - XFRM_MSG_MAPPING = 0x26 - XFRM_MSG_MAX = 0x26 - XFRM_NR_MSGTYPES = 0x17 + XFRM_MSG_BASE XfrmMsgType = 0x10 + XFRM_MSG_NEWSA = 0x10 + XFRM_MSG_DELSA = 0x11 + XFRM_MSG_GETSA = 0x12 + XFRM_MSG_NEWPOLICY = 0x13 + XFRM_MSG_DELPOLICY = 0x14 + XFRM_MSG_GETPOLICY = 0x15 + XFRM_MSG_ALLOCSPI = 0x16 + XFRM_MSG_ACQUIRE = 0x17 + XFRM_MSG_EXPIRE = 0x18 + XFRM_MSG_UPDPOLICY = 0x19 + XFRM_MSG_UPDSA = 0x1a + XFRM_MSG_POLEXPIRE = 0x1b + XFRM_MSG_FLUSHSA = 0x1c + XFRM_MSG_FLUSHPOLICY = 0x1d + XFRM_MSG_NEWAE = 0x1e + XFRM_MSG_GETAE = 0x1f + XFRM_MSG_REPORT = 0x20 + XFRM_MSG_MIGRATE = 0x21 + XFRM_MSG_NEWSADINFO = 0x22 + XFRM_MSG_GETSADINFO = 0x23 + XFRM_MSG_NEWSPDINFO = 0x24 + XFRM_MSG_GETSPDINFO = 0x25 + XFRM_MSG_MAPPING = 0x26 + XFRM_MSG_MAX = 0x26 + XFRM_NR_MSGTYPES = 0x17 ) // Attribute types @@ -78,6 +84,21 @@ const ( SizeofXfrmLifetimeCfg = 0x40 SizeofXfrmLifetimeCur = 0x20 SizeofXfrmId = 0x18 + SizeofXfrmMark = 0x08 +) + +// Netlink groups +const ( + XFRMNLGRP_NONE = 0x0 + XFRMNLGRP_ACQUIRE = 0x1 + XFRMNLGRP_EXPIRE = 0x2 + XFRMNLGRP_SA = 0x3 + XFRMNLGRP_POLICY = 0x4 + XFRMNLGRP_AEVENTS = 0x5 + XFRMNLGRP_REPORT = 0x6 + XFRMNLGRP_MIGRATE = 0x7 + XFRMNLGRP_MAPPING = 0x8 + __XFRMNLGRP_MAX = 0x9 ) // typedef union { @@ -256,3 +277,20 @@ func DeserializeXfrmId(b []byte) *XfrmId { func (msg *XfrmId) Serialize() []byte { return (*(*[SizeofXfrmId]byte)(unsafe.Pointer(msg)))[:] } + +type XfrmMark struct { + Value uint32 + Mask uint32 +} + +func (msg *XfrmMark) Len() int { + return SizeofXfrmMark +} + +func DeserializeXfrmMark(b []byte) *XfrmMark { + return (*XfrmMark)(unsafe.Pointer(&b[0:SizeofXfrmMark][0])) +} + +func (msg *XfrmMark) Serialize() []byte { + return (*(*[SizeofXfrmMark]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux.go new file mode 100644 index 00000000..715df4cc --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux.go @@ -0,0 +1,32 @@ +package nl + +import ( + "unsafe" +) + +const ( + SizeofXfrmUserExpire = 0xe8 +) + +// struct xfrm_user_expire { +// struct xfrm_usersa_info state; +// __u8 hard; +// }; + +type XfrmUserExpire struct { + XfrmUsersaInfo XfrmUsersaInfo + Hard uint8 + Pad [7]byte +} + +func (msg *XfrmUserExpire) Len() int { + return SizeofXfrmUserExpire +} + +func DeserializeXfrmUserExpire(b []byte) *XfrmUserExpire { + return (*XfrmUserExpire)(unsafe.Pointer(&b[0:SizeofXfrmUserExpire][0])) +} + +func (msg *XfrmUserExpire) Serialize() []byte { + return (*(*[SizeofXfrmUserExpire]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go index 4876ce45..b6290fd5 100644 --- a/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go @@ -5,12 +5,27 @@ import ( ) const ( - SizeofXfrmUsersaId = 0x18 - SizeofXfrmStats = 0x0c - SizeofXfrmUsersaInfo = 0xe0 - SizeofXfrmAlgo = 0x44 - SizeofXfrmAlgoAuth = 0x48 - SizeofXfrmEncapTmpl = 0x18 + SizeofXfrmUsersaId = 0x18 + SizeofXfrmStats = 0x0c + SizeofXfrmUsersaInfo = 0xe0 + SizeofXfrmUserSpiInfo = 0xe8 + SizeofXfrmAlgo = 0x44 + SizeofXfrmAlgoAuth = 0x48 + SizeofXfrmAlgoAEAD = 0x48 + SizeofXfrmEncapTmpl = 0x18 + SizeofXfrmUsersaFlush = 0x8 + SizeofXfrmReplayStateEsn = 0x18 +) + +const ( + XFRM_STATE_NOECN = 1 + XFRM_STATE_DECAP_DSCP = 2 + XFRM_STATE_NOPMTUDISC = 4 + XFRM_STATE_WILDRECV = 8 + XFRM_STATE_ICMP = 16 + XFRM_STATE_AF_UNSPEC = 32 + XFRM_STATE_ALIGN4 = 64 + XFRM_STATE_ESN = 128 ) // struct xfrm_usersa_id { @@ -118,6 +133,30 @@ func (msg *XfrmUsersaInfo) Serialize() []byte { return (*(*[SizeofXfrmUsersaInfo]byte)(unsafe.Pointer(msg)))[:] } +// struct xfrm_userspi_info { +// struct xfrm_usersa_info info; +// __u32 min; +// __u32 max; +// }; + +type XfrmUserSpiInfo struct { + XfrmUsersaInfo XfrmUsersaInfo + Min uint32 + Max uint32 +} + +func (msg *XfrmUserSpiInfo) Len() int { + return SizeofXfrmUserSpiInfo +} + +func DeserializeXfrmUserSpiInfo(b []byte) *XfrmUserSpiInfo { + return (*XfrmUserSpiInfo)(unsafe.Pointer(&b[0:SizeofXfrmUserSpiInfo][0])) +} + +func (msg *XfrmUserSpiInfo) Serialize() []byte { + return (*(*[SizeofXfrmUserSpiInfo]byte)(unsafe.Pointer(msg)))[:] +} + // struct xfrm_algo { // char alg_name[64]; // unsigned int alg_key_len; /* in bits */ @@ -193,6 +232,35 @@ func (msg *XfrmAlgoAuth) Serialize() []byte { // char alg_key[0]; // } +type XfrmAlgoAEAD struct { + AlgName [64]byte + AlgKeyLen uint32 + AlgICVLen uint32 + AlgKey []byte +} + +func (msg *XfrmAlgoAEAD) Len() int { + return SizeofXfrmAlgoAEAD + int(msg.AlgKeyLen/8) +} + +func DeserializeXfrmAlgoAEAD(b []byte) *XfrmAlgoAEAD { + ret := XfrmAlgoAEAD{} + copy(ret.AlgName[:], b[0:64]) + ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64])) + ret.AlgICVLen = *(*uint32)(unsafe.Pointer(&b[68])) + ret.AlgKey = b[72:ret.Len()] + return &ret +} + +func (msg *XfrmAlgoAEAD) Serialize() []byte { + b := make([]byte, msg.Len()) + copy(b[0:64], msg.AlgName[:]) + copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:]) + copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgICVLen)))[:]) + copy(b[72:msg.Len()], msg.AlgKey[:]) + return b +} + // struct xfrm_encap_tmpl { // __u16 encap_type; // __be16 encap_sport; @@ -219,3 +287,48 @@ func DeserializeXfrmEncapTmpl(b []byte) *XfrmEncapTmpl { func (msg *XfrmEncapTmpl) Serialize() []byte { return (*(*[SizeofXfrmEncapTmpl]byte)(unsafe.Pointer(msg)))[:] } + +// struct xfrm_usersa_flush { +// __u8 proto; +// }; + +type XfrmUsersaFlush struct { + Proto uint8 +} + +func (msg *XfrmUsersaFlush) Len() int { + return SizeofXfrmUsersaFlush +} + +func DeserializeXfrmUsersaFlush(b []byte) *XfrmUsersaFlush { + return (*XfrmUsersaFlush)(unsafe.Pointer(&b[0:SizeofXfrmUsersaFlush][0])) +} + +func (msg *XfrmUsersaFlush) Serialize() []byte { + return (*(*[SizeofXfrmUsersaFlush]byte)(unsafe.Pointer(msg)))[:] +} + +// struct xfrm_replay_state_esn { +// unsigned int bmp_len; +// __u32 oseq; +// __u32 seq; +// __u32 oseq_hi; +// __u32 seq_hi; +// __u32 replay_window; +// __u32 bmp[0]; +// }; + +type XfrmReplayStateEsn struct { + BmpLen uint32 + OSeq uint32 + Seq uint32 + OSeqHi uint32 + SeqHi uint32 + ReplayWindow uint32 + Bmp []uint32 +} + +func (msg *XfrmReplayStateEsn) Serialize() []byte { + // We deliberately do not pass Bmp, as it gets set by the kernel. + return (*(*[SizeofXfrmReplayStateEsn]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/github.com/vishvananda/netlink/order.go b/vendor/github.com/vishvananda/netlink/order.go new file mode 100644 index 00000000..e28e153a --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/order.go @@ -0,0 +1,32 @@ +package netlink + +import ( + "encoding/binary" + + "github.com/vishvananda/netlink/nl" +) + +var ( + native = nl.NativeEndian() + networkOrder = binary.BigEndian +) + +func htonl(val uint32) []byte { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, val) + return bytes +} + +func htons(val uint16) []byte { + bytes := make([]byte, 2) + binary.BigEndian.PutUint16(bytes, val) + return bytes +} + +func ntohl(buf []byte) uint32 { + return binary.BigEndian.Uint32(buf) +} + +func ntohs(buf []byte) uint16 { + return binary.BigEndian.Uint16(buf) +} diff --git a/vendor/github.com/vishvananda/netlink/protinfo.go b/vendor/github.com/vishvananda/netlink/protinfo.go index f39ab8f4..ead3f2f1 100644 --- a/vendor/github.com/vishvananda/netlink/protinfo.go +++ b/vendor/github.com/vishvananda/netlink/protinfo.go @@ -46,8 +46,5 @@ func boolToByte(x bool) []byte { } func byteToBool(x byte) bool { - if uint8(x) != 0 { - return true - } - return false + return uint8(x) != 0 } diff --git a/vendor/github.com/vishvananda/netlink/protinfo_linux.go b/vendor/github.com/vishvananda/netlink/protinfo_linux.go index 7181eba1..ea726953 100644 --- a/vendor/github.com/vishvananda/netlink/protinfo_linux.go +++ b/vendor/github.com/vishvananda/netlink/protinfo_linux.go @@ -8,10 +8,14 @@ import ( ) func LinkGetProtinfo(link Link) (Protinfo, error) { + return pkgHandle.LinkGetProtinfo(link) +} + +func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) { base := link.Attrs() - ensureIndex(base) + h.ensureIndex(base) var pi Protinfo - req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) + req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) req.AddData(msg) msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0) @@ -36,25 +40,31 @@ func LinkGetProtinfo(link Link) (Protinfo, error) { if err != nil { return pi, err } - var pi Protinfo - for _, info := range infos { - switch info.Attr.Type { - case nl.IFLA_BRPORT_MODE: - pi.Hairpin = byteToBool(info.Value[0]) - case nl.IFLA_BRPORT_GUARD: - pi.Guard = byteToBool(info.Value[0]) - case nl.IFLA_BRPORT_FAST_LEAVE: - pi.FastLeave = byteToBool(info.Value[0]) - case nl.IFLA_BRPORT_PROTECT: - pi.RootBlock = byteToBool(info.Value[0]) - case nl.IFLA_BRPORT_LEARNING: - pi.Learning = byteToBool(info.Value[0]) - case nl.IFLA_BRPORT_UNICAST_FLOOD: - pi.Flood = byteToBool(info.Value[0]) - } - } + pi = *parseProtinfo(infos) + return pi, nil } } return pi, fmt.Errorf("Device with index %d not found", base.Index) } + +func parseProtinfo(infos []syscall.NetlinkRouteAttr) *Protinfo { + var pi Protinfo + for _, info := range infos { + switch info.Attr.Type { + case nl.IFLA_BRPORT_MODE: + pi.Hairpin = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_GUARD: + pi.Guard = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_FAST_LEAVE: + pi.FastLeave = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_PROTECT: + pi.RootBlock = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_LEARNING: + pi.Learning = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_UNICAST_FLOOD: + pi.Flood = byteToBool(info.Value[0]) + } + } + return &pi +} diff --git a/vendor/github.com/vishvananda/netlink/qdisc.go b/vendor/github.com/vishvananda/netlink/qdisc.go index 48fe7c79..0ca86ebe 100644 --- a/vendor/github.com/vishvananda/netlink/qdisc.go +++ b/vendor/github.com/vishvananda/netlink/qdisc.go @@ -8,16 +8,21 @@ import ( const ( HANDLE_NONE = 0 HANDLE_INGRESS = 0xFFFFFFF1 + HANDLE_CLSACT = HANDLE_INGRESS HANDLE_ROOT = 0xFFFFFFFF PRIORITY_MAP_LEN = 16 ) +const ( + HANDLE_MIN_INGRESS = 0xFFFFFFF2 + HANDLE_MIN_EGRESS = 0xFFFFFFF3 +) type Qdisc interface { Attrs() *QdiscAttrs Type() string } -// Qdisc represents a netlink qdisc. A qdisc is associated with a link, +// QdiscAttrs represents a netlink qdisc. A qdisc is associated with a link, // has a handle, a parent and a refcnt. The root qdisc of a device should // have parent == HANDLE_ROOT. type QdiscAttrs struct { @@ -28,7 +33,7 @@ type QdiscAttrs struct { } func (q QdiscAttrs) String() string { - return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Refcnt: %s}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Refcnt) + return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Refcnt: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Refcnt) } func MakeHandle(major, minor uint16) uint32 { @@ -149,7 +154,7 @@ type NetemQdiscAttrs struct { func (q NetemQdiscAttrs) String() string { return fmt.Sprintf( - "{Latency: %d, Limit: %d, Loss: %d, Gap: %d, Duplicate: %d, Jitter: %d}", + "{Latency: %d, Limit: %d, Loss: %f, Gap: %d, Duplicate: %f, Jitter: %d}", q.Latency, q.Limit, q.Loss, q.Gap, q.Duplicate, q.Jitter, ) } @@ -171,70 +176,6 @@ type Netem struct { CorruptCorr uint32 } -func NewNetem(attrs QdiscAttrs, nattrs NetemQdiscAttrs) *Netem { - var limit uint32 = 1000 - var loss_corr, delay_corr, duplicate_corr uint32 - var reorder_prob, reorder_corr uint32 - var corrupt_prob, corrupt_corr uint32 - - latency := nattrs.Latency - loss := Percentage2u32(nattrs.Loss) - gap := nattrs.Gap - duplicate := Percentage2u32(nattrs.Duplicate) - jitter := nattrs.Jitter - - // Correlation - if latency > 0 && jitter > 0 { - delay_corr = Percentage2u32(nattrs.DelayCorr) - } - if loss > 0 { - loss_corr = Percentage2u32(nattrs.LossCorr) - } - if duplicate > 0 { - duplicate_corr = Percentage2u32(nattrs.DuplicateCorr) - } - // FIXME should validate values(like loss/duplicate are percentages...) - latency = time2Tick(latency) - - if nattrs.Limit != 0 { - limit = nattrs.Limit - } - // Jitter is only value if latency is > 0 - if latency > 0 { - jitter = time2Tick(jitter) - } - - reorder_prob = Percentage2u32(nattrs.ReorderProb) - reorder_corr = Percentage2u32(nattrs.ReorderCorr) - - if reorder_prob > 0 { - // ERROR if lantency == 0 - if gap == 0 { - gap = 1 - } - } - - corrupt_prob = Percentage2u32(nattrs.CorruptProb) - corrupt_corr = Percentage2u32(nattrs.CorruptCorr) - - return &Netem{ - QdiscAttrs: attrs, - Latency: latency, - DelayCorr: delay_corr, - Limit: limit, - Loss: loss, - LossCorr: loss_corr, - Gap: gap, - Duplicate: duplicate, - DuplicateCorr: duplicate_corr, - Jitter: jitter, - ReorderProb: reorder_prob, - ReorderCorr: reorder_corr, - CorruptProb: corrupt_prob, - CorruptCorr: corrupt_corr, - } -} - func (qdisc *Netem) Attrs() *QdiscAttrs { return &qdisc.QdiscAttrs } @@ -246,10 +187,11 @@ func (qdisc *Netem) Type() string { // Tbf is a classless qdisc that rate limits based on tokens type Tbf struct { QdiscAttrs - // TODO: handle 64bit rate properly - Rate uint64 - Limit uint32 - Buffer uint32 + Rate uint64 + Limit uint32 + Buffer uint32 + Peakrate uint64 + Minburst uint32 // TODO: handle other settings } diff --git a/vendor/github.com/vishvananda/netlink/qdisc_linux.go b/vendor/github.com/vishvananda/netlink/qdisc_linux.go index d9a8b170..2c0deddb 100644 --- a/vendor/github.com/vishvananda/netlink/qdisc_linux.go +++ b/vendor/github.com/vishvananda/netlink/qdisc_linux.go @@ -10,24 +10,109 @@ import ( "github.com/vishvananda/netlink/nl" ) +// NOTE function is here because it uses other linux functions +func NewNetem(attrs QdiscAttrs, nattrs NetemQdiscAttrs) *Netem { + var limit uint32 = 1000 + var lossCorr, delayCorr, duplicateCorr uint32 + var reorderProb, reorderCorr uint32 + var corruptProb, corruptCorr uint32 + + latency := nattrs.Latency + loss := Percentage2u32(nattrs.Loss) + gap := nattrs.Gap + duplicate := Percentage2u32(nattrs.Duplicate) + jitter := nattrs.Jitter + + // Correlation + if latency > 0 && jitter > 0 { + delayCorr = Percentage2u32(nattrs.DelayCorr) + } + if loss > 0 { + lossCorr = Percentage2u32(nattrs.LossCorr) + } + if duplicate > 0 { + duplicateCorr = Percentage2u32(nattrs.DuplicateCorr) + } + // FIXME should validate values(like loss/duplicate are percentages...) + latency = time2Tick(latency) + + if nattrs.Limit != 0 { + limit = nattrs.Limit + } + // Jitter is only value if latency is > 0 + if latency > 0 { + jitter = time2Tick(jitter) + } + + reorderProb = Percentage2u32(nattrs.ReorderProb) + reorderCorr = Percentage2u32(nattrs.ReorderCorr) + + if reorderProb > 0 { + // ERROR if lantency == 0 + if gap == 0 { + gap = 1 + } + } + + corruptProb = Percentage2u32(nattrs.CorruptProb) + corruptCorr = Percentage2u32(nattrs.CorruptCorr) + + return &Netem{ + QdiscAttrs: attrs, + Latency: latency, + DelayCorr: delayCorr, + Limit: limit, + Loss: loss, + LossCorr: lossCorr, + Gap: gap, + Duplicate: duplicate, + DuplicateCorr: duplicateCorr, + Jitter: jitter, + ReorderProb: reorderProb, + ReorderCorr: reorderCorr, + CorruptProb: corruptProb, + CorruptCorr: corruptCorr, + } +} + // QdiscDel will delete a qdisc from the system. // Equivalent to: `tc qdisc del $qdisc` func QdiscDel(qdisc Qdisc) error { - return qdiscModify(syscall.RTM_DELQDISC, 0, qdisc) + return pkgHandle.QdiscDel(qdisc) +} + +// QdiscDel will delete a qdisc from the system. +// Equivalent to: `tc qdisc del $qdisc` +func (h *Handle) QdiscDel(qdisc Qdisc) error { + return h.qdiscModify(syscall.RTM_DELQDISC, 0, qdisc) } // QdiscChange will change a qdisc in place // Equivalent to: `tc qdisc change $qdisc` // The parent and handle MUST NOT be changed. func QdiscChange(qdisc Qdisc) error { - return qdiscModify(syscall.RTM_NEWQDISC, 0, qdisc) + return pkgHandle.QdiscChange(qdisc) +} + +// QdiscChange will change a qdisc in place +// Equivalent to: `tc qdisc change $qdisc` +// The parent and handle MUST NOT be changed. +func (h *Handle) QdiscChange(qdisc Qdisc) error { + return h.qdiscModify(syscall.RTM_NEWQDISC, 0, qdisc) } // QdiscReplace will replace a qdisc to the system. // Equivalent to: `tc qdisc replace $qdisc` // The handle MUST change. func QdiscReplace(qdisc Qdisc) error { - return qdiscModify( + return pkgHandle.QdiscReplace(qdisc) +} + +// QdiscReplace will replace a qdisc to the system. +// Equivalent to: `tc qdisc replace $qdisc` +// The handle MUST change. +func (h *Handle) QdiscReplace(qdisc Qdisc) error { + return h.qdiscModify( syscall.RTM_NEWQDISC, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE, qdisc) @@ -36,14 +121,20 @@ func QdiscReplace(qdisc Qdisc) error { // QdiscAdd will add a qdisc to the system. // Equivalent to: `tc qdisc add $qdisc` func QdiscAdd(qdisc Qdisc) error { - return qdiscModify( + return pkgHandle.QdiscAdd(qdisc) +} + +// QdiscAdd will add a qdisc to the system. +// Equivalent to: `tc qdisc add $qdisc` +func (h *Handle) QdiscAdd(qdisc Qdisc) error { + return h.qdiscModify( syscall.RTM_NEWQDISC, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL, qdisc) } -func qdiscModify(cmd, flags int, qdisc Qdisc) error { - req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK) +func (h *Handle) qdiscModify(cmd, flags int, qdisc Qdisc) error { + req := h.newNetlinkRequest(cmd, flags|syscall.NLM_F_ACK) base := qdisc.Attrs() msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, @@ -77,11 +168,20 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error { options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize()) } else if tbf, ok := qdisc.(*Tbf); ok { opt := nl.TcTbfQopt{} - // TODO: handle rate > uint32 opt.Rate.Rate = uint32(tbf.Rate) + opt.Peakrate.Rate = uint32(tbf.Peakrate) opt.Limit = tbf.Limit opt.Buffer = tbf.Buffer nl.NewRtAttrChild(options, nl.TCA_TBF_PARMS, opt.Serialize()) + if tbf.Rate >= uint64(1<<32) { + nl.NewRtAttrChild(options, nl.TCA_TBF_RATE64, nl.Uint64Attr(tbf.Rate)) + } + if tbf.Peakrate >= uint64(1<<32) { + nl.NewRtAttrChild(options, nl.TCA_TBF_PRATE64, nl.Uint64Attr(tbf.Peakrate)) + } + if tbf.Peakrate > 0 { + nl.NewRtAttrChild(options, nl.TCA_TBF_PBURST, nl.Uint32Attr(tbf.Minburst)) + } } else if htb, ok := qdisc.(*Htb); ok { opt := nl.TcHtbGlob{} opt.Version = htb.Version @@ -139,11 +239,18 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error { // Equivalent to: `tc qdisc show`. // The list can be filtered by link. func QdiscList(link Link) ([]Qdisc, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETQDISC, syscall.NLM_F_DUMP) + return pkgHandle.QdiscList(link) +} + +// QdiscList gets a list of qdiscs in the system. +// Equivalent to: `tc qdisc show`. +// The list can be filtered by link. +func (h *Handle) QdiscList(link Link) ([]Qdisc, error) { + req := h.newNetlinkRequest(syscall.RTM_GETQDISC, syscall.NLM_F_DUMP) index := int32(0) if link != nil { base := link.Attrs() - ensureIndex(base) + h.ensureIndex(base) index = int32(base.Index) } msg := &nl.TcMsg{ @@ -320,10 +427,15 @@ func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { case nl.TCA_TBF_PARMS: opt := nl.DeserializeTcTbfQopt(datum.Value) tbf.Rate = uint64(opt.Rate.Rate) + tbf.Peakrate = uint64(opt.Peakrate.Rate) tbf.Limit = opt.Limit tbf.Buffer = opt.Buffer case nl.TCA_TBF_RATE64: - tbf.Rate = native.Uint64(datum.Value[0:4]) + tbf.Rate = native.Uint64(datum.Value[0:8]) + case nl.TCA_TBF_PRATE64: + tbf.Peakrate = native.Uint64(datum.Value[0:8]) + case nl.TCA_TBF_PBURST: + tbf.Minburst = native.Uint32(datum.Value[0:4]) } } return nil @@ -334,9 +446,9 @@ const ( ) var ( - tickInUsec float64 = 0.0 - clockFactor float64 = 0.0 - hz float64 = 0.0 + tickInUsec float64 + clockFactor float64 + hz float64 ) func initClock() { diff --git a/vendor/github.com/vishvananda/netlink/route.go b/vendor/github.com/vishvananda/netlink/route.go index a7303d4c..03ac4b23 100644 --- a/vendor/github.com/vishvananda/netlink/route.go +++ b/vendor/github.com/vishvananda/netlink/route.go @@ -3,26 +3,27 @@ package netlink import ( "fmt" "net" - "syscall" + "strings" ) // Scope is an enum representing a route scope. type Scope uint8 -const ( - SCOPE_UNIVERSE Scope = syscall.RT_SCOPE_UNIVERSE - SCOPE_SITE Scope = syscall.RT_SCOPE_SITE - SCOPE_LINK Scope = syscall.RT_SCOPE_LINK - SCOPE_HOST Scope = syscall.RT_SCOPE_HOST - SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE -) - type NextHopFlag int -const ( - FLAG_ONLINK NextHopFlag = syscall.RTNH_F_ONLINK - FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE -) +type Destination interface { + Family() int + Decode([]byte) error + Encode() ([]byte, error) + String() string +} + +type Encap interface { + Type() int + Decode([]byte) error + Encode() ([]byte, error) + String() string +} // Route represents a netlink route. type Route struct { @@ -32,17 +33,43 @@ type Route struct { Dst *net.IPNet Src net.IP Gw net.IP + MultiPath []*NexthopInfo Protocol int Priority int Table int Type int Tos int Flags int + MPLSDst *int + NewDst Destination + Encap Encap } func (r Route) String() string { - return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s Flags: %s}", r.LinkIndex, r.Dst, - r.Src, r.Gw, r.ListFlags()) + elems := []string{} + if len(r.MultiPath) == 0 { + elems = append(elems, fmt.Sprintf("Ifindex: %d", r.LinkIndex)) + } + if r.MPLSDst != nil { + elems = append(elems, fmt.Sprintf("Dst: %d", r.MPLSDst)) + } else { + elems = append(elems, fmt.Sprintf("Dst: %s", r.Dst)) + } + if r.NewDst != nil { + elems = append(elems, fmt.Sprintf("NewDst: %s", r.NewDst)) + } + if r.Encap != nil { + elems = append(elems, fmt.Sprintf("Encap: %s", r.Encap)) + } + elems = append(elems, fmt.Sprintf("Src: %s", r.Src)) + if len(r.MultiPath) > 0 { + elems = append(elems, fmt.Sprintf("Gw: %s", r.MultiPath)) + } else { + elems = append(elems, fmt.Sprintf("Gw: %s", r.Gw)) + } + elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags())) + elems = append(elems, fmt.Sprintf("Table: %d", r.Table)) + return fmt.Sprintf("{%s}", strings.Join(elems, " ")) } func (r *Route) SetFlag(flag NextHopFlag) { @@ -58,23 +85,32 @@ type flagString struct { s string } -var testFlags = []flagString{ - flagString{f: FLAG_ONLINK, s: "onlink"}, - flagString{f: FLAG_PERVASIVE, s: "pervasive"}, -} - -func (r *Route) ListFlags() []string { - var flags []string - for _, tf := range testFlags { - if r.Flags&int(tf.f) != 0 { - flags = append(flags, tf.s) - } - } - return flags -} - // RouteUpdate is sent when a route changes - type is RTM_NEWROUTE or RTM_DELROUTE type RouteUpdate struct { Type uint16 Route } + +type NexthopInfo struct { + LinkIndex int + Hops int + Gw net.IP + Flags int + NewDst Destination + Encap Encap +} + +func (n *NexthopInfo) String() string { + elems := []string{} + elems = append(elems, fmt.Sprintf("Ifindex: %d", n.LinkIndex)) + if n.NewDst != nil { + elems = append(elems, fmt.Sprintf("NewDst: %s", n.NewDst)) + } + if n.Encap != nil { + elems = append(elems, fmt.Sprintf("Encap: %s", n.Encap)) + } + elems = append(elems, fmt.Sprintf("Weight: %d", n.Hops+1)) + elems = append(elems, fmt.Sprintf("Gw: %d", n.Gw)) + elems = append(elems, fmt.Sprintf("Flags: %s", n.ListFlags())) + return fmt.Sprintf("{%s}", strings.Join(elems, " ")) +} diff --git a/vendor/github.com/vishvananda/netlink/route_linux.go b/vendor/github.com/vishvananda/netlink/route_linux.go index d8026a7d..9e0f1f93 100644 --- a/vendor/github.com/vishvananda/netlink/route_linux.go +++ b/vendor/github.com/vishvananda/netlink/route_linux.go @@ -3,13 +3,23 @@ package netlink import ( "fmt" "net" + "strings" "syscall" "github.com/vishvananda/netlink/nl" + "github.com/vishvananda/netns" ) // RtAttr is shared so it is in netlink_linux.go +const ( + SCOPE_UNIVERSE Scope = syscall.RT_SCOPE_UNIVERSE + SCOPE_SITE Scope = syscall.RT_SCOPE_SITE + SCOPE_LINK Scope = syscall.RT_SCOPE_LINK + SCOPE_HOST Scope = syscall.RT_SCOPE_HOST + SCOPE_NOWHERE Scope = syscall.RT_SCOPE_NOWHERE +) + const ( RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota) RT_FILTER_SCOPE @@ -23,22 +33,145 @@ const ( RT_FILTER_TABLE ) +const ( + FLAG_ONLINK NextHopFlag = syscall.RTNH_F_ONLINK + FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE +) + +var testFlags = []flagString{ + {f: FLAG_ONLINK, s: "onlink"}, + {f: FLAG_PERVASIVE, s: "pervasive"}, +} + +func listFlags(flag int) []string { + var flags []string + for _, tf := range testFlags { + if flag&int(tf.f) != 0 { + flags = append(flags, tf.s) + } + } + return flags +} + +func (r *Route) ListFlags() []string { + return listFlags(r.Flags) +} + +func (n *NexthopInfo) ListFlags() []string { + return listFlags(n.Flags) +} + +type MPLSDestination struct { + Labels []int +} + +func (d *MPLSDestination) Family() int { + return nl.FAMILY_MPLS +} + +func (d *MPLSDestination) Decode(buf []byte) error { + d.Labels = nl.DecodeMPLSStack(buf) + return nil +} + +func (d *MPLSDestination) Encode() ([]byte, error) { + return nl.EncodeMPLSStack(d.Labels...), nil +} + +func (d *MPLSDestination) String() string { + s := make([]string, 0, len(d.Labels)) + for _, l := range d.Labels { + s = append(s, fmt.Sprintf("%d", l)) + } + return strings.Join(s, "/") +} + +type MPLSEncap struct { + Labels []int +} + +func (e *MPLSEncap) Type() int { + return nl.LWTUNNEL_ENCAP_MPLS +} + +func (e *MPLSEncap) Decode(buf []byte) error { + if len(buf) < 4 { + return fmt.Errorf("Lack of bytes") + } + native := nl.NativeEndian() + l := native.Uint16(buf) + if len(buf) < int(l) { + return fmt.Errorf("Lack of bytes") + } + buf = buf[:l] + typ := native.Uint16(buf[2:]) + if typ != nl.MPLS_IPTUNNEL_DST { + return fmt.Errorf("Unknown MPLS Encap Type: %d", typ) + } + e.Labels = nl.DecodeMPLSStack(buf[4:]) + return nil +} + +func (e *MPLSEncap) Encode() ([]byte, error) { + s := nl.EncodeMPLSStack(e.Labels...) + native := nl.NativeEndian() + hdr := make([]byte, 4) + native.PutUint16(hdr, uint16(len(s)+4)) + native.PutUint16(hdr[2:], nl.MPLS_IPTUNNEL_DST) + return append(hdr, s...), nil +} + +func (e *MPLSEncap) String() string { + s := make([]string, 0, len(e.Labels)) + for _, l := range e.Labels { + s = append(s, fmt.Sprintf("%d", l)) + } + return strings.Join(s, "/") +} + // RouteAdd will add a route to the system. // Equivalent to: `ip route add $route` func RouteAdd(route *Route) error { - req := nl.NewNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) - return routeHandle(route, req, nl.NewRtMsg()) + return pkgHandle.RouteAdd(route) +} + +// RouteAdd will add a route to the system. +// Equivalent to: `ip route add $route` +func (h *Handle) RouteAdd(route *Route) error { + flags := syscall.NLM_F_CREATE | syscall.NLM_F_EXCL | syscall.NLM_F_ACK + req := h.newNetlinkRequest(syscall.RTM_NEWROUTE, flags) + return h.routeHandle(route, req, nl.NewRtMsg()) +} + +// RouteReplace will add a route to the system. +// Equivalent to: `ip route replace $route` +func RouteReplace(route *Route) error { + return pkgHandle.RouteReplace(route) +} + +// RouteReplace will add a route to the system. +// Equivalent to: `ip route replace $route` +func (h *Handle) RouteReplace(route *Route) error { + flags := syscall.NLM_F_CREATE | syscall.NLM_F_REPLACE | syscall.NLM_F_ACK + req := h.newNetlinkRequest(syscall.RTM_NEWROUTE, flags) + return h.routeHandle(route, req, nl.NewRtMsg()) } // RouteDel will delete a route from the system. // Equivalent to: `ip route del $route` func RouteDel(route *Route) error { - req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK) - return routeHandle(route, req, nl.NewRtDelMsg()) + return pkgHandle.RouteDel(route) } -func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { - if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil { +// RouteDel will delete a route from the system. +// Equivalent to: `ip route del $route` +func (h *Handle) RouteDel(route *Route) error { + req := h.newNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK) + return h.routeHandle(route, req, nl.NewRtDelMsg()) +} + +func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { + if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil && route.MPLSDst == nil { return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil") } @@ -57,6 +190,33 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { dstData = route.Dst.IP.To16() } rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData)) + } else if route.MPLSDst != nil { + family = nl.FAMILY_MPLS + msg.Dst_len = uint8(20) + msg.Type = syscall.RTN_UNICAST + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, nl.EncodeMPLSStack(*route.MPLSDst))) + } + + if route.NewDst != nil { + if family != -1 && family != route.NewDst.Family() { + return fmt.Errorf("new destination and destination are not the same address family") + } + buf, err := route.NewDst.Encode() + if err != nil { + return err + } + rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_NEWDST, buf)) + } + + if route.Encap != nil { + buf := make([]byte, 2) + native.PutUint16(buf, uint16(route.Encap.Type())) + rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_ENCAP_TYPE, buf)) + buf, err := route.Encap.Encode() + if err != nil { + return err + } + rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_ENCAP, buf)) } if route.Src != nil { @@ -90,6 +250,54 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData)) } + if len(route.MultiPath) > 0 { + buf := []byte{} + for _, nh := range route.MultiPath { + rtnh := &nl.RtNexthop{ + RtNexthop: syscall.RtNexthop{ + Hops: uint8(nh.Hops), + Ifindex: int32(nh.LinkIndex), + Flags: uint8(nh.Flags), + }, + } + children := []nl.NetlinkRequestData{} + if nh.Gw != nil { + gwFamily := nl.GetIPFamily(nh.Gw) + if family != -1 && family != gwFamily { + return fmt.Errorf("gateway, source, and destination ip are not the same IP family") + } + if gwFamily == FAMILY_V4 { + children = append(children, nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To4()))) + } else { + children = append(children, nl.NewRtAttr(syscall.RTA_GATEWAY, []byte(nh.Gw.To16()))) + } + } + if nh.NewDst != nil { + if family != -1 && family != nh.NewDst.Family() { + return fmt.Errorf("new destination and destination are not the same address family") + } + buf, err := nh.NewDst.Encode() + if err != nil { + return err + } + children = append(children, nl.NewRtAttr(nl.RTA_NEWDST, buf)) + } + if nh.Encap != nil { + buf := make([]byte, 2) + native.PutUint16(buf, uint16(nh.Encap.Type())) + rtAttrs = append(rtAttrs, nl.NewRtAttr(nl.RTA_ENCAP_TYPE, buf)) + buf, err := nh.Encap.Encode() + if err != nil { + return err + } + children = append(children, nl.NewRtAttr(nl.RTA_ENCAP, buf)) + } + rtnh.Children = children + buf = append(buf, rtnh.Serialize()...) + } + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_MULTIPATH, buf)) + } + if route.Table > 0 { if route.Table >= 256 { msg.Table = syscall.RT_TABLE_UNSPEC @@ -116,6 +324,7 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { msg.Type = uint8(route.Type) } + msg.Flags = uint32(route.Flags) msg.Scope = uint8(route.Scope) msg.Family = uint8(family) req.AddData(msg) @@ -139,19 +348,32 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { // Equivalent to: `ip route show`. // The list can be filtered by link and ip family. func RouteList(link Link, family int) ([]Route, error) { + return pkgHandle.RouteList(link, family) +} + +// RouteList gets a list of routes in the system. +// Equivalent to: `ip route show`. +// The list can be filtered by link and ip family. +func (h *Handle) RouteList(link Link, family int) ([]Route, error) { var routeFilter *Route if link != nil { routeFilter = &Route{ LinkIndex: link.Attrs().Index, } } - return RouteListFiltered(family, routeFilter, RT_FILTER_OIF) + return h.RouteListFiltered(family, routeFilter, RT_FILTER_OIF) } // RouteListFiltered gets a list of routes in the system filtered with specified rules. // All rules must be defined in RouteFilter struct func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) + return pkgHandle.RouteListFiltered(family, filter, filterMask) +} + +// RouteListFiltered gets a list of routes in the system filtered with specified rules. +// All rules must be defined in RouteFilter struct +func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) { + req := h.newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) infmsg := nl.NewIfInfomsg(family) req.AddData(infmsg) @@ -197,14 +419,22 @@ func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, e continue case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src): continue - case filterMask&RT_FILTER_DST != 0 && filter.Dst != nil: - if route.Dst == nil { - continue - } - aMaskLen, aMaskBits := route.Dst.Mask.Size() - bMaskLen, bMaskBits := filter.Dst.Mask.Size() - if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) { - continue + case filterMask&RT_FILTER_DST != 0: + if filter.MPLSDst == nil || route.MPLSDst == nil || (*filter.MPLSDst) != (*route.MPLSDst) { + if filter.Dst == nil { + if route.Dst != nil { + continue + } + } else { + if route.Dst == nil { + continue + } + aMaskLen, aMaskBits := route.Dst.Mask.Size() + bMaskLen, bMaskBits := filter.Dst.Mask.Size() + if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) { + continue + } + } } } } @@ -230,6 +460,7 @@ func deserializeRoute(m []byte) (Route, error) { } native := nl.NativeEndian() + var encap, encapType syscall.NetlinkRouteAttr for _, attr := range attrs { switch attr.Attr.Type { case syscall.RTA_GATEWAY: @@ -237,9 +468,17 @@ func deserializeRoute(m []byte) (Route, error) { case syscall.RTA_PREFSRC: route.Src = net.IP(attr.Value) case syscall.RTA_DST: - route.Dst = &net.IPNet{ - IP: attr.Value, - Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), + if msg.Family == nl.FAMILY_MPLS { + stack := nl.DecodeMPLSStack(attr.Value) + if len(stack) == 0 || len(stack) > 1 { + return route, fmt.Errorf("invalid MPLS RTA_DST") + } + route.MPLSDst = &stack[0] + } else { + route.Dst = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), + } } case syscall.RTA_OIF: route.LinkIndex = int(native.Uint32(attr.Value[0:4])) @@ -249,15 +488,113 @@ func deserializeRoute(m []byte) (Route, error) { route.Priority = int(native.Uint32(attr.Value[0:4])) case syscall.RTA_TABLE: route.Table = int(native.Uint32(attr.Value[0:4])) + case syscall.RTA_MULTIPATH: + parseRtNexthop := func(value []byte) (*NexthopInfo, []byte, error) { + if len(value) < syscall.SizeofRtNexthop { + return nil, nil, fmt.Errorf("Lack of bytes") + } + nh := nl.DeserializeRtNexthop(value) + if len(value) < int(nh.RtNexthop.Len) { + return nil, nil, fmt.Errorf("Lack of bytes") + } + info := &NexthopInfo{ + LinkIndex: int(nh.RtNexthop.Ifindex), + Hops: int(nh.RtNexthop.Hops), + Flags: int(nh.RtNexthop.Flags), + } + attrs, err := nl.ParseRouteAttr(value[syscall.SizeofRtNexthop:int(nh.RtNexthop.Len)]) + if err != nil { + return nil, nil, err + } + var encap, encapType syscall.NetlinkRouteAttr + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_GATEWAY: + info.Gw = net.IP(attr.Value) + case nl.RTA_NEWDST: + var d Destination + switch msg.Family { + case nl.FAMILY_MPLS: + d = &MPLSDestination{} + } + if err := d.Decode(attr.Value); err != nil { + return nil, nil, err + } + info.NewDst = d + case nl.RTA_ENCAP_TYPE: + encapType = attr + case nl.RTA_ENCAP: + encap = attr + } + } + + if len(encap.Value) != 0 && len(encapType.Value) != 0 { + typ := int(native.Uint16(encapType.Value[0:2])) + var e Encap + switch typ { + case nl.LWTUNNEL_ENCAP_MPLS: + e = &MPLSEncap{} + if err := e.Decode(encap.Value); err != nil { + return nil, nil, err + } + } + info.Encap = e + } + + return info, value[int(nh.RtNexthop.Len):], nil + } + rest := attr.Value + for len(rest) > 0 { + info, buf, err := parseRtNexthop(rest) + if err != nil { + return route, err + } + route.MultiPath = append(route.MultiPath, info) + rest = buf + } + case nl.RTA_NEWDST: + var d Destination + switch msg.Family { + case nl.FAMILY_MPLS: + d = &MPLSDestination{} + } + if err := d.Decode(attr.Value); err != nil { + return route, err + } + route.NewDst = d + case nl.RTA_ENCAP_TYPE: + encapType = attr + case nl.RTA_ENCAP: + encap = attr } } + + if len(encap.Value) != 0 && len(encapType.Value) != 0 { + typ := int(native.Uint16(encapType.Value[0:2])) + var e Encap + switch typ { + case nl.LWTUNNEL_ENCAP_MPLS: + e = &MPLSEncap{} + if err := e.Decode(encap.Value); err != nil { + return route, err + } + } + route.Encap = e + } + return route, nil } // RouteGet gets a route to a specific destination from the host system. // Equivalent to: 'ip route get'. func RouteGet(destination net.IP) ([]Route, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST) + return pkgHandle.RouteGet(destination) +} + +// RouteGet gets a route to a specific destination from the host system. +// Equivalent to: 'ip route get'. +func (h *Handle) RouteGet(destination net.IP) ([]Route, error) { + req := h.newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST) family := nl.GetIPFamily(destination) var destinationData []byte var bitlen uint8 @@ -296,7 +633,17 @@ func RouteGet(destination net.IP) ([]Route, error) { // RouteSubscribe takes a chan down which notifications will be sent // when routes are added or deleted. Close the 'done' chan to stop subscription. func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error { - s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE) + return routeSubscribeAt(netns.None(), netns.None(), ch, done) +} + +// RouteSubscribeAt works like RouteSubscribe plus it allows the caller +// to choose the network namespace in which to subscribe (ns). +func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error { + return routeSubscribeAt(ns, netns.None(), ch, done) +} + +func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error { + s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE) if err != nil { return err } diff --git a/vendor/github.com/vishvananda/netlink/route_unspecified.go b/vendor/github.com/vishvananda/netlink/route_unspecified.go new file mode 100644 index 00000000..2701862b --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/route_unspecified.go @@ -0,0 +1,11 @@ +// +build !linux + +package netlink + +func (r *Route) ListFlags() []string { + return []string{} +} + +func (n *NexthopInfo) ListFlags() []string { + return []string{} +} diff --git a/vendor/github.com/vishvananda/netlink/rule.go b/vendor/github.com/vishvananda/netlink/rule.go index bd699a7e..f0243def 100644 --- a/vendor/github.com/vishvananda/netlink/rule.go +++ b/vendor/github.com/vishvananda/netlink/rule.go @@ -3,13 +3,10 @@ package netlink import ( "fmt" "net" - - "github.com/vishvananda/netlink/nl" ) // Rule represents a netlink rule. type Rule struct { - *nl.RtMsg Priority int Table int Mark int diff --git a/vendor/github.com/vishvananda/netlink/rule_linux.go b/vendor/github.com/vishvananda/netlink/rule_linux.go index ba84be00..f9cdc855 100644 --- a/vendor/github.com/vishvananda/netlink/rule_linux.go +++ b/vendor/github.com/vishvananda/netlink/rule_linux.go @@ -11,14 +11,26 @@ import ( // RuleAdd adds a rule to the system. // Equivalent to: ip rule add func RuleAdd(rule *Rule) error { - req := nl.NewNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return pkgHandle.RuleAdd(rule) +} + +// RuleAdd adds a rule to the system. +// Equivalent to: ip rule add +func (h *Handle) RuleAdd(rule *Rule) error { + req := h.newNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) return ruleHandle(rule, req) } // RuleDel deletes a rule from the system. // Equivalent to: ip rule del func RuleDel(rule *Rule) error { - req := nl.NewNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return pkgHandle.RuleDel(rule) +} + +// RuleDel deletes a rule from the system. +// Equivalent to: ip rule del +func (h *Handle) RuleDel(rule *Rule) error { + req := h.newNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) return ruleHandle(rule, req) } @@ -70,41 +82,46 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { req.AddData(rtAttrs[i]) } - var ( - b = make([]byte, 4) - native = nl.NativeEndian() - ) + native := nl.NativeEndian() if rule.Priority >= 0 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.Priority)) req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b)) } if rule.Mark >= 0 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.Mark)) req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b)) } if rule.Mask >= 0 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.Mask)) req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b)) } if rule.Flow >= 0 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.Flow)) req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b)) } if rule.TunID > 0 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.TunID)) req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b)) } if rule.Table >= 256 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.Table)) req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b)) } if msg.Table > 0 { if rule.SuppressPrefixlen >= 0 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.SuppressPrefixlen)) req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b)) } if rule.SuppressIfgroup >= 0 { + b := make([]byte, 4) native.PutUint32(b, uint32(rule.SuppressIfgroup)) req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b)) } @@ -117,6 +134,7 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { } if rule.Goto >= 0 { msg.Type = nl.FR_ACT_NOP + b := make([]byte, 4) native.PutUint32(b, uint32(rule.Goto)) req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b)) } @@ -128,7 +146,13 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { // RuleList lists rules in the system. // Equivalent to: ip rule list func RuleList(family int) ([]Rule, error) { - req := nl.NewNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST) + return pkgHandle.RuleList(family) +} + +// RuleList lists rules in the system. +// Equivalent to: ip rule list +func (h *Handle) RuleList(family int) ([]Rule, error) { + req := h.newNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST) msg := nl.NewIfInfomsg(family) req.AddData(msg) @@ -147,7 +171,6 @@ func RuleList(family int) ([]Rule, error) { } rule := NewRule() - rule.RtMsg = msg for j := range attrs { switch attrs[j].Attr.Type { diff --git a/vendor/github.com/vishvananda/netlink/socket.go b/vendor/github.com/vishvananda/netlink/socket.go new file mode 100644 index 00000000..41aa7262 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/socket.go @@ -0,0 +1,27 @@ +package netlink + +import "net" + +// SocketID identifies a single socket. +type SocketID struct { + SourcePort uint16 + DestinationPort uint16 + Source net.IP + Destination net.IP + Interface uint32 + Cookie [2]uint32 +} + +// Socket represents a netlink socket. +type Socket struct { + Family uint8 + State uint8 + Timer uint8 + Retrans uint8 + ID SocketID + Expires uint32 + RQueue uint32 + WQueue uint32 + UID uint32 + INode uint32 +} diff --git a/vendor/github.com/vishvananda/netlink/socket_linux.go b/vendor/github.com/vishvananda/netlink/socket_linux.go new file mode 100644 index 00000000..b42b84f0 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/socket_linux.go @@ -0,0 +1,159 @@ +package netlink + +import ( + "errors" + "fmt" + "net" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +const ( + sizeofSocketID = 0x30 + sizeofSocketRequest = sizeofSocketID + 0x8 + sizeofSocket = sizeofSocketID + 0x18 +) + +type socketRequest struct { + Family uint8 + Protocol uint8 + Ext uint8 + pad uint8 + States uint32 + ID SocketID +} + +type writeBuffer struct { + Bytes []byte + pos int +} + +func (b *writeBuffer) Write(c byte) { + b.Bytes[b.pos] = c + b.pos++ +} + +func (b *writeBuffer) Next(n int) []byte { + s := b.Bytes[b.pos : b.pos+n] + b.pos += n + return s +} + +func (r *socketRequest) Serialize() []byte { + b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)} + b.Write(r.Family) + b.Write(r.Protocol) + b.Write(r.Ext) + b.Write(r.pad) + native.PutUint32(b.Next(4), r.States) + networkOrder.PutUint16(b.Next(2), r.ID.SourcePort) + networkOrder.PutUint16(b.Next(2), r.ID.DestinationPort) + copy(b.Next(4), r.ID.Source.To4()) + b.Next(12) + copy(b.Next(4), r.ID.Destination.To4()) + b.Next(12) + native.PutUint32(b.Next(4), r.ID.Interface) + native.PutUint32(b.Next(4), r.ID.Cookie[0]) + native.PutUint32(b.Next(4), r.ID.Cookie[1]) + return b.Bytes +} + +func (r *socketRequest) Len() int { return sizeofSocketRequest } + +type readBuffer struct { + Bytes []byte + pos int +} + +func (b *readBuffer) Read() byte { + c := b.Bytes[b.pos] + b.pos++ + return c +} + +func (b *readBuffer) Next(n int) []byte { + s := b.Bytes[b.pos : b.pos+n] + b.pos += n + return s +} + +func (s *Socket) deserialize(b []byte) error { + if len(b) < sizeofSocket { + return fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofSocket) + } + rb := readBuffer{Bytes: b} + s.Family = rb.Read() + s.State = rb.Read() + s.Timer = rb.Read() + s.Retrans = rb.Read() + s.ID.SourcePort = networkOrder.Uint16(rb.Next(2)) + s.ID.DestinationPort = networkOrder.Uint16(rb.Next(2)) + s.ID.Source = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) + rb.Next(12) + s.ID.Destination = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) + rb.Next(12) + s.ID.Interface = native.Uint32(rb.Next(4)) + s.ID.Cookie[0] = native.Uint32(rb.Next(4)) + s.ID.Cookie[1] = native.Uint32(rb.Next(4)) + s.Expires = native.Uint32(rb.Next(4)) + s.RQueue = native.Uint32(rb.Next(4)) + s.WQueue = native.Uint32(rb.Next(4)) + s.UID = native.Uint32(rb.Next(4)) + s.INode = native.Uint32(rb.Next(4)) + return nil +} + +// SocketGet returns the Socket identified by its local and remote addresses. +func SocketGet(local, remote net.Addr) (*Socket, error) { + localTCP, ok := local.(*net.TCPAddr) + if !ok { + return nil, ErrNotImplemented + } + remoteTCP, ok := remote.(*net.TCPAddr) + if !ok { + return nil, ErrNotImplemented + } + localIP := localTCP.IP.To4() + if localIP == nil { + return nil, ErrNotImplemented + } + remoteIP := remoteTCP.IP.To4() + if remoteIP == nil { + return nil, ErrNotImplemented + } + + s, err := nl.Subscribe(syscall.NETLINK_INET_DIAG) + if err != nil { + return nil, err + } + defer s.Close() + req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, 0) + req.AddData(&socketRequest{ + Family: syscall.AF_INET, + Protocol: syscall.IPPROTO_TCP, + ID: SocketID{ + SourcePort: uint16(localTCP.Port), + DestinationPort: uint16(remoteTCP.Port), + Source: localIP, + Destination: remoteIP, + Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE}, + }, + }) + s.Send(req) + msgs, err := s.Receive() + if err != nil { + return nil, err + } + if len(msgs) == 0 { + return nil, errors.New("no message nor error from netlink") + } + if len(msgs) > 2 { + return nil, fmt.Errorf("multiple (%d) matching sockets", len(msgs)) + } + sock := &Socket{} + if err := sock.deserialize(msgs[0].Data); err != nil { + return nil, err + } + return sock, nil +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm.go b/vendor/github.com/vishvananda/netlink/xfrm.go index 621ffb6c..9962dcf7 100644 --- a/vendor/github.com/vishvananda/netlink/xfrm.go +++ b/vendor/github.com/vishvananda/netlink/xfrm.go @@ -13,7 +13,7 @@ const ( XFRM_PROTO_ESP Proto = syscall.IPPROTO_ESP XFRM_PROTO_AH Proto = syscall.IPPROTO_AH XFRM_PROTO_HAO Proto = syscall.IPPROTO_DSTOPTS - XFRM_PROTO_COMP Proto = syscall.IPPROTO_COMP + XFRM_PROTO_COMP Proto = 0x6c // NOTE not defined on darwin XFRM_PROTO_IPSEC_ANY Proto = syscall.IPPROTO_RAW ) @@ -62,3 +62,13 @@ func (m Mode) String() string { } return fmt.Sprintf("%d", m) } + +// XfrmMark represents the mark associated to the state or policy +type XfrmMark struct { + Value uint32 + Mask uint32 +} + +func (m *XfrmMark) String() string { + return fmt.Sprintf("(0x%x,0x%x)", m.Value, m.Mask) +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm_monitor_linux.go b/vendor/github.com/vishvananda/netlink/xfrm_monitor_linux.go new file mode 100644 index 00000000..7b98c9cb --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/xfrm_monitor_linux.go @@ -0,0 +1,98 @@ +package netlink + +import ( + "fmt" + "syscall" + + "github.com/vishvananda/netns" + + "github.com/vishvananda/netlink/nl" +) + +type XfrmMsg interface { + Type() nl.XfrmMsgType +} + +type XfrmMsgExpire struct { + XfrmState *XfrmState + Hard bool +} + +func (ue *XfrmMsgExpire) Type() nl.XfrmMsgType { + return nl.XFRM_MSG_EXPIRE +} + +func parseXfrmMsgExpire(b []byte) *XfrmMsgExpire { + var e XfrmMsgExpire + + msg := nl.DeserializeXfrmUserExpire(b) + e.XfrmState = xfrmStateFromXfrmUsersaInfo(&msg.XfrmUsersaInfo) + e.Hard = msg.Hard == 1 + + return &e +} + +func XfrmMonitor(ch chan<- XfrmMsg, done <-chan struct{}, errorChan chan<- error, + types ...nl.XfrmMsgType) error { + + groups, err := xfrmMcastGroups(types) + if err != nil { + return nil + } + s, err := nl.SubscribeAt(netns.None(), netns.None(), syscall.NETLINK_XFRM, groups...) + if err != nil { + return err + } + + if done != nil { + go func() { + <-done + s.Close() + }() + + } + + go func() { + defer close(ch) + for { + msgs, err := s.Receive() + if err != nil { + errorChan <- err + return + } + for _, m := range msgs { + switch m.Header.Type { + case nl.XFRM_MSG_EXPIRE: + ch <- parseXfrmMsgExpire(m.Data) + default: + errorChan <- fmt.Errorf("unsupported msg type: %x", m.Header.Type) + } + } + } + }() + + return nil +} + +func xfrmMcastGroups(types []nl.XfrmMsgType) ([]uint, error) { + groups := make([]uint, 0) + + if len(types) == 0 { + return nil, fmt.Errorf("no xfrm msg type specified") + } + + for _, t := range types { + var group uint + + switch t { + case nl.XFRM_MSG_EXPIRE: + group = nl.XFRMNLGRP_EXPIRE + default: + return nil, fmt.Errorf("unsupported group: %x", t) + } + + groups = append(groups, group) + } + + return groups, nil +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm_policy.go b/vendor/github.com/vishvananda/netlink/xfrm_policy.go index d85c65d2..c97ec43a 100644 --- a/vendor/github.com/vishvananda/netlink/xfrm_policy.go +++ b/vendor/github.com/vishvananda/netlink/xfrm_policy.go @@ -43,17 +43,32 @@ type XfrmPolicyTmpl struct { Src net.IP Proto Proto Mode Mode + Spi int Reqid int } +func (t XfrmPolicyTmpl) String() string { + return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, Mode: %s, Spi: 0x%x, Reqid: 0x%x}", + t.Dst, t.Src, t.Proto, t.Mode, t.Spi, t.Reqid) +} + // XfrmPolicy represents an ipsec policy. It represents the overlay network // and has a list of XfrmPolicyTmpls representing the base addresses of // the policy. type XfrmPolicy struct { Dst *net.IPNet Src *net.IPNet + Proto Proto + DstPort int + SrcPort int Dir Dir Priority int Index int + Mark *XfrmMark Tmpls []XfrmPolicyTmpl } + +func (p XfrmPolicy) String() string { + return fmt.Sprintf("{Dst: %v, Src: %v, Proto: %s, DstPort: %d, SrcPort: %d, Dir: %s, Priority: %d, Index: %d, Mark: %s, Tmpls: %s}", + p.Dst, p.Src, p.Proto, p.DstPort, p.SrcPort, p.Dir, p.Priority, p.Index, p.Mark, p.Tmpls) +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go b/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go index 2daf6dc8..c3d4e422 100644 --- a/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go +++ b/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go @@ -7,19 +7,55 @@ import ( ) func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) { - sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP)) - sel.Daddr.FromIP(policy.Dst.IP) - sel.Saddr.FromIP(policy.Src.IP) - prefixlenD, _ := policy.Dst.Mask.Size() - sel.PrefixlenD = uint8(prefixlenD) - prefixlenS, _ := policy.Src.Mask.Size() - sel.PrefixlenS = uint8(prefixlenS) + sel.Family = uint16(nl.FAMILY_V4) + if policy.Dst != nil { + sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP)) + sel.Daddr.FromIP(policy.Dst.IP) + prefixlenD, _ := policy.Dst.Mask.Size() + sel.PrefixlenD = uint8(prefixlenD) + } + if policy.Src != nil { + sel.Saddr.FromIP(policy.Src.IP) + prefixlenS, _ := policy.Src.Mask.Size() + sel.PrefixlenS = uint8(prefixlenS) + } + sel.Proto = uint8(policy.Proto) + sel.Dport = nl.Swap16(uint16(policy.DstPort)) + sel.Sport = nl.Swap16(uint16(policy.SrcPort)) + if sel.Dport != 0 { + sel.DportMask = ^uint16(0) + } + if sel.Sport != 0 { + sel.SportMask = ^uint16(0) + } } // XfrmPolicyAdd will add an xfrm policy to the system. // Equivalent to: `ip xfrm policy add $policy` func XfrmPolicyAdd(policy *XfrmPolicy) error { - req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWPOLICY, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return pkgHandle.XfrmPolicyAdd(policy) +} + +// XfrmPolicyAdd will add an xfrm policy to the system. +// Equivalent to: `ip xfrm policy add $policy` +func (h *Handle) XfrmPolicyAdd(policy *XfrmPolicy) error { + return h.xfrmPolicyAddOrUpdate(policy, nl.XFRM_MSG_NEWPOLICY) +} + +// XfrmPolicyUpdate will update an xfrm policy to the system. +// Equivalent to: `ip xfrm policy update $policy` +func XfrmPolicyUpdate(policy *XfrmPolicy) error { + return pkgHandle.XfrmPolicyUpdate(policy) +} + +// XfrmPolicyUpdate will update an xfrm policy to the system. +// Equivalent to: `ip xfrm policy update $policy` +func (h *Handle) XfrmPolicyUpdate(policy *XfrmPolicy) error { + return h.xfrmPolicyAddOrUpdate(policy, nl.XFRM_MSG_UPDPOLICY) +} + +func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error { + req := h.newNetlinkRequest(nlProto, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) msg := &nl.XfrmUserpolicyInfo{} selFromPolicy(&msg.Sel, policy) @@ -39,6 +75,7 @@ func XfrmPolicyAdd(policy *XfrmPolicy) error { userTmpl.XfrmId.Daddr.FromIP(tmpl.Dst) userTmpl.Saddr.FromIP(tmpl.Src) userTmpl.XfrmId.Proto = uint8(tmpl.Proto) + userTmpl.XfrmId.Spi = nl.Swap32(uint32(tmpl.Spi)) userTmpl.Mode = uint8(tmpl.Mode) userTmpl.Reqid = uint32(tmpl.Reqid) userTmpl.Aalgos = ^uint32(0) @@ -49,6 +86,10 @@ func XfrmPolicyAdd(policy *XfrmPolicy) error { tmpls := nl.NewRtAttr(nl.XFRMA_TMPL, tmplData) req.AddData(tmpls) } + if policy.Mark != nil { + out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(policy.Mark)) + req.AddData(out) + } _, err := req.Execute(syscall.NETLINK_XFRM, 0) return err @@ -58,15 +99,14 @@ func XfrmPolicyAdd(policy *XfrmPolicy) error { // the Tmpls are ignored when matching the policy to delete. // Equivalent to: `ip xfrm policy del $policy` func XfrmPolicyDel(policy *XfrmPolicy) error { - req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELPOLICY, syscall.NLM_F_ACK) + return pkgHandle.XfrmPolicyDel(policy) +} - msg := &nl.XfrmUserpolicyId{} - selFromPolicy(&msg.Sel, policy) - msg.Index = uint32(policy.Index) - msg.Dir = uint8(policy.Dir) - req.AddData(msg) - - _, err := req.Execute(syscall.NETLINK_XFRM, 0) +// XfrmPolicyDel will delete an xfrm policy from the system. Note that +// the Tmpls are ignored when matching the policy to delete. +// Equivalent to: `ip xfrm policy del $policy` +func (h *Handle) XfrmPolicyDel(policy *XfrmPolicy) error { + _, err := h.xfrmPolicyGetOrDelete(policy, nl.XFRM_MSG_DELPOLICY) return err } @@ -74,7 +114,14 @@ func XfrmPolicyDel(policy *XfrmPolicy) error { // Equivalent to: `ip xfrm policy show`. // The list can be filtered by ip family. func XfrmPolicyList(family int) ([]XfrmPolicy, error) { - req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETPOLICY, syscall.NLM_F_DUMP) + return pkgHandle.XfrmPolicyList(family) +} + +// XfrmPolicyList gets a list of xfrm policies in the system. +// Equivalent to: `ip xfrm policy show`. +// The list can be filtered by ip family. +func (h *Handle) XfrmPolicyList(family int) ([]XfrmPolicy, error) { + req := h.newNetlinkRequest(nl.XFRM_MSG_GETPOLICY, syscall.NLM_F_DUMP) msg := nl.NewIfInfomsg(family) req.AddData(msg) @@ -86,42 +133,125 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) { var res []XfrmPolicy for _, m := range msgs { - msg := nl.DeserializeXfrmUserpolicyInfo(m) - - if family != FAMILY_ALL && family != int(msg.Sel.Family) { + if policy, err := parseXfrmPolicy(m, family); err == nil { + res = append(res, *policy) + } else if err == familyError { continue - } - - var policy XfrmPolicy - - policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD) - policy.Src = msg.Sel.Saddr.ToIPNet(msg.Sel.PrefixlenS) - policy.Priority = int(msg.Priority) - policy.Index = int(msg.Index) - policy.Dir = Dir(msg.Dir) - - attrs, err := nl.ParseRouteAttr(m[msg.Len():]) - if err != nil { + } else { return nil, err } - - for _, attr := range attrs { - switch attr.Attr.Type { - case nl.XFRMA_TMPL: - max := len(attr.Value) - for i := 0; i < max; i += nl.SizeofXfrmUserTmpl { - var resTmpl XfrmPolicyTmpl - tmpl := nl.DeserializeXfrmUserTmpl(attr.Value[i : i+nl.SizeofXfrmUserTmpl]) - resTmpl.Dst = tmpl.XfrmId.Daddr.ToIP() - resTmpl.Src = tmpl.Saddr.ToIP() - resTmpl.Proto = Proto(tmpl.XfrmId.Proto) - resTmpl.Mode = Mode(tmpl.Mode) - resTmpl.Reqid = int(tmpl.Reqid) - policy.Tmpls = append(policy.Tmpls, resTmpl) - } - } - } - res = append(res, policy) } return res, nil } + +// XfrmPolicyGet gets a the policy described by the index or selector, if found. +// Equivalent to: `ip xfrm policy get { SELECTOR | index INDEX } dir DIR [ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]`. +func XfrmPolicyGet(policy *XfrmPolicy) (*XfrmPolicy, error) { + return pkgHandle.XfrmPolicyGet(policy) +} + +// XfrmPolicyGet gets a the policy described by the index or selector, if found. +// Equivalent to: `ip xfrm policy get { SELECTOR | index INDEX } dir DIR [ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]`. +func (h *Handle) XfrmPolicyGet(policy *XfrmPolicy) (*XfrmPolicy, error) { + return h.xfrmPolicyGetOrDelete(policy, nl.XFRM_MSG_GETPOLICY) +} + +// XfrmPolicyFlush will flush the policies on the system. +// Equivalent to: `ip xfrm policy flush` +func XfrmPolicyFlush() error { + return pkgHandle.XfrmPolicyFlush() +} + +// XfrmPolicyFlush will flush the policies on the system. +// Equivalent to: `ip xfrm policy flush` +func (h *Handle) XfrmPolicyFlush() error { + req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHPOLICY, syscall.NLM_F_ACK) + _, err := req.Execute(syscall.NETLINK_XFRM, 0) + return err +} + +func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPolicy, error) { + req := h.newNetlinkRequest(nlProto, syscall.NLM_F_ACK) + + msg := &nl.XfrmUserpolicyId{} + selFromPolicy(&msg.Sel, policy) + msg.Index = uint32(policy.Index) + msg.Dir = uint8(policy.Dir) + req.AddData(msg) + + if policy.Mark != nil { + out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(policy.Mark)) + req.AddData(out) + } + + resType := nl.XFRM_MSG_NEWPOLICY + if nlProto == nl.XFRM_MSG_DELPOLICY { + resType = 0 + } + + msgs, err := req.Execute(syscall.NETLINK_XFRM, uint16(resType)) + if err != nil { + return nil, err + } + + if nlProto == nl.XFRM_MSG_DELPOLICY { + return nil, err + } + + p, err := parseXfrmPolicy(msgs[0], FAMILY_ALL) + if err != nil { + return nil, err + } + + return p, nil +} + +func parseXfrmPolicy(m []byte, family int) (*XfrmPolicy, error) { + msg := nl.DeserializeXfrmUserpolicyInfo(m) + + // This is mainly for the policy dump + if family != FAMILY_ALL && family != int(msg.Sel.Family) { + return nil, familyError + } + + var policy XfrmPolicy + + policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD) + policy.Src = msg.Sel.Saddr.ToIPNet(msg.Sel.PrefixlenS) + policy.Proto = Proto(msg.Sel.Proto) + policy.DstPort = int(nl.Swap16(msg.Sel.Dport)) + policy.SrcPort = int(nl.Swap16(msg.Sel.Sport)) + policy.Priority = int(msg.Priority) + policy.Index = int(msg.Index) + policy.Dir = Dir(msg.Dir) + + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + for _, attr := range attrs { + switch attr.Attr.Type { + case nl.XFRMA_TMPL: + max := len(attr.Value) + for i := 0; i < max; i += nl.SizeofXfrmUserTmpl { + var resTmpl XfrmPolicyTmpl + tmpl := nl.DeserializeXfrmUserTmpl(attr.Value[i : i+nl.SizeofXfrmUserTmpl]) + resTmpl.Dst = tmpl.XfrmId.Daddr.ToIP() + resTmpl.Src = tmpl.Saddr.ToIP() + resTmpl.Proto = Proto(tmpl.XfrmId.Proto) + resTmpl.Mode = Mode(tmpl.Mode) + resTmpl.Spi = int(nl.Swap32(tmpl.XfrmId.Spi)) + resTmpl.Reqid = int(tmpl.Reqid) + policy.Tmpls = append(policy.Tmpls, resTmpl) + } + case nl.XFRMA_MARK: + mark := nl.DeserializeXfrmMark(attr.Value[:]) + policy.Mark = new(XfrmMark) + policy.Mark.Value = mark.Value + policy.Mark.Mask = mark.Mask + } + } + + return &policy, nil +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm_state.go b/vendor/github.com/vishvananda/netlink/xfrm_state.go index 5b8f2df7..368a9b98 100644 --- a/vendor/github.com/vishvananda/netlink/xfrm_state.go +++ b/vendor/github.com/vishvananda/netlink/xfrm_state.go @@ -1,6 +1,7 @@ package netlink import ( + "fmt" "net" ) @@ -9,9 +10,21 @@ type XfrmStateAlgo struct { Name string Key []byte TruncateLen int // Auth only + ICVLen int // AEAD only } -// EncapType is an enum representing an ipsec template direction. +func (a XfrmStateAlgo) String() string { + base := fmt.Sprintf("{Name: %s, Key: 0x%x", a.Name, a.Key) + if a.TruncateLen != 0 { + base = fmt.Sprintf("%s, Truncate length: %d", base, a.TruncateLen) + } + if a.ICVLen != 0 { + base = fmt.Sprintf("%s, ICV length: %d", base, a.ICVLen) + } + return fmt.Sprintf("%s}", base) +} + +// EncapType is an enum representing the optional packet encapsulation. type EncapType uint8 const ( @@ -22,14 +35,14 @@ const ( func (e EncapType) String() string { switch e { case XFRM_ENCAP_ESPINUDP_NONIKE: - return "espinudp-nonike" + return "espinudp-non-ike" case XFRM_ENCAP_ESPINUDP: return "espinudp" } return "unknown" } -// XfrmEncap represents the encapsulation to use for the ipsec encryption. +// XfrmStateEncap represents the encapsulation to use for the ipsec encryption. type XfrmStateEncap struct { Type EncapType SrcPort int @@ -37,6 +50,23 @@ type XfrmStateEncap struct { OriginalAddress net.IP } +func (e XfrmStateEncap) String() string { + return fmt.Sprintf("{Type: %s, Srcport: %d, DstPort: %d, OriginalAddress: %v}", + e.Type, e.SrcPort, e.DstPort, e.OriginalAddress) +} + +// XfrmStateLimits represents the configured limits for the state. +type XfrmStateLimits struct { + ByteSoft uint64 + ByteHard uint64 + PacketSoft uint64 + PacketHard uint64 + TimeSoft uint64 + TimeHard uint64 + TimeUseSoft uint64 + TimeUseHard uint64 +} + // XfrmState represents the state of an ipsec policy. It optionally // contains an XfrmStateAlgo for encryption and one for authentication. type XfrmState struct { @@ -47,7 +77,32 @@ type XfrmState struct { Spi int Reqid int ReplayWindow int + Limits XfrmStateLimits + Mark *XfrmMark Auth *XfrmStateAlgo Crypt *XfrmStateAlgo + Aead *XfrmStateAlgo Encap *XfrmStateEncap + ESN bool +} + +func (sa XfrmState) String() string { + return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Aead: %v, Encap: %v, ESN: %t", + sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Aead, sa.Encap, sa.ESN) +} +func (sa XfrmState) Print(stats bool) string { + if !stats { + return sa.String() + } + + return fmt.Sprintf("%s, ByteSoft: %s, ByteHard: %s, PacketSoft: %s, PacketHard: %s, TimeSoft: %d, TimeHard: %d, TimeUseSoft: %d, TimeUseHard: %d", + sa.String(), printLimit(sa.Limits.ByteSoft), printLimit(sa.Limits.ByteHard), printLimit(sa.Limits.PacketSoft), printLimit(sa.Limits.PacketHard), + sa.Limits.TimeSoft, sa.Limits.TimeHard, sa.Limits.TimeUseSoft, sa.Limits.TimeUseHard) +} + +func printLimit(lmt uint64) string { + if lmt == ^uint64(0) { + return "(INF)" + } + return fmt.Sprintf("%d", lmt) } diff --git a/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go b/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go index fc8604b9..6a7bc0de 100644 --- a/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go +++ b/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go @@ -3,6 +3,7 @@ package netlink import ( "fmt" "syscall" + "unsafe" "github.com/vishvananda/netlink/nl" ) @@ -34,28 +35,95 @@ func writeStateAlgoAuth(a *XfrmStateAlgo) []byte { return algo.Serialize() } +func writeStateAlgoAead(a *XfrmStateAlgo) []byte { + algo := nl.XfrmAlgoAEAD{ + AlgKeyLen: uint32(len(a.Key) * 8), + AlgICVLen: uint32(a.ICVLen), + AlgKey: a.Key, + } + end := len(a.Name) + if end > 64 { + end = 64 + } + copy(algo.AlgName[:end], a.Name) + return algo.Serialize() +} + +func writeMark(m *XfrmMark) []byte { + mark := &nl.XfrmMark{ + Value: m.Value, + Mask: m.Mask, + } + if mark.Mask == 0 { + mark.Mask = ^uint32(0) + } + return mark.Serialize() +} + +func writeReplayEsn(replayWindow int) []byte { + replayEsn := &nl.XfrmReplayStateEsn{ + OSeq: 0, + Seq: 0, + OSeqHi: 0, + SeqHi: 0, + ReplayWindow: uint32(replayWindow), + } + + // taken from iproute2/ip/xfrm_state.c: + replayEsn.BmpLen = uint32((replayWindow + (4 * 8) - 1) / (4 * 8)) + + return replayEsn.Serialize() +} + // XfrmStateAdd will add an xfrm state to the system. // Equivalent to: `ip xfrm state add $state` func XfrmStateAdd(state *XfrmState) error { + return pkgHandle.XfrmStateAdd(state) +} + +// XfrmStateAdd will add an xfrm state to the system. +// Equivalent to: `ip xfrm state add $state` +func (h *Handle) XfrmStateAdd(state *XfrmState) error { + return h.xfrmStateAddOrUpdate(state, nl.XFRM_MSG_NEWSA) +} + +// XfrmStateAllocSpi will allocate an xfrm state in the system. +// Equivalent to: `ip xfrm state allocspi` +func XfrmStateAllocSpi(state *XfrmState) (*XfrmState, error) { + return pkgHandle.xfrmStateAllocSpi(state) +} + +// XfrmStateUpdate will update an xfrm state to the system. +// Equivalent to: `ip xfrm state update $state` +func XfrmStateUpdate(state *XfrmState) error { + return pkgHandle.XfrmStateUpdate(state) +} + +// XfrmStateUpdate will update an xfrm state to the system. +// Equivalent to: `ip xfrm state update $state` +func (h *Handle) XfrmStateUpdate(state *XfrmState) error { + return h.xfrmStateAddOrUpdate(state, nl.XFRM_MSG_UPDSA) +} + +func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error { + // A state with spi 0 can't be deleted so don't allow it to be set if state.Spi == 0 { return fmt.Errorf("Spi must be set when adding xfrm state.") } - req := nl.NewNetlinkRequest(nl.XFRM_MSG_NEWSA, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + req := h.newNetlinkRequest(nlProto, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) - msg := &nl.XfrmUsersaInfo{} - msg.Family = uint16(nl.GetIPFamily(state.Dst)) - msg.Id.Daddr.FromIP(state.Dst) - msg.Saddr.FromIP(state.Src) - msg.Id.Proto = uint8(state.Proto) - msg.Mode = uint8(state.Mode) - msg.Id.Spi = nl.Swap32(uint32(state.Spi)) - msg.Reqid = uint32(state.Reqid) - msg.ReplayWindow = uint8(state.ReplayWindow) - msg.Lft.SoftByteLimit = nl.XFRM_INF - msg.Lft.HardByteLimit = nl.XFRM_INF - msg.Lft.SoftPacketLimit = nl.XFRM_INF - msg.Lft.HardPacketLimit = nl.XFRM_INF + msg := xfrmUsersaInfoFromXfrmState(state) + + if state.ESN { + if state.ReplayWindow == 0 { + return fmt.Errorf("ESN flag set without ReplayWindow") + } + msg.Flags |= nl.XFRM_STATE_ESN + msg.ReplayWindow = 0 + } + + limitsToLft(state.Limits, &msg.Lft) req.AddData(msg) if state.Auth != nil { @@ -66,6 +134,10 @@ func XfrmStateAdd(state *XfrmState) error { out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt)) req.AddData(out) } + if state.Aead != nil { + out := nl.NewRtAttr(nl.XFRMA_ALG_AEAD, writeStateAlgoAead(state.Aead)) + req.AddData(out) + } if state.Encap != nil { encapData := make([]byte, nl.SizeofXfrmEncapTmpl) encap := nl.DeserializeXfrmEncapTmpl(encapData) @@ -76,39 +148,75 @@ func XfrmStateAdd(state *XfrmState) error { out := nl.NewRtAttr(nl.XFRMA_ENCAP, encapData) req.AddData(out) } + if state.Mark != nil { + out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(state.Mark)) + req.AddData(out) + } + if state.ESN { + out := nl.NewRtAttr(nl.XFRMA_REPLAY_ESN_VAL, writeReplayEsn(state.ReplayWindow)) + req.AddData(out) + } _, err := req.Execute(syscall.NETLINK_XFRM, 0) return err } +func (h *Handle) xfrmStateAllocSpi(state *XfrmState) (*XfrmState, error) { + req := h.newNetlinkRequest(nl.XFRM_MSG_ALLOCSPI, + syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + msg := &nl.XfrmUserSpiInfo{} + msg.XfrmUsersaInfo = *(xfrmUsersaInfoFromXfrmState(state)) + // 1-255 is reserved by IANA for future use + msg.Min = 0x100 + msg.Max = 0xffffffff + req.AddData(msg) + + if state.Mark != nil { + out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(state.Mark)) + req.AddData(out) + } + + msgs, err := req.Execute(syscall.NETLINK_XFRM, 0) + if err != nil { + return nil, err + } + + s, err := parseXfrmState(msgs[0], FAMILY_ALL) + if err != nil { + return nil, err + } + + return s, err +} + // XfrmStateDel will delete an xfrm state from the system. Note that // the Algos are ignored when matching the state to delete. // Equivalent to: `ip xfrm state del $state` func XfrmStateDel(state *XfrmState) error { - req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELSA, syscall.NLM_F_ACK) + return pkgHandle.XfrmStateDel(state) +} - msg := &nl.XfrmUsersaId{} - msg.Daddr.FromIP(state.Dst) - msg.Family = uint16(nl.GetIPFamily(state.Dst)) - msg.Proto = uint8(state.Proto) - msg.Spi = nl.Swap32(uint32(state.Spi)) - req.AddData(msg) - - saddr := nl.XfrmAddress{} - saddr.FromIP(state.Src) - srcdata := nl.NewRtAttr(nl.XFRMA_SRCADDR, saddr.Serialize()) - - req.AddData(srcdata) - - _, err := req.Execute(syscall.NETLINK_XFRM, 0) +// XfrmStateDel will delete an xfrm state from the system. Note that +// the Algos are ignored when matching the state to delete. +// Equivalent to: `ip xfrm state del $state` +func (h *Handle) XfrmStateDel(state *XfrmState) error { + _, err := h.xfrmStateGetOrDelete(state, nl.XFRM_MSG_DELSA) return err } +// XfrmStateList gets a list of xfrm states in the system. +// Equivalent to: `ip [-4|-6] xfrm state show`. +// The list can be filtered by ip family. +func XfrmStateList(family int) ([]XfrmState, error) { + return pkgHandle.XfrmStateList(family) +} + // XfrmStateList gets a list of xfrm states in the system. // Equivalent to: `ip xfrm state show`. // The list can be filtered by ip family. -func XfrmStateList(family int) ([]XfrmState, error) { - req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP) +func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) { + req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP) msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWSA) if err != nil { @@ -117,62 +225,220 @@ func XfrmStateList(family int) ([]XfrmState, error) { var res []XfrmState for _, m := range msgs { - msg := nl.DeserializeXfrmUsersaInfo(m) - - if family != FAMILY_ALL && family != int(msg.Family) { + if state, err := parseXfrmState(m, family); err == nil { + res = append(res, *state) + } else if err == familyError { continue - } - - var state XfrmState - - state.Dst = msg.Id.Daddr.ToIP() - state.Src = msg.Saddr.ToIP() - state.Proto = Proto(msg.Id.Proto) - state.Mode = Mode(msg.Mode) - state.Spi = int(nl.Swap32(msg.Id.Spi)) - state.Reqid = int(msg.Reqid) - state.ReplayWindow = int(msg.ReplayWindow) - - attrs, err := nl.ParseRouteAttr(m[msg.Len():]) - if err != nil { + } else { return nil, err } - - for _, attr := range attrs { - switch attr.Attr.Type { - case nl.XFRMA_ALG_AUTH, nl.XFRMA_ALG_CRYPT: - var resAlgo *XfrmStateAlgo - if attr.Attr.Type == nl.XFRMA_ALG_AUTH { - if state.Auth == nil { - state.Auth = new(XfrmStateAlgo) - } - resAlgo = state.Auth - } else { - state.Crypt = new(XfrmStateAlgo) - resAlgo = state.Crypt - } - algo := nl.DeserializeXfrmAlgo(attr.Value[:]) - (*resAlgo).Name = nl.BytesToString(algo.AlgName[:]) - (*resAlgo).Key = algo.AlgKey - case nl.XFRMA_ALG_AUTH_TRUNC: - if state.Auth == nil { - state.Auth = new(XfrmStateAlgo) - } - algo := nl.DeserializeXfrmAlgoAuth(attr.Value[:]) - state.Auth.Name = nl.BytesToString(algo.AlgName[:]) - state.Auth.Key = algo.AlgKey - state.Auth.TruncateLen = int(algo.AlgTruncLen) - case nl.XFRMA_ENCAP: - encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:]) - state.Encap = new(XfrmStateEncap) - state.Encap.Type = EncapType(encap.EncapType) - state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport)) - state.Encap.DstPort = int(nl.Swap16(encap.EncapDport)) - state.Encap.OriginalAddress = encap.EncapOa.ToIP() - } - - } - res = append(res, state) } return res, nil } + +// XfrmStateGet gets the xfrm state described by the ID, if found. +// Equivalent to: `ip xfrm state get ID [ mark MARK [ mask MASK ] ]`. +// Only the fields which constitue the SA ID must be filled in: +// ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ] +// mark is optional +func XfrmStateGet(state *XfrmState) (*XfrmState, error) { + return pkgHandle.XfrmStateGet(state) +} + +// XfrmStateGet gets the xfrm state described by the ID, if found. +// Equivalent to: `ip xfrm state get ID [ mark MARK [ mask MASK ] ]`. +// Only the fields which constitue the SA ID must be filled in: +// ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ] +// mark is optional +func (h *Handle) XfrmStateGet(state *XfrmState) (*XfrmState, error) { + return h.xfrmStateGetOrDelete(state, nl.XFRM_MSG_GETSA) +} + +func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState, error) { + req := h.newNetlinkRequest(nlProto, syscall.NLM_F_ACK) + + msg := &nl.XfrmUsersaId{} + msg.Family = uint16(nl.GetIPFamily(state.Dst)) + msg.Daddr.FromIP(state.Dst) + msg.Proto = uint8(state.Proto) + msg.Spi = nl.Swap32(uint32(state.Spi)) + req.AddData(msg) + + if state.Mark != nil { + out := nl.NewRtAttr(nl.XFRMA_MARK, writeMark(state.Mark)) + req.AddData(out) + } + if state.Src != nil { + out := nl.NewRtAttr(nl.XFRMA_SRCADDR, state.Src.To16()) + req.AddData(out) + } + + resType := nl.XFRM_MSG_NEWSA + if nlProto == nl.XFRM_MSG_DELSA { + resType = 0 + } + + msgs, err := req.Execute(syscall.NETLINK_XFRM, uint16(resType)) + if err != nil { + return nil, err + } + + if nlProto == nl.XFRM_MSG_DELSA { + return nil, nil + } + + s, err := parseXfrmState(msgs[0], FAMILY_ALL) + if err != nil { + return nil, err + } + + return s, nil +} + +var familyError = fmt.Errorf("family error") + +func xfrmStateFromXfrmUsersaInfo(msg *nl.XfrmUsersaInfo) *XfrmState { + var state XfrmState + + state.Dst = msg.Id.Daddr.ToIP() + state.Src = msg.Saddr.ToIP() + state.Proto = Proto(msg.Id.Proto) + state.Mode = Mode(msg.Mode) + state.Spi = int(nl.Swap32(msg.Id.Spi)) + state.Reqid = int(msg.Reqid) + state.ReplayWindow = int(msg.ReplayWindow) + lftToLimits(&msg.Lft, &state.Limits) + + return &state +} + +func parseXfrmState(m []byte, family int) (*XfrmState, error) { + msg := nl.DeserializeXfrmUsersaInfo(m) + + // This is mainly for the state dump + if family != FAMILY_ALL && family != int(msg.Family) { + return nil, familyError + } + + state := xfrmStateFromXfrmUsersaInfo(msg) + + attrs, err := nl.ParseRouteAttr(m[nl.SizeofXfrmUsersaInfo:]) + if err != nil { + return nil, err + } + + for _, attr := range attrs { + switch attr.Attr.Type { + case nl.XFRMA_ALG_AUTH, nl.XFRMA_ALG_CRYPT: + var resAlgo *XfrmStateAlgo + if attr.Attr.Type == nl.XFRMA_ALG_AUTH { + if state.Auth == nil { + state.Auth = new(XfrmStateAlgo) + } + resAlgo = state.Auth + } else { + state.Crypt = new(XfrmStateAlgo) + resAlgo = state.Crypt + } + algo := nl.DeserializeXfrmAlgo(attr.Value[:]) + (*resAlgo).Name = nl.BytesToString(algo.AlgName[:]) + (*resAlgo).Key = algo.AlgKey + case nl.XFRMA_ALG_AUTH_TRUNC: + if state.Auth == nil { + state.Auth = new(XfrmStateAlgo) + } + algo := nl.DeserializeXfrmAlgoAuth(attr.Value[:]) + state.Auth.Name = nl.BytesToString(algo.AlgName[:]) + state.Auth.Key = algo.AlgKey + state.Auth.TruncateLen = int(algo.AlgTruncLen) + case nl.XFRMA_ALG_AEAD: + state.Aead = new(XfrmStateAlgo) + algo := nl.DeserializeXfrmAlgoAEAD(attr.Value[:]) + state.Aead.Name = nl.BytesToString(algo.AlgName[:]) + state.Aead.Key = algo.AlgKey + state.Aead.ICVLen = int(algo.AlgICVLen) + case nl.XFRMA_ENCAP: + encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:]) + state.Encap = new(XfrmStateEncap) + state.Encap.Type = EncapType(encap.EncapType) + state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport)) + state.Encap.DstPort = int(nl.Swap16(encap.EncapDport)) + state.Encap.OriginalAddress = encap.EncapOa.ToIP() + case nl.XFRMA_MARK: + mark := nl.DeserializeXfrmMark(attr.Value[:]) + state.Mark = new(XfrmMark) + state.Mark.Value = mark.Value + state.Mark.Mask = mark.Mask + } + } + + return state, nil +} + +// XfrmStateFlush will flush the xfrm state on the system. +// proto = 0 means any transformation protocols +// Equivalent to: `ip xfrm state flush [ proto XFRM-PROTO ]` +func XfrmStateFlush(proto Proto) error { + return pkgHandle.XfrmStateFlush(proto) +} + +// XfrmStateFlush will flush the xfrm state on the system. +// proto = 0 means any transformation protocols +// Equivalent to: `ip xfrm state flush [ proto XFRM-PROTO ]` +func (h *Handle) XfrmStateFlush(proto Proto) error { + req := h.newNetlinkRequest(nl.XFRM_MSG_FLUSHSA, syscall.NLM_F_ACK) + + req.AddData(&nl.XfrmUsersaFlush{Proto: uint8(proto)}) + + _, err := req.Execute(syscall.NETLINK_XFRM, 0) + if err != nil { + return err + } + + return nil +} + +func limitsToLft(lmts XfrmStateLimits, lft *nl.XfrmLifetimeCfg) { + if lmts.ByteSoft != 0 { + lft.SoftByteLimit = lmts.ByteSoft + } else { + lft.SoftByteLimit = nl.XFRM_INF + } + if lmts.ByteHard != 0 { + lft.HardByteLimit = lmts.ByteHard + } else { + lft.HardByteLimit = nl.XFRM_INF + } + if lmts.PacketSoft != 0 { + lft.SoftPacketLimit = lmts.PacketSoft + } else { + lft.SoftPacketLimit = nl.XFRM_INF + } + if lmts.PacketHard != 0 { + lft.HardPacketLimit = lmts.PacketHard + } else { + lft.HardPacketLimit = nl.XFRM_INF + } + lft.SoftAddExpiresSeconds = lmts.TimeSoft + lft.HardAddExpiresSeconds = lmts.TimeHard + lft.SoftUseExpiresSeconds = lmts.TimeUseSoft + lft.HardUseExpiresSeconds = lmts.TimeUseHard +} + +func lftToLimits(lft *nl.XfrmLifetimeCfg, lmts *XfrmStateLimits) { + *lmts = *(*XfrmStateLimits)(unsafe.Pointer(lft)) +} + +func xfrmUsersaInfoFromXfrmState(state *XfrmState) *nl.XfrmUsersaInfo { + msg := &nl.XfrmUsersaInfo{} + msg.Family = uint16(nl.GetIPFamily(state.Dst)) + msg.Id.Daddr.FromIP(state.Dst) + msg.Saddr.FromIP(state.Src) + msg.Id.Proto = uint8(state.Proto) + msg.Mode = uint8(state.Mode) + msg.Id.Spi = nl.Swap32(uint32(state.Spi)) + msg.Reqid = uint32(state.Reqid) + msg.ReplayWindow = uint8(state.ReplayWindow) + + return msg +} diff --git a/vendor/github.com/vishvananda/netns/LICENSE b/vendor/github.com/vishvananda/netns/LICENSE new file mode 100644 index 00000000..9f64db85 --- /dev/null +++ b/vendor/github.com/vishvananda/netns/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014 Vishvananda Ishaya. + Copyright 2014 Docker, Inc. + + 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. diff --git a/vendor/github.com/vishvananda/netns/README.md b/vendor/github.com/vishvananda/netns/README.md new file mode 100644 index 00000000..66a5f725 --- /dev/null +++ b/vendor/github.com/vishvananda/netns/README.md @@ -0,0 +1,51 @@ +# netns - network namespaces in go # + +The netns package provides an ultra-simple interface for handling +network namespaces in go. Changing namespaces requires elevated +privileges, so in most cases this code needs to be run as root. + +## Local Build and Test ## + +You can use go get command: + + go get github.com/vishvananda/netns + +Testing (requires root): + + sudo -E go test github.com/vishvananda/netns + +## Example ## + +```go +package main + +import ( + "fmt" + "net" + "runtime" + "github.com/vishvananda/netns" +) + +func main() { + // Lock the OS Thread so we don't accidentally switch namespaces + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Save the current network namespace + origns, _ := netns.Get() + defer origns.Close() + + // Create a new network namespace + newns, _ := netns.New() + netns.Set(newns) + defer newns.Close() + + // Do something with the network namespace + ifaces, _ := net.Interfaces() + fmt.Printf("Interfaces: %v\n", ifaces) + + // Switch back to the original namespace + netns.Set(origns) +} + +``` diff --git a/vendor/github.com/vishvananda/netns/netns.go b/vendor/github.com/vishvananda/netns/netns.go new file mode 100644 index 00000000..dd2f2157 --- /dev/null +++ b/vendor/github.com/vishvananda/netns/netns.go @@ -0,0 +1,80 @@ +// Package netns allows ultra-simple network namespace handling. NsHandles +// can be retrieved and set. Note that the current namespace is thread +// local so actions that set and reset namespaces should use LockOSThread +// to make sure the namespace doesn't change due to a goroutine switch. +// It is best to close NsHandles when you are done with them. This can be +// accomplished via a `defer ns.Close()` on the handle. Changing namespaces +// requires elevated privileges, so in most cases this code needs to be run +// as root. +package netns + +import ( + "fmt" + "syscall" +) + +// NsHandle is a handle to a network namespace. It can be cast directly +// to an int and used as a file descriptor. +type NsHandle int + +// Equal determines if two network handles refer to the same network +// namespace. This is done by comparing the device and inode that the +// file descriptors point to. +func (ns NsHandle) Equal(other NsHandle) bool { + if ns == other { + return true + } + var s1, s2 syscall.Stat_t + if err := syscall.Fstat(int(ns), &s1); err != nil { + return false + } + if err := syscall.Fstat(int(other), &s2); err != nil { + return false + } + return (s1.Dev == s2.Dev) && (s1.Ino == s2.Ino) +} + +// String shows the file descriptor number and its dev and inode. +func (ns NsHandle) String() string { + var s syscall.Stat_t + if ns == -1 { + return "NS(None)" + } + if err := syscall.Fstat(int(ns), &s); err != nil { + return fmt.Sprintf("NS(%d: unknown)", ns) + } + return fmt.Sprintf("NS(%d: %d, %d)", ns, s.Dev, s.Ino) +} + +// UniqueId returns a string which uniquely identifies the namespace +// associated with the network handle. +func (ns NsHandle) UniqueId() string { + var s syscall.Stat_t + if ns == -1 { + return "NS(none)" + } + if err := syscall.Fstat(int(ns), &s); err != nil { + return "NS(unknown)" + } + return fmt.Sprintf("NS(%d:%d)", s.Dev, s.Ino) +} + +// IsOpen returns true if Close() has not been called. +func (ns NsHandle) IsOpen() bool { + return ns != -1 +} + +// Close closes the NsHandle and resets its file descriptor to -1. +// It is not safe to use an NsHandle after Close() is called. +func (ns *NsHandle) Close() error { + if err := syscall.Close(int(*ns)); err != nil { + return err + } + (*ns) = -1 + return nil +} + +// None gets an empty (closed) NsHandle. +func None() NsHandle { + return NsHandle(-1) +} diff --git a/vendor/github.com/vishvananda/netns/netns_linux.go b/vendor/github.com/vishvananda/netns/netns_linux.go new file mode 100644 index 00000000..7162b86a --- /dev/null +++ b/vendor/github.com/vishvananda/netns/netns_linux.go @@ -0,0 +1,222 @@ +// +build linux + +package netns + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "syscall" +) + +// SYS_SETNS syscall allows changing the namespace of the current process. +var SYS_SETNS = map[string]uintptr{ + "386": 346, + "amd64": 308, + "arm64": 268, + "arm": 375, + "mips": 4344, + "mipsle": 4344, + "ppc64": 350, + "ppc64le": 350, + "s390x": 339, +}[runtime.GOARCH] + +// Deprecated: use syscall pkg instead (go >= 1.5 needed). +const ( + CLONE_NEWUTS = 0x04000000 /* New utsname group? */ + CLONE_NEWIPC = 0x08000000 /* New ipcs */ + CLONE_NEWUSER = 0x10000000 /* New user namespace */ + CLONE_NEWPID = 0x20000000 /* New pid namespace */ + CLONE_NEWNET = 0x40000000 /* New network namespace */ + CLONE_IO = 0x80000000 /* Get io context */ +) + +// Setns sets namespace using syscall. Note that this should be a method +// in syscall but it has not been added. +func Setns(ns NsHandle, nstype int) (err error) { + _, _, e1 := syscall.Syscall(SYS_SETNS, uintptr(ns), uintptr(nstype), 0) + if e1 != 0 { + err = e1 + } + return +} + +// Set sets the current network namespace to the namespace represented +// by NsHandle. +func Set(ns NsHandle) (err error) { + return Setns(ns, CLONE_NEWNET) +} + +// New creates a new network namespace and returns a handle to it. +func New() (ns NsHandle, err error) { + if err := syscall.Unshare(CLONE_NEWNET); err != nil { + return -1, err + } + return Get() +} + +// Get gets a handle to the current threads network namespace. +func Get() (NsHandle, error) { + return GetFromThread(os.Getpid(), syscall.Gettid()) +} + +// GetFromPath gets a handle to a network namespace +// identified by the path +func GetFromPath(path string) (NsHandle, error) { + fd, err := syscall.Open(path, syscall.O_RDONLY, 0) + if err != nil { + return -1, err + } + return NsHandle(fd), nil +} + +// GetFromName gets a handle to a named network namespace such as one +// created by `ip netns add`. +func GetFromName(name string) (NsHandle, error) { + return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name)) +} + +// GetFromPid gets a handle to the network namespace of a given pid. +func GetFromPid(pid int) (NsHandle, error) { + return GetFromPath(fmt.Sprintf("/proc/%d/ns/net", pid)) +} + +// GetFromThread gets a handle to the network namespace of a given pid and tid. +func GetFromThread(pid, tid int) (NsHandle, error) { + return GetFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)) +} + +// GetFromDocker gets a handle to the network namespace of a docker container. +// Id is prefixed matched against the running docker containers, so a short +// identifier can be used as long as it isn't ambiguous. +func GetFromDocker(id string) (NsHandle, error) { + pid, err := getPidForContainer(id) + if err != nil { + return -1, err + } + return GetFromPid(pid) +} + +// borrowed from docker/utils/utils.go +func findCgroupMountpoint(cgroupType string) (string, error) { + output, err := ioutil.ReadFile("/proc/mounts") + if err != nil { + return "", err + } + + // /proc/mounts has 6 fields per line, one mount per line, e.g. + // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 + for _, line := range strings.Split(string(output), "\n") { + parts := strings.Split(line, " ") + if len(parts) == 6 && parts[2] == "cgroup" { + for _, opt := range strings.Split(parts[3], ",") { + if opt == cgroupType { + return parts[1], nil + } + } + } + } + + return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) +} + +// Returns the relative path to the cgroup docker is running in. +// borrowed from docker/utils/utils.go +// modified to get the docker pid instead of using /proc/self +func getThisCgroup(cgroupType string) (string, error) { + dockerpid, err := ioutil.ReadFile("/var/run/docker.pid") + if err != nil { + return "", err + } + result := strings.Split(string(dockerpid), "\n") + if len(result) == 0 || len(result[0]) == 0 { + return "", fmt.Errorf("docker pid not found in /var/run/docker.pid") + } + pid, err := strconv.Atoi(result[0]) + + output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) + if err != nil { + return "", err + } + for _, line := range strings.Split(string(output), "\n") { + parts := strings.Split(line, ":") + // any type used by docker should work + if parts[1] == cgroupType { + return parts[2], nil + } + } + return "", fmt.Errorf("cgroup '%s' not found in /proc/%d/cgroup", cgroupType, pid) +} + +// Returns the first pid in a container. +// borrowed from docker/utils/utils.go +// modified to only return the first pid +// modified to glob with id +// modified to search for newer docker containers +func getPidForContainer(id string) (int, error) { + pid := 0 + + // memory is chosen randomly, any cgroup used by docker works + cgroupType := "memory" + + cgroupRoot, err := findCgroupMountpoint(cgroupType) + if err != nil { + return pid, err + } + + cgroupThis, err := getThisCgroup(cgroupType) + if err != nil { + return pid, err + } + + id += "*" + + attempts := []string{ + filepath.Join(cgroupRoot, cgroupThis, id, "tasks"), + // With more recent lxc versions use, cgroup will be in lxc/ + filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks"), + // With more recent docker, cgroup will be in docker/ + filepath.Join(cgroupRoot, cgroupThis, "docker", id, "tasks"), + // Even more recent docker versions under systemd use docker-.scope/ + filepath.Join(cgroupRoot, "system.slice", "docker-"+id+".scope", "tasks"), + // Even more recent docker versions under cgroup/systemd/docker// + filepath.Join(cgroupRoot, "..", "systemd", "docker", id, "tasks"), + } + + var filename string + for _, attempt := range attempts { + filenames, _ := filepath.Glob(attempt) + if len(filenames) > 1 { + return pid, fmt.Errorf("Ambiguous id supplied: %v", filenames) + } else if len(filenames) == 1 { + filename = filenames[0] + break + } + } + + if filename == "" { + return pid, fmt.Errorf("Unable to find container: %v", id[:len(id)-1]) + } + + output, err := ioutil.ReadFile(filename) + if err != nil { + return pid, err + } + + result := strings.Split(string(output), "\n") + if len(result) == 0 || len(result[0]) == 0 { + return pid, fmt.Errorf("No pid found for container") + } + + pid, err = strconv.Atoi(result[0]) + if err != nil { + return pid, fmt.Errorf("Invalid pid '%s': %s", result[0], err) + } + + return pid, nil +} diff --git a/vendor/github.com/vishvananda/netns/netns_unspecified.go b/vendor/github.com/vishvananda/netns/netns_unspecified.go new file mode 100644 index 00000000..d06af62b --- /dev/null +++ b/vendor/github.com/vishvananda/netns/netns_unspecified.go @@ -0,0 +1,43 @@ +// +build !linux + +package netns + +import ( + "errors" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) + +func Set(ns NsHandle) (err error) { + return ErrNotImplemented +} + +func New() (ns NsHandle, err error) { + return -1, ErrNotImplemented +} + +func Get() (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromPath(path string) (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromName(name string) (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromPid(pid int) (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromThread(pid, tid int) (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromDocker(id string) (NsHandle, error) { + return -1, ErrNotImplemented +} From 64949bb8881ef1717e8ca5e6ab295878580aaf14 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Sun, 12 Mar 2017 21:26:03 +1000 Subject: [PATCH 08/13] WIP refactor to simplify reading Signed-off-by: Sven Dowideit --- .../datasource/metadata/ec2/metadata.go | 21 ++++++++++--------- .../datasource/metadata/ec2/metadata_test.go | 10 ++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/config/cloudinit/datasource/metadata/ec2/metadata.go b/config/cloudinit/datasource/metadata/ec2/metadata.go index 2ffef4df..c2b328af 100755 --- a/config/cloudinit/datasource/metadata/ec2/metadata.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata.go @@ -32,8 +32,8 @@ import ( const ( DefaultAddress = "http://169.254.169.254/" apiVersion = "2009-04-04/" - userdataPath = apiVersion + "user-data" - metadataPath = apiVersion + "meta-data" + userdataPath = apiVersion + "user-data/" + metadataPath = apiVersion + "meta-data/" ) type MetadataService struct { @@ -51,7 +51,7 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { metadata := datasource.Metadata{} metadata.NetworkConfig = netconf.NetworkConfig{} - if keynames, err := ms.fetchAttributes(fmt.Sprintf("%s/public-keys", ms.MetadataURL())); err == nil { + if keynames, err := ms.fetchAttributes("public-keys"); err == nil { keyIDs := make(map[string]string) for _, keyname := range keynames { tokens := strings.SplitN(keyname, "=", 2) @@ -63,7 +63,7 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { metadata.SSHPublicKeys = map[string]string{} for name, id := range keyIDs { - sshkey, err := ms.fetchAttribute(fmt.Sprintf("%s/public-keys/%s/openssh-key", ms.MetadataURL(), id)) + sshkey, err := ms.fetchAttribute(fmt.Sprintf("public-keys/%s/openssh-key", id)) if err != nil { return metadata, err } @@ -74,14 +74,14 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { return metadata, err } - if hostname, err := ms.fetchAttribute(fmt.Sprintf("%s/hostname", ms.MetadataURL())); err == nil { + if hostname, err := ms.fetchAttribute("hostname"); err == nil { metadata.Hostname = strings.Split(hostname, " ")[0] } else if _, ok := err.(pkg.ErrNotFound); !ok { return metadata, err } network := netconf.InterfaceConfig{} - if localAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/local-ipv4", ms.MetadataURL())); err == nil { + if localAddr, err := ms.fetchAttribute("local-ipv4"); err == nil { metadata.PrivateIPv4 = net.ParseIP(localAddr) network.Addresses = append(network.Addresses, localAddr) @@ -89,7 +89,7 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { return metadata, err } - if publicAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/public-ipv4", ms.MetadataURL())); err == nil { + if publicAddr, err := ms.fetchAttribute("public-ipv4"); err == nil { metadata.PublicIPv4 = net.ParseIP(publicAddr) network.Addresses = append(network.Addresses, publicAddr) } else if _, ok := err.(pkg.ErrNotFound); !ok { @@ -106,7 +106,8 @@ func (ms MetadataService) Type() string { return "ec2-metadata-service" } -func (ms MetadataService) fetchAttributes(url string) ([]string, error) { +func (ms MetadataService) fetchAttributes(key string) ([]string, error) { + url := ms.MetadataURL() + key resp, err := ms.FetchData(url) if err != nil { return nil, err @@ -119,8 +120,8 @@ func (ms MetadataService) fetchAttributes(url string) ([]string, error) { return data, scanner.Err() } -func (ms MetadataService) fetchAttribute(url string) (string, error) { - attrs, err := ms.fetchAttributes(url) +func (ms MetadataService) fetchAttribute(key string) (string, error) { + attrs, err := ms.fetchAttributes(key) if err == nil && len(attrs) > 0 { return attrs[0], nil } diff --git a/config/cloudinit/datasource/metadata/ec2/metadata_test.go b/config/cloudinit/datasource/metadata/ec2/metadata_test.go index e6784a95..208fc109 100755 --- a/config/cloudinit/datasource/metadata/ec2/metadata_test.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata_test.go @@ -153,7 +153,7 @@ func TestFetchMetadata(t *testing.T) { }{ { root: "/", - metadataPath: "2009-04-04/meta-data", + metadataPath: "2009-04-04/meta-data/", resources: map[string]string{ "/2009-04-04/meta-data/public-keys": "bad\n", }, @@ -161,7 +161,7 @@ func TestFetchMetadata(t *testing.T) { }, { root: "/", - metadataPath: "2009-04-04/meta-data", + metadataPath: "2009-04-04/meta-data/", resources: map[string]string{ "/2009-04-04/meta-data/hostname": "host", "/2009-04-04/meta-data/local-ipv4": "1.2.3.4", @@ -189,7 +189,7 @@ func TestFetchMetadata(t *testing.T) { }, { root: "/", - metadataPath: "2009-04-04/meta-data", + metadataPath: "2009-04-04/meta-data/", resources: map[string]string{ "/2009-04-04/meta-data/hostname": "host domain another_domain", "/2009-04-04/meta-data/local-ipv4": "1.2.3.4", @@ -227,10 +227,10 @@ func TestFetchMetadata(t *testing.T) { }} metadata, err := service.FetchMetadata() if Error(err) != Error(tt.expectErr) { - t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err) + t.Fatalf("bad error (%q): \nwant %q, \ngot %q\n", tt.resources, tt.expectErr, err) } if !reflect.DeepEqual(tt.expect, metadata) { - t.Fatalf("bad fetch (%q): want %#v, got %#v", tt.resources, tt.expect, metadata) + t.Fatalf("bad fetch (%q): \nwant %#v, \ngot %#v\n", tt.resources, tt.expect, metadata) } } } From d65f9518dfb535570b844d715f2ba6a3ce7fa708 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 13 Mar 2017 10:23:57 +1000 Subject: [PATCH 09/13] use DHCP for aws datasource Signed-off-by: Sven Dowideit --- cmd/network/network.go | 1 + .../datasource/metadata/ec2/metadata.go | 49 ++++++++++++++++--- .../datasource/metadata/ec2/metadata_test.go | 36 +++++++------- netconf/netconf_linux.go | 7 ++- 4 files changed, 66 insertions(+), 27 deletions(-) mode change 100644 => 100755 cmd/network/network.go diff --git a/cmd/network/network.go b/cmd/network/network.go old mode 100644 new mode 100755 index 00289927..dce8a734 --- a/cmd/network/network.go +++ b/cmd/network/network.go @@ -41,6 +41,7 @@ func ApplyNetworkConfig(cfg *config.CloudConfig) { log.Error(err) } + // TODO: seems wrong to do this outside netconf userSetHostname := cfg.Hostname != "" if err := netconf.RunDhcp(&cfg.Rancher.Network, !userSetHostname, !userSetDNS); err != nil { log.Error(err) diff --git a/config/cloudinit/datasource/metadata/ec2/metadata.go b/config/cloudinit/datasource/metadata/ec2/metadata.go index c2b328af..01971229 100755 --- a/config/cloudinit/datasource/metadata/ec2/metadata.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata.go @@ -31,7 +31,7 @@ import ( const ( DefaultAddress = "http://169.254.169.254/" - apiVersion = "2009-04-04/" + apiVersion = "latest/" userdataPath = apiVersion + "user-data/" metadataPath = apiVersion + "meta-data/" ) @@ -48,6 +48,7 @@ func NewDatasource(root string) *MetadataService { } func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { + // see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html metadata := datasource.Metadata{} metadata.NetworkConfig = netconf.NetworkConfig{} @@ -80,24 +81,58 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { return metadata, err } - network := netconf.InterfaceConfig{} + // TODO: these are only on the first interface - it looks like you can have as many as you need... if localAddr, err := ms.fetchAttribute("local-ipv4"); err == nil { metadata.PrivateIPv4 = net.ParseIP(localAddr) - network.Addresses = append(network.Addresses, localAddr) - } else if _, ok := err.(pkg.ErrNotFound); !ok { return metadata, err } - if publicAddr, err := ms.fetchAttribute("public-ipv4"); err == nil { metadata.PublicIPv4 = net.ParseIP(publicAddr) - network.Addresses = append(network.Addresses, publicAddr) } else if _, ok := err.(pkg.ErrNotFound); !ok { return metadata, err } metadata.NetworkConfig.Interfaces = make(map[string]netconf.InterfaceConfig) - metadata.NetworkConfig.Interfaces["eth0"] = network + if macs, err := ms.fetchAttributes("network/interfaces/macs"); err != nil { + for _, mac := range macs { + if deviceNumber, err := ms.fetchAttribute(fmt.Sprintf("network/interfaces/macs/%s/device-number", mac)); err != nil { + network := netconf.InterfaceConfig{ + DHCP: true, + } + /* Looks like we must use DHCP for aws + // private ipv4 + if subnetCidrBlock, err := ms.fetchAttribute(fmt.Sprintf("network/interfaces/macs/%s/subnet-ipv4-cidr-block", mac)); err != nil { + cidr := strings.Split(subnetCidrBlock, "/") + if localAddr, err := ms.fetchAttributes(fmt.Sprintf("network/interfaces/macs/%s/local-ipv4s", mac)); err != nil { + for _, addr := range localAddr { + network.Addresses = append(network.Addresses, addr+"/"+cidr[1]) + } + } + } + // ipv6 + if localAddr, err := ms.fetchAttributes(fmt.Sprintf("network/interfaces/macs/%s/ipv6s", mac)); err != nil { + if subnetCidrBlock, err := ms.fetchAttributes(fmt.Sprintf("network/interfaces/macs/%s/subnet-ipv6-cidr-block", mac)); err != nil { + for i, addr := range localAddr { + cidr := strings.Split(subnetCidrBlock[i], "/") + network.Addresses = append(network.Addresses, addr+"/"+cidr[1]) + } + } + } + */ + // disabled - it looks to me like you don't actually put the public IP on the eth device + /* if publicAddr, err := ms.fetchAttributes(fmt.Sprintf("network/interfaces/macs/%s/public-ipv4s", mac)); err != nil { + if vpcCidrBlock, err := ms.fetchAttribute(fmt.Sprintf("network/interfaces/macs/%s/vpc-ipv4-cidr-block", mac)); err != nil { + cidr := strings.Split(vpcCidrBlock, "/") + network.Addresses = append(network.Addresses, publicAddr+"/"+cidr[1]) + } + } + */ + + metadata.NetworkConfig.Interfaces["eth"+deviceNumber] = network + } + } + } return metadata, nil } diff --git a/config/cloudinit/datasource/metadata/ec2/metadata_test.go b/config/cloudinit/datasource/metadata/ec2/metadata_test.go index 208fc109..4c266a7f 100755 --- a/config/cloudinit/datasource/metadata/ec2/metadata_test.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata_test.go @@ -177,13 +177,13 @@ func TestFetchMetadata(t *testing.T) { SSHPublicKeys: map[string]string{"test1": "key"}, NetworkConfig: netconf.NetworkConfig{ Interfaces: map[string]netconf.InterfaceConfig{ - "eth0": netconf.InterfaceConfig{ - Addresses: []string{ - "1.2.3.4", - "5.6.7.8", - }, - }, - }, + /* "eth0": netconf.InterfaceConfig{ + Addresses: []string{ + "1.2.3.4", + "5.6.7.8", + }, + }, + */}, }, }, }, @@ -192,26 +192,26 @@ func TestFetchMetadata(t *testing.T) { metadataPath: "2009-04-04/meta-data/", resources: map[string]string{ "/2009-04-04/meta-data/hostname": "host domain another_domain", - "/2009-04-04/meta-data/local-ipv4": "1.2.3.4", - "/2009-04-04/meta-data/public-ipv4": "5.6.7.8", + "/2009-04-04/meta-data/local-ipv4": "21.2.3.4", + "/2009-04-04/meta-data/public-ipv4": "25.6.7.8", "/2009-04-04/meta-data/public-keys": "0=test1\n", "/2009-04-04/meta-data/public-keys/0": "openssh-key", "/2009-04-04/meta-data/public-keys/0/openssh-key": "key", }, expect: datasource.Metadata{ Hostname: "host", - PrivateIPv4: net.ParseIP("1.2.3.4"), - PublicIPv4: net.ParseIP("5.6.7.8"), + PrivateIPv4: net.ParseIP("21.2.3.4"), + PublicIPv4: net.ParseIP("25.6.7.8"), SSHPublicKeys: map[string]string{"test1": "key"}, NetworkConfig: netconf.NetworkConfig{ Interfaces: map[string]netconf.InterfaceConfig{ - "eth0": netconf.InterfaceConfig{ - Addresses: []string{ - "1.2.3.4", - "5.6.7.8", - }, - }, - }, + /* "eth0": netconf.InterfaceConfig{ + Addresses: []string{ + "1.2.3.4", + "5.6.7.8", + }, + }, + */}, }, }, }, diff --git a/netconf/netconf_linux.go b/netconf/netconf_linux.go index 205c5f1f..29979842 100755 --- a/netconf/netconf_linux.go +++ b/netconf/netconf_linux.go @@ -148,7 +148,10 @@ func populateDefault(netCfg *NetworkConfig) { if _, ok := netCfg.Interfaces["lo"]; !ok { netCfg.Interfaces["lo"] = InterfaceConfig{ - Address: "127.0.0.1/8", + Addresses: []string{ + "127.0.0.1/8", + "::1/128", + }, } } } @@ -401,7 +404,7 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { log.Errorf("Fail to set gateway %s", netConf.GatewayIpv6) } - // TODO: how to remove a GW? + // TODO: how to remove a GW? (on aws it seems to be hard to find out what the gw is :/) runCmds(netConf.PostUp, link.Attrs().Name) return nil From 78c08c4dd981215348b86cbdc63b9f44aa02ca4b Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 13 Mar 2017 13:14:54 +1000 Subject: [PATCH 10/13] ok, lets not remove the un-speced ip's in 0.9.0 Signed-off-by: Sven Dowideit --- .../datasource/metadata/ec2/metadata.go | 5 +++ netconf/netconf_linux.go | 36 ++++++++++++------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/config/cloudinit/datasource/metadata/ec2/metadata.go b/config/cloudinit/datasource/metadata/ec2/metadata.go index 01971229..8ef35c92 100755 --- a/config/cloudinit/datasource/metadata/ec2/metadata.go +++ b/config/cloudinit/datasource/metadata/ec2/metadata.go @@ -47,6 +47,11 @@ func NewDatasource(root string) *MetadataService { return &MetadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, nil)} } +func (ms MetadataService) AvailabilityChanges() bool { + // TODO: if it can't find the network, maybe we can start it? + return false +} + func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { // see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html metadata := datasource.Metadata{} diff --git a/netconf/netconf_linux.go b/netconf/netconf_linux.go index 29979842..a03f34f3 100755 --- a/netconf/netconf_linux.go +++ b/netconf/netconf_linux.go @@ -282,8 +282,9 @@ func removeAddress(addr netlink.Addr, link netlink.Link) error { return nil } -// setGateway will set _one_ gateway on an interface (ie, replace an existing one) -func setGateway(gateway string) error { +// setGateway(add=false) will set _one_ gateway on an interface (ie, replace an existing one) +// setGateway(add=true) will add another gateway to an interface +func setGateway(gateway string, add bool) error { if gateway == "" { return nil } @@ -298,11 +299,20 @@ func setGateway(gateway string) error { Gw: gatewayIP, } - if err := netlink.RouteReplace(&route); err == syscall.EEXIST { - //Ignore this error - } else if err != nil { - log.Errorf("gateway set failed: %v", err) - return err + if add { + if err := netlink.RouteAdd(&route); err == syscall.EEXIST { + //Ignore this error + } else if err != nil { + log.Errorf("gateway add failed: %v", err) + return err + } + } else { + if err := netlink.RouteReplace(&route); err == syscall.EEXIST { + //Ignore this error + } else if err != nil { + log.Errorf("gateway replace failed: %v", err) + return err + } } log.Infof("Set default gateway %s", gateway) @@ -376,8 +386,8 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { } for _, addr := range existingAddrs { if _, ok := addrMap[addr.IPNet.String()]; !ok { - log.Infof("removing %s from %s", addr.String(), link.Attrs().Name) - removeAddress(addr, link) + log.Infof("leaving %s from %s", addr.String(), link.Attrs().Name) + //removeAddress(addr, link) } } @@ -395,12 +405,12 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { return err } - // TODO: how to remove a GW? - if err := setGateway(netConf.Gateway); err != nil { + // replace the existing gw with the main ipv4 one + if err := setGateway(netConf.Gateway, false); err != nil { log.Errorf("Fail to set gateway %s", netConf.Gateway) } - - if err := setGateway(netConf.GatewayIpv6); err != nil { + //and then add the ipv6 one if it exists + if err := setGateway(netConf.GatewayIpv6, true); err != nil { log.Errorf("Fail to set gateway %s", netConf.GatewayIpv6) } From 8a4fa93202e55d4065c0c604cd4290635c039e35 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 13 Mar 2017 14:27:24 +1000 Subject: [PATCH 11/13] lets also not replace routes :( Signed-off-by: Sven Dowideit --- netconf/netconf_linux.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/netconf/netconf_linux.go b/netconf/netconf_linux.go index a03f34f3..30015fca 100755 --- a/netconf/netconf_linux.go +++ b/netconf/netconf_linux.go @@ -306,6 +306,7 @@ func setGateway(gateway string, add bool) error { log.Errorf("gateway add failed: %v", err) return err } + log.Infof("Added default gateway %s", gateway) } else { if err := netlink.RouteReplace(&route); err == syscall.EEXIST { //Ignore this error @@ -313,9 +314,9 @@ func setGateway(gateway string, add bool) error { log.Errorf("gateway replace failed: %v", err) return err } + log.Infof("Replaced default gateway %s", gateway) } - log.Infof("Set default gateway %s", gateway) return nil } @@ -406,7 +407,7 @@ func applyInterfaceConfig(link netlink.Link, netConf InterfaceConfig) error { } // replace the existing gw with the main ipv4 one - if err := setGateway(netConf.Gateway, false); err != nil { + if err := setGateway(netConf.Gateway, true); err != nil { log.Errorf("Fail to set gateway %s", netConf.Gateway) } //and then add the ipv6 one if it exists From be9874d2f465f072e5238f532d59bc1d3bca8956 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 13 Mar 2017 14:51:52 +1000 Subject: [PATCH 12/13] use the rancher.state.dev cfg to detect where we're installing Signed-off-by: Sven Dowideit --- cmd/control/install.go | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/control/install.go b/cmd/control/install.go index 36044a96..1ff89c81 100755 --- a/cmd/control/install.go +++ b/cmd/control/install.go @@ -742,23 +742,29 @@ func mountdevice(baseName, bootDir, partition string, raw bool) (string, string, //rootfs := partition // Don't use ResolveDevice - it can fail, whereas `blkid -L LABEL` works more often - //if dev := util.ResolveDevice("LABEL=RANCHER_BOOT"); dev != "" { - cmd := exec.Command("blkid", "-L", "RANCHER_BOOT") - log.Debugf("Run(%v)", cmd) - cmd.Stderr = os.Stderr - if out, err := cmd.Output(); err == nil { - partition = strings.TrimSpace(string(out)) - baseName = filepath.Join(baseName, "boot") + + cfg := config.LoadConfig() + if dev := util.ResolveDevice(cfg.Rancher.State.Dev); dev != "" { + // try the rancher.state.dev setting + partition = dev } else { - cmd := exec.Command("blkid", "-L", "RANCHER_STATE") + cmd := exec.Command("blkid", "-L", "RANCHER_BOOT") log.Debugf("Run(%v)", cmd) cmd.Stderr = os.Stderr if out, err := cmd.Output(); err == nil { partition = strings.TrimSpace(string(out)) + baseName = filepath.Join(baseName, "boot") + } else { + cmd := exec.Command("blkid", "-L", "RANCHER_STATE") + log.Debugf("Run(%v)", cmd) + cmd.Stderr = os.Stderr + if out, err := cmd.Output(); err == nil { + partition = strings.TrimSpace(string(out)) + } } } device := "" - cmd = exec.Command("lsblk", "-no", "pkname", partition) + cmd := exec.Command("lsblk", "-no", "pkname", partition) log.Debugf("Run(%v)", cmd) cmd.Stderr = os.Stderr if out, err := cmd.Output(); err == nil { From 90963f8f45a2462cdda89256473cea98bd8717b8 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 13 Mar 2017 16:13:36 +1000 Subject: [PATCH 13/13] fix test Signed-off-by: Sven Dowideit --- tests/network_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/network_test.go b/tests/network_test.go index 229600e1..9588bc54 100755 --- a/tests/network_test.go +++ b/tests/network_test.go @@ -52,6 +52,8 @@ func (s *QemuSuite) TestNetworkCfg(c *C) { link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 10.1.0.41/24 scope global eth0 valid_lft forever preferred_lft forever