1
0
mirror of https://github.com/rancher/os.git synced 2025-06-30 00:31:49 +00:00

Merge pull request #1666 from SvenDowideit/refactor-cloud-init

WIP Refactor cloud init
This commit is contained in:
Sven Dowideit 2017-03-13 16:14:28 +10:00 committed by GitHub
commit 299d59b5fc
79 changed files with 5740 additions and 1226 deletions

View File

@ -16,8 +16,10 @@
package cloudinitsave package cloudinitsave
import ( import (
"bytes"
"errors" "errors"
"os" "os"
"path"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -60,16 +62,21 @@ func Main() {
cfg := rancherConfig.LoadConfig() cfg := rancherConfig.LoadConfig()
network.ApplyNetworkConfig(cfg) network.ApplyNetworkConfig(cfg)
if err := SaveCloudConfig(true); err != nil { if err := SaveCloudConfig(); err != nil {
log.Errorf("Failed to save cloud-config: %v", err) 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 { func SaveCloudConfig() error {
log.Debugf("SaveCloudConfig") log.Debugf("SaveCloudConfig")
cfg := rancherConfig.LoadConfig() cfg := rancherConfig.LoadConfig()
dss := getDatasources(cfg, network) dss := getDatasources(cfg)
if len(dss) == 0 { if len(dss) == 0 {
log.Errorf("currentDatasource - none found") log.Errorf("currentDatasource - none found")
return nil return nil
@ -81,6 +88,7 @@ func SaveCloudConfig(network bool) error {
func RequiresNetwork(datasource string) bool { func RequiresNetwork(datasource string) bool {
// TODO: move into the datasources (and metadatasources) // TODO: move into the datasources (and metadatasources)
// and then we can enable that platforms defaults..
parts := strings.SplitN(datasource, ":", 2) parts := strings.SplitN(datasource, ":", 2)
requiresNetwork, ok := map[string]bool{ requiresNetwork, ok := map[string]bool{
"ec2": true, "ec2": true,
@ -110,8 +118,8 @@ func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadat
if err := util.WriteFileAtomic(rancherConfig.CloudConfigBootFile, cloudConfigBytes, 400); err != nil { if err := util.WriteFileAtomic(rancherConfig.CloudConfigBootFile, cloudConfigBytes, 400); err != nil {
return err return err
} }
// Don't put secrets into the log // TODO: Don't put secrets into the log
//log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes)) log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes))
} }
metaDataBytes, err := yaml.Marshal(metadata) metaDataBytes, err := yaml.Marshal(metadata)
@ -122,8 +130,34 @@ func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadat
if err = util.WriteFileAtomic(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil { if err = util.WriteFileAtomic(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil {
return err return err
} }
// Don't put secrets into the log // TODO: Don't put secrets into the log
//log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes)) 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 return nil
} }
@ -161,7 +195,7 @@ func fetchAndSave(ds datasource.Datasource) error {
userDataBytes = []byte{} userDataBytes = []byte{}
} }
} else { } else {
log.Errorf("Unrecognized user-data\n%s", userData) log.Errorf("Unrecognized user-data\n(%s)", userData)
userDataBytes = []byte{} userDataBytes = []byte{}
} }
@ -175,60 +209,43 @@ func fetchAndSave(ds datasource.Datasource) error {
// getDatasources creates a slice of possible Datasources for cloudinit based // getDatasources creates a slice of possible Datasources for cloudinit based
// on the different source command-line flags. // 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) dss := make([]datasource.Datasource, 0, 5)
for _, ds := range cfg.Rancher.CloudInit.Datasources { for _, ds := range cfg.Rancher.CloudInit.Datasources {
parts := strings.SplitN(ds, ":", 2) parts := strings.SplitN(ds, ":", 2)
root := ""
if len(parts) > 1 {
root = parts[1]
}
switch parts[0] { switch parts[0] {
case "ec2": case "ec2":
if network { dss = append(dss, ec2.NewDatasource(root))
if len(parts) == 1 {
dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress))
} else {
dss = append(dss, ec2.NewDatasource(parts[1]))
}
}
case "file": case "file":
if len(parts) == 2 { if root != "" {
dss = append(dss, file.NewDatasource(parts[1])) dss = append(dss, file.NewDatasource(root))
} }
case "url": case "url":
if network { if root != "" {
if len(parts) == 2 { dss = append(dss, url.NewDatasource(root))
dss = append(dss, url.NewDatasource(parts[1]))
}
} }
case "cmdline": case "cmdline":
if network {
if len(parts) == 1 { if len(parts) == 1 {
dss = append(dss, proccmdline.NewDatasource()) dss = append(dss, proccmdline.NewDatasource())
} }
}
case "configdrive": case "configdrive":
if len(parts) == 2 { if root != "" {
dss = append(dss, configdrive.NewDatasource(parts[1])) dss = append(dss, configdrive.NewDatasource(root))
} }
case "digitalocean": case "digitalocean":
if network { // TODO: should we enableDoLinkLocal() - to avoid the need for the other kernel/oem options?
if len(parts) == 1 { dss = append(dss, digitalocean.NewDatasource(root))
dss = append(dss, digitalocean.NewDatasource(digitalocean.DefaultAddress))
} else {
dss = append(dss, digitalocean.NewDatasource(parts[1]))
}
} else {
enableDoLinkLocal()
}
case "gce": case "gce":
if network { dss = append(dss, gce.NewDatasource(root))
dss = append(dss, gce.NewDatasource("http://metadata.google.internal/"))
}
case "packet": case "packet":
if !network { dss = append(dss, packet.NewDatasource(root))
enablePacketNetwork(&cfg.Rancher)
}
dss = append(dss, packet.NewDatasource("https://metadata.packet.net/"))
} }
} }
@ -236,8 +253,8 @@ func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.D
} }
func enableDoLinkLocal() { func enableDoLinkLocal() {
err := netconf.ApplyNetworkConfigs(&rancherConfig.NetworkConfig{ err := netconf.ApplyNetworkConfigs(&netconf.NetworkConfig{
Interfaces: map[string]rancherConfig.InterfaceConfig{ Interfaces: map[string]netconf.InterfaceConfig{
"eth0": { "eth0": {
IPV4LL: true, IPV4LL: true,
}, },

View File

@ -1,104 +0,0 @@
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"
)
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
}
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
}
}
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))
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 := config.WriteToFile(cc, config.CloudConfigNetworkFile); err != nil {
log.Errorf("Failed to save config file %s: %v", config.CloudConfigNetworkFile, err)
}
}

View File

@ -742,7 +742,12 @@ func mountdevice(baseName, bootDir, partition string, raw bool) (string, string,
//rootfs := partition //rootfs := partition
// Don't use ResolveDevice - it can fail, whereas `blkid -L LABEL` works more often // Don't use ResolveDevice - it can fail, whereas `blkid -L LABEL` works more often
//if dev := util.ResolveDevice("LABEL=RANCHER_BOOT"); dev != "" {
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_BOOT") cmd := exec.Command("blkid", "-L", "RANCHER_BOOT")
log.Debugf("Run(%v)", cmd) log.Debugf("Run(%v)", cmd)
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@ -757,8 +762,9 @@ func mountdevice(baseName, bootDir, partition string, raw bool) (string, string,
partition = strings.TrimSpace(string(out)) partition = strings.TrimSpace(string(out))
} }
} }
}
device := "" device := ""
cmd = exec.Command("lsblk", "-no", "pkname", partition) cmd := exec.Command("lsblk", "-no", "pkname", partition)
log.Debugf("Run(%v)", cmd) log.Debugf("Run(%v)", cmd)
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
if out, err := cmd.Output(); err == nil { if out, err := cmd.Output(); err == nil {

2
cmd/network/network.go Normal file → Executable file
View File

@ -28,6 +28,7 @@ func ApplyNetworkConfig(cfg *config.CloudConfig) {
search = cfg.Rancher.Defaults.Network.DNS.Search 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 { if _, err := resolvconf.Build("/etc/resolv.conf", nameservers, search, nil); err != nil {
log.Error(err) log.Error(err)
} }
@ -40,6 +41,7 @@ func ApplyNetworkConfig(cfg *config.CloudConfig) {
log.Error(err) log.Error(err)
} }
// TODO: seems wrong to do this outside netconf
userSetHostname := cfg.Hostname != "" userSetHostname := cfg.Hostname != ""
if err := netconf.RunDhcp(&cfg.Rancher.Network, !userSetHostname, !userSetDNS); err != nil { if err := netconf.RunDhcp(&cfg.Rancher.Network, !userSetHostname, !userSetDNS); err != nil {
log.Error(err) log.Error(err)

View File

@ -102,10 +102,11 @@ func (cd *ConfigDrive) FetchMetadata() (metadata datasource.Metadata, err error)
metadata.SSHPublicKeys = m.SSHAuthorizedKeyMap metadata.SSHPublicKeys = m.SSHAuthorizedKeyMap
metadata.Hostname = m.Hostname metadata.Hostname = m.Hostname
if 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)) metadata.NetworkConfig, err = cd.tryReadFile(path.Join(cd.openstackRoot(), m.NetworkConfig.ContentPath))
} }
*/
return return
} }

View File

@ -49,7 +49,6 @@ func TestFetchMetadata(t *testing.T) {
), ),
metadata: datasource.Metadata{ metadata: datasource.Metadata{
Hostname: "host", Hostname: "host",
NetworkConfig: []byte("make it work"),
SSHPublicKeys: map[string]string{ SSHPublicKeys: map[string]string{
"1": "key1", "1": "key1",
"2": "key2", "2": "key2",

View File

@ -16,6 +16,8 @@ package datasource
import ( import (
"net" "net"
"github.com/rancher/os/netconf"
) )
type Datasource interface { type Datasource interface {
@ -31,11 +33,14 @@ type Datasource interface {
} }
type Metadata struct { type Metadata struct {
// TODO: move to netconf/types.go ?
// see https://ahmetalpbalkan.com/blog/comparison-of-instance-metadata-services/
Hostname string
SSHPublicKeys map[string]string
NetworkConfig netconf.NetworkConfig
PublicIPv4 net.IP PublicIPv4 net.IP
PublicIPv6 net.IP PublicIPv6 net.IP
PrivateIPv4 net.IP PrivateIPv4 net.IP
PrivateIPv6 net.IP PrivateIPv6 net.IP
Hostname string
SSHPublicKeys map[string]string
NetworkConfig interface{}
} }

View File

@ -16,9 +16,13 @@ package digitalocean
import ( import (
"encoding/json" "encoding/json"
"net" "fmt"
"strconv" "strconv"
"github.com/rancher/os/netconf"
"net"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata"
) )
@ -66,9 +70,28 @@ type MetadataService struct {
} }
func NewDatasource(root string) *MetadataService { func NewDatasource(root string) *MetadataService {
if root == "" {
root = DefaultAddress
}
return &MetadataService{Service: metadata.NewDatasource(root, apiVersion, userdataURL, metadataPath, nil)} 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) { func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err error) {
var data []byte var data []byte
var m Metadata var m Metadata
@ -96,12 +119,68 @@ func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err er
metadata.PrivateIPv6 = net.ParseIP(m.Interfaces.Private[0].IPv6.IPAddress) 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, ipmask(eth.IPv4))
if metadata.PublicIPv4 == nil {
metadata.PublicIPv4 = net.ParseIP(eth.IPv4.IPAddress)
}
}
if eth.AnchorIPv4 != nil {
network.Addresses = append(network.Addresses, ipmask(eth.AnchorIPv4))
}
if eth.IPv6 != nil {
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)
}
}
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, ipmask(eth.IPv4))
if metadata.PrivateIPv4 == nil {
metadata.PrivateIPv4 = net.ParseIP(eth.IPv6.IPAddress)
}
}
if eth.AnchorIPv4 != nil {
network.Addresses = append(network.Addresses, ipmask(eth.AnchorIPv4))
}
if eth.IPv6 != nil {
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)
}
}
metadata.NetworkConfig.Interfaces[fmt.Sprintf("eth%d", ethNumber)] = network
ethNumber = ethNumber + 1
}
metadata.NetworkConfig.DNS.Nameservers = m.DNS.Nameservers
metadata.Hostname = m.Hostname metadata.Hostname = m.Hostname
metadata.SSHPublicKeys = map[string]string{} metadata.SSHPublicKeys = map[string]string{}
for i, key := range m.PublicKeys { for i, key := range m.PublicKeys {
metadata.SSHPublicKeys[strconv.Itoa(i)] = key metadata.SSHPublicKeys[strconv.Itoa(i)] = key
} }
metadata.NetworkConfig = m
return return
} }

View File

@ -20,6 +20,8 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/rancher/os/netconf"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata"
"github.com/rancher/os/config/cloudinit/datasource/metadata/test" "github.com/rancher/os/config/cloudinit/datasource/metadata/test"
@ -90,26 +92,23 @@ func TestFetchMetadata(t *testing.T) {
"0": "publickey1", "0": "publickey1",
"1": "publickey2", "1": "publickey2",
}, },
NetworkConfig: Metadata{ NetworkConfig: netconf.NetworkConfig{
Interfaces: Interfaces{ Interfaces: map[string]netconf.InterfaceConfig{
Public: []Interface{ "eth0": netconf.InterfaceConfig{
{ Addresses: []string{
IPv4: &Address{ "192.168.1.2/24",
IPAddress: "192.168.1.2", "fe00::/126",
Netmask: "255.255.255.0", },
//Netmask: "255.255.255.0",
Gateway: "192.168.1.1", Gateway: "192.168.1.1",
},
IPv6: &Address{ //Cidr: 126,
IPAddress: "fe00::", GatewayIpv6: "fe00::",
Cidr: 126, //MAC: "ab:cd:ef:gh:ij",
Gateway: "fe00::", //Type: "public",
},
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() metadata, err := service.FetchMetadata()
if Error(err) != Error(tt.expectErr) { 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) { 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)
} }
} }
} }

82
config/cloudinit/datasource/metadata/ec2/metadata.go Normal file → Executable file
View File

@ -22,6 +22,8 @@ import (
"net" "net"
"strings" "strings"
"github.com/rancher/os/netconf"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata"
"github.com/rancher/os/config/cloudinit/pkg" "github.com/rancher/os/config/cloudinit/pkg"
@ -29,9 +31,9 @@ import (
const ( const (
DefaultAddress = "http://169.254.169.254/" DefaultAddress = "http://169.254.169.254/"
apiVersion = "2009-04-04/" apiVersion = "latest/"
userdataPath = apiVersion + "user-data" userdataPath = apiVersion + "user-data/"
metadataPath = apiVersion + "meta-data" metadataPath = apiVersion + "meta-data/"
) )
type MetadataService struct { type MetadataService struct {
@ -39,13 +41,23 @@ type MetadataService struct {
} }
func NewDatasource(root string) *MetadataService { func NewDatasource(root string) *MetadataService {
if root == "" {
root = DefaultAddress
}
return &MetadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, nil)} return &MetadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, nil)}
} }
func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) { func (ms MetadataService) AvailabilityChanges() bool {
metadata := datasource.Metadata{} // TODO: if it can't find the network, maybe we can start it?
return false
}
if keynames, err := ms.fetchAttributes(fmt.Sprintf("%s/public-keys", ms.MetadataURL())); err == nil { 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{}
if keynames, err := ms.fetchAttributes("public-keys"); err == nil {
keyIDs := make(map[string]string) keyIDs := make(map[string]string)
for _, keyname := range keynames { for _, keyname := range keynames {
tokens := strings.SplitN(keyname, "=", 2) tokens := strings.SplitN(keyname, "=", 2)
@ -57,7 +69,7 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) {
metadata.SSHPublicKeys = map[string]string{} metadata.SSHPublicKeys = map[string]string{}
for name, id := range keyIDs { 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 { if err != nil {
return metadata, err return metadata, err
} }
@ -68,24 +80,65 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) {
return metadata, err 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] metadata.Hostname = strings.Split(hostname, " ")[0]
} else if _, ok := err.(pkg.ErrNotFound); !ok { } else if _, ok := err.(pkg.ErrNotFound); !ok {
return metadata, err return metadata, err
} }
if localAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/local-ipv4", ms.MetadataURL())); err == nil { // 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) metadata.PrivateIPv4 = net.ParseIP(localAddr)
} else if _, ok := err.(pkg.ErrNotFound); !ok { } else if _, ok := err.(pkg.ErrNotFound); !ok {
return metadata, err return metadata, err
} }
if publicAddr, err := ms.fetchAttribute("public-ipv4"); err == nil {
if publicAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/public-ipv4", ms.MetadataURL())); err == nil {
metadata.PublicIPv4 = net.ParseIP(publicAddr) metadata.PublicIPv4 = net.ParseIP(publicAddr)
} else if _, ok := err.(pkg.ErrNotFound); !ok { } else if _, ok := err.(pkg.ErrNotFound); !ok {
return metadata, err return metadata, err
} }
metadata.NetworkConfig.Interfaces = make(map[string]netconf.InterfaceConfig)
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 return metadata, nil
} }
@ -93,7 +146,8 @@ func (ms MetadataService) Type() string {
return "ec2-metadata-service" 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) resp, err := ms.FetchData(url)
if err != nil { if err != nil {
return nil, err return nil, err
@ -106,8 +160,8 @@ func (ms MetadataService) fetchAttributes(url string) ([]string, error) {
return data, scanner.Err() return data, scanner.Err()
} }
func (ms MetadataService) fetchAttribute(url string) (string, error) { func (ms MetadataService) fetchAttribute(key string) (string, error) {
attrs, err := ms.fetchAttributes(url) attrs, err := ms.fetchAttributes(key)
if err == nil && len(attrs) > 0 { if err == nil && len(attrs) > 0 {
return attrs[0], nil return attrs[0], nil
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata"
"github.com/rancher/os/config/cloudinit/datasource/metadata/test" "github.com/rancher/os/config/cloudinit/datasource/metadata/test"
"github.com/rancher/os/config/cloudinit/pkg" "github.com/rancher/os/config/cloudinit/pkg"
"github.com/rancher/os/netconf"
) )
func TestType(t *testing.T) { func TestType(t *testing.T) {
@ -152,7 +153,7 @@ func TestFetchMetadata(t *testing.T) {
}{ }{
{ {
root: "/", root: "/",
metadataPath: "2009-04-04/meta-data", metadataPath: "2009-04-04/meta-data/",
resources: map[string]string{ resources: map[string]string{
"/2009-04-04/meta-data/public-keys": "bad\n", "/2009-04-04/meta-data/public-keys": "bad\n",
}, },
@ -160,7 +161,7 @@ func TestFetchMetadata(t *testing.T) {
}, },
{ {
root: "/", root: "/",
metadataPath: "2009-04-04/meta-data", metadataPath: "2009-04-04/meta-data/",
resources: map[string]string{ resources: map[string]string{
"/2009-04-04/meta-data/hostname": "host", "/2009-04-04/meta-data/hostname": "host",
"/2009-04-04/meta-data/local-ipv4": "1.2.3.4", "/2009-04-04/meta-data/local-ipv4": "1.2.3.4",
@ -174,24 +175,44 @@ func TestFetchMetadata(t *testing.T) {
PrivateIPv4: net.ParseIP("1.2.3.4"), PrivateIPv4: net.ParseIP("1.2.3.4"),
PublicIPv4: net.ParseIP("5.6.7.8"), PublicIPv4: net.ParseIP("5.6.7.8"),
SSHPublicKeys: map[string]string{"test1": "key"}, 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",
},
},
*/},
},
}, },
}, },
{ {
root: "/", root: "/",
metadataPath: "2009-04-04/meta-data", metadataPath: "2009-04-04/meta-data/",
resources: map[string]string{ resources: map[string]string{
"/2009-04-04/meta-data/hostname": "host domain another_domain", "/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/local-ipv4": "21.2.3.4",
"/2009-04-04/meta-data/public-ipv4": "5.6.7.8", "/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=test1\n",
"/2009-04-04/meta-data/public-keys/0": "openssh-key", "/2009-04-04/meta-data/public-keys/0": "openssh-key",
"/2009-04-04/meta-data/public-keys/0/openssh-key": "key", "/2009-04-04/meta-data/public-keys/0/openssh-key": "key",
}, },
expect: datasource.Metadata{ expect: datasource.Metadata{
Hostname: "host", Hostname: "host",
PrivateIPv4: net.ParseIP("1.2.3.4"), PrivateIPv4: net.ParseIP("21.2.3.4"),
PublicIPv4: net.ParseIP("5.6.7.8"), PublicIPv4: net.ParseIP("25.6.7.8"),
SSHPublicKeys: map[string]string{"test1": "key"}, 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",
},
},
*/},
},
}, },
}, },
{ {
@ -206,10 +227,10 @@ func TestFetchMetadata(t *testing.T) {
}} }}
metadata, err := service.FetchMetadata() metadata, err := service.FetchMetadata()
if Error(err) != Error(tt.expectErr) { 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) { 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)
} }
} }
} }

View File

@ -21,11 +21,14 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/rancher/os/netconf"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata"
) )
const ( const (
DefaultAddress = "http://metadata.google.internal/"
apiVersion = "computeMetadata/v1/" apiVersion = "computeMetadata/v1/"
metadataPath = apiVersion metadataPath = apiVersion
userdataPath = apiVersion + "instance/attributes/user-data" userdataPath = apiVersion + "instance/attributes/user-data"
@ -36,6 +39,9 @@ type MetadataService struct {
} }
func NewDatasource(root string) *MetadataService { func NewDatasource(root string) *MetadataService {
if root == "" {
root = DefaultAddress
}
return &MetadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, http.Header{"Metadata-Flavor": {"Google"}})} return &MetadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath, http.Header{"Metadata-Flavor": {"Google"}})}
} }
@ -68,6 +74,22 @@ func (ms MetadataService) FetchMetadata() (datasource.Metadata, error) {
SSHPublicKeys: nil, 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") keyStrings := strings.Split(projectSSHKeys+"\n"+instanceSSHKeys, "\n")
i := 0 i := 0

View File

@ -20,6 +20,8 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/rancher/os/netconf"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/datasource/metadata" "github.com/rancher/os/config/cloudinit/datasource/metadata"
"github.com/rancher/os/config/cloudinit/datasource/metadata/test" "github.com/rancher/os/config/cloudinit/datasource/metadata/test"
@ -73,6 +75,16 @@ func TestFetchMetadata(t *testing.T) {
Hostname: "host", Hostname: "host",
PrivateIPv4: net.ParseIP("1.2.3.4"), PrivateIPv4: net.ParseIP("1.2.3.4"),
PublicIPv4: net.ParseIP("5.6.7.8"), 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",
},
},
},
},
}, },
}, },
{ {

View File

@ -20,6 +20,7 @@ import (
"strings" "strings"
"github.com/rancher/os/config/cloudinit/pkg" "github.com/rancher/os/config/cloudinit/pkg"
"github.com/rancher/os/log"
) )
type Service struct { type Service struct {
@ -40,6 +41,9 @@ func NewDatasource(root, apiVersion, userdataPath, metadataPath string, header h
func (ms Service) IsAvailable() bool { func (ms Service) IsAvailable() bool {
_, ms.lastError = ms.Client.Get(ms.Root + ms.APIVersion) _, 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) return (ms.lastError == nil)
} }
@ -48,7 +52,7 @@ func (ms *Service) Finish() error {
} }
func (ms *Service) String() string { 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 { func (ms Service) AvailabilityChanges() bool {

104
config/cloudinit/datasource/metadata/packet/metadata.go Normal file → Executable file
View File

@ -15,12 +15,19 @@
package packet package packet
import ( import (
"encoding/json" "bytes"
"net" "fmt"
"net/http"
"strconv" "strconv"
"strings"
"github.com/rancher/os/config/cloudinit/datasource" "github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/datasource/metadata" "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 ( const (
@ -30,44 +37,68 @@ const (
metadataPath = "metadata" 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 { type MetadataService struct {
metadata.Service metadata.Service
} }
func NewDatasource(root string) *MetadataService { func NewDatasource(root string) *MetadataService {
if root == "" {
root = DefaultAddress
}
return &MetadataService{Service: metadata.NewDatasource(root, apiVersion, userdataURL, metadataPath, nil)} return &MetadataService{Service: metadata.NewDatasource(root, apiVersion, userdataURL, metadataPath, nil)}
} }
func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err error) { func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err error) {
var data []byte c := packetMetadata.NewClient(http.DefaultClient)
var m Metadata m, err := c.Metadata.Get()
if err != nil {
log.Errorf("Failed to get Packet metadata: %v", err)
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",
},
}
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 { if data, err = ms.FetchData(ms.MetadataURL()); err != nil || len(data) == 0 {
return return
@ -90,13 +121,20 @@ func (ms *MetadataService) FetchMetadata() (metadata datasource.Metadata, err er
} }
} }
} }
*/
metadata.Hostname = m.Hostname metadata.Hostname = m.Hostname
metadata.SSHPublicKeys = map[string]string{} metadata.SSHPublicKeys = map[string]string{}
for i, key := range m.SSHKeys { for i, key := range m.SshKeys {
metadata.SSHPublicKeys[strconv.Itoa(i)] = key metadata.SSHPublicKeys[strconv.Itoa(i)] = key
} }
metadata.NetworkConfig = m.NetworkData 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 return
} }

View File

@ -125,7 +125,7 @@ func (v VMWare) FetchMetadata() (metadata datasource.Metadata, err error) {
} }
} }
} }
metadata.NetworkConfig = netconf // metadata.NetworkConfig = netconf
return return
} }

View File

@ -56,10 +56,10 @@ func TestFetchMetadata(t *testing.T) {
"interface.0.dhcp": "yes", "interface.0.dhcp": "yes",
}, },
metadata: datasource.Metadata{ metadata: datasource.Metadata{
NetworkConfig: map[string]string{ // NetworkConfig: map[string]string{
"interface.0.mac": "test mac", // "interface.0.mac": "test mac",
"interface.0.dhcp": "yes", // "interface.0.dhcp": "yes",
}, // },
}, },
}, },
{ {
@ -68,10 +68,10 @@ func TestFetchMetadata(t *testing.T) {
"interface.0.dhcp": "yes", "interface.0.dhcp": "yes",
}, },
metadata: datasource.Metadata{ metadata: datasource.Metadata{
NetworkConfig: map[string]string{ // NetworkConfig: map[string]string{
"interface.0.name": "test name", // "interface.0.name": "test name",
"interface.0.dhcp": "yes", // "interface.0.dhcp": "yes",
}, // },
}, },
}, },
{ {
@ -86,12 +86,12 @@ func TestFetchMetadata(t *testing.T) {
metadata: datasource.Metadata{ metadata: datasource.Metadata{
Hostname: "test host", Hostname: "test host",
PrivateIPv6: net.ParseIP("fe00::100"), PrivateIPv6: net.ParseIP("fe00::100"),
NetworkConfig: map[string]string{ // NetworkConfig: map[string]string{
"interface.0.mac": "test mac", // "interface.0.mac": "test mac",
"interface.0.ip.0.address": "fe00::100/64", // "interface.0.ip.0.address": "fe00::100/64",
"interface.0.route.0.gateway": "fe00::1", // "interface.0.route.0.gateway": "fe00::1",
"interface.0.route.0.destination": "::", // "interface.0.route.0.destination": "::",
}, // },
}, },
}, },
{ {
@ -113,17 +113,17 @@ func TestFetchMetadata(t *testing.T) {
Hostname: "test host", Hostname: "test host",
PublicIPv4: net.ParseIP("10.0.0.101"), PublicIPv4: net.ParseIP("10.0.0.101"),
PrivateIPv4: net.ParseIP("10.0.0.102"), PrivateIPv4: net.ParseIP("10.0.0.102"),
NetworkConfig: map[string]string{ // NetworkConfig: map[string]string{
"interface.0.name": "test name", // "interface.0.name": "test name",
"interface.0.ip.0.address": "10.0.0.100/24", // "interface.0.ip.0.address": "10.0.0.100/24",
"interface.0.ip.1.address": "10.0.0.101/24", // "interface.0.ip.1.address": "10.0.0.101/24",
"interface.0.route.0.gateway": "10.0.0.1", // "interface.0.route.0.gateway": "10.0.0.1",
"interface.0.route.0.destination": "0.0.0.0", // "interface.0.route.0.destination": "0.0.0.0",
"interface.1.mac": "test mac", // "interface.1.mac": "test mac",
"interface.1.route.0.gateway": "10.0.0.2", // "interface.1.route.0.gateway": "10.0.0.2",
"interface.1.route.0.destination": "0.0.0.0", // "interface.1.route.0.destination": "0.0.0.0",
"interface.1.ip.0.address": "10.0.0.102/24", // "interface.1.ip.0.address": "10.0.0.102/24",
}, // },
}, },
}, },
} }
@ -257,17 +257,17 @@ func TestOvfTransport(t *testing.T) {
Hostname: "test host", Hostname: "test host",
PublicIPv4: net.ParseIP("10.0.0.101"), PublicIPv4: net.ParseIP("10.0.0.101"),
PrivateIPv4: net.ParseIP("10.0.0.102"), PrivateIPv4: net.ParseIP("10.0.0.102"),
NetworkConfig: map[string]string{ //NetworkConfig: map[string]string{
"interface.0.name": "test name", // "interface.0.name": "test name",
"interface.0.ip.0.address": "10.0.0.100/24", // "interface.0.ip.0.address": "10.0.0.100/24",
"interface.0.ip.1.address": "10.0.0.101/24", // "interface.0.ip.1.address": "10.0.0.101/24",
"interface.0.route.0.gateway": "10.0.0.1", // "interface.0.route.0.gateway": "10.0.0.1",
"interface.0.route.0.destination": "0.0.0.0", // "interface.0.route.0.destination": "0.0.0.0",
"interface.1.mac": "test mac", // "interface.1.mac": "test mac",
"interface.1.route.0.gateway": "10.0.0.2", // "interface.1.route.0.gateway": "10.0.0.2",
"interface.1.route.0.destination": "0.0.0.0", // "interface.1.route.0.destination": "0.0.0.0",
"interface.1.ip.0.address": "10.0.0.102/24", // "interface.1.ip.0.address": "10.0.0.102/24",
}, // },
}, },
userdata: []byte("test config"), userdata: []byte("test config"),
}, },

27
config/cloudinit/network/packet.go Normal file → Executable file
View File

@ -17,14 +17,15 @@ package network
import ( import (
"net" "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 var nameservers []net.IP
if netdata.DNS != nil { for _, v := range netdata.DNS.Nameservers {
nameservers = netdata.DNS nameservers = append(nameservers, net.ParseIP(v))
} else { }
if len(nameservers) == 0 {
nameservers = append(nameservers, net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")) nameservers = append(nameservers, net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4"))
} }
@ -36,11 +37,12 @@ func ProcessPacketNetconf(netdata packet.NetworkData) ([]InterfaceGenerator, err
return generators, nil 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 interfaces []InterfaceGenerator
var addresses []net.IPNet var addresses []net.IPNet
var routes []route var routes []route
for _, netblock := range netdata.Netblocks { // 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{ addresses = append(addresses, net.IPNet{
IP: netblock.Address, IP: netblock.Address,
Mask: net.IPMask(netblock.Netmask), Mask: net.IPMask(netblock.Netmask),
@ -73,6 +75,7 @@ func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]Interface
} }
} }
} }
*/
bond := bondInterface{ bond := bondInterface{
logicalInterface: logicalInterface{ logicalInterface: logicalInterface{
@ -92,14 +95,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 { index := 0
bond.slaves = append(bond.slaves, iface.Name) for name := range netdata.Interfaces {
bond.slaves = append(bond.slaves, name)
interfaces = append(interfaces, &physicalInterface{ interfaces = append(interfaces, &physicalInterface{
logicalInterface: logicalInterface{ logicalInterface: logicalInterface{
name: iface.Name, name: name,
config: configMethodStatic{ config: configMethodStatic{
nameservers: nameservers, nameservers: nameservers,
}, },
@ -107,6 +111,7 @@ func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]Interface
configDepth: index, configDepth: index,
}, },
}) })
index = index + 1
} }
interfaces = append(interfaces, &bond) interfaces = append(interfaces, &bond)

View File

@ -317,6 +317,7 @@ func readConfigFile(file string) ([]byte, error) {
} }
func substituteVars(userDataBytes []byte, metadata datasource.Metadata) []byte { func substituteVars(userDataBytes []byte, metadata datasource.Metadata) []byte {
// TODO: I think this currently does nothing - its hardcoded for COREOS env..
env := initialize.NewEnvironment("", "", "", "", metadata) env := initialize.NewEnvironment("", "", "", "", metadata)
userData := env.Apply(string(userDataBytes)) userData := env.Apply(string(userDataBytes))

40
config/types.go Normal file → Executable file
View File

@ -8,6 +8,7 @@ import (
composeConfig "github.com/docker/libcompose/config" composeConfig "github.com/docker/libcompose/config"
"github.com/rancher/os/config/cloudinit/config" "github.com/rancher/os/config/cloudinit/config"
"github.com/rancher/os/config/yaml" "github.com/rancher/os/config/yaml"
"github.com/rancher/os/netconf"
) )
const ( const (
@ -113,8 +114,8 @@ type RancherConfig struct {
Disable []string `yaml:"disable,omitempty"` Disable []string `yaml:"disable,omitempty"`
ServicesInclude map[string]bool `yaml:"services_include,omitempty"` ServicesInclude map[string]bool `yaml:"services_include,omitempty"`
Modules []string `yaml:"modules,omitempty"` Modules []string `yaml:"modules,omitempty"`
Network NetworkConfig `yaml:"network,omitempty"` Network netconf.NetworkConfig `yaml:"network,omitempty"`
DefaultNetwork NetworkConfig `yaml:"default_network,omitempty"` DefaultNetwork netconf.NetworkConfig `yaml:"default_network,omitempty"`
Repositories Repositories `yaml:"repositories,omitempty"` Repositories Repositories `yaml:"repositories,omitempty"`
SSH SSHConfig `yaml:"ssh,omitempty"` SSH SSHConfig `yaml:"ssh,omitempty"`
State StateConfig `yaml:"state,omitempty"` State StateConfig `yaml:"state,omitempty"`
@ -170,39 +171,6 @@ type DockerConfig struct {
Exec bool `yaml:"exec,omitempty"` 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 { type SSHConfig struct {
Keys map[string]string `yaml:"keys,omitempty"` Keys map[string]string `yaml:"keys,omitempty"`
} }
@ -227,7 +195,7 @@ type CloudInit struct {
type Defaults struct { type Defaults struct {
Hostname string `yaml:"hostname,omitempty"` Hostname string `yaml:"hostname,omitempty"`
Docker DockerConfig `yaml:"docker,omitempty"` Docker DockerConfig `yaml:"docker,omitempty"`
Network NetworkConfig `yaml:"network,omitempty"` Network netconf.NetworkConfig `yaml:"network,omitempty"`
} }
func (r Repositories) ToArray() []string { func (r Repositories) ToArray() []string {

View File

@ -12,7 +12,6 @@ import (
"syscall" "syscall"
"github.com/docker/libnetwork/resolvconf" "github.com/docker/libnetwork/resolvconf"
"github.com/rancher/os/config"
"github.com/rancher/os/log" "github.com/rancher/os/log"
"github.com/rancher/os/netconf" "github.com/rancher/os/netconf"
"github.com/rancher/os/selinux" "github.com/rancher/os/selinux"
@ -46,7 +45,7 @@ type Config struct {
Fork bool Fork bool
PidOne bool PidOne bool
CommandName string CommandName string
DNSConfig config.DNSConfig DNSConfig netconf.DNSConfig
BridgeName string BridgeName string
BridgeAddress string BridgeAddress string
BridgeMtu int BridgeMtu int
@ -359,8 +358,8 @@ ff02::2 ip6-allrouters
if cfg.BridgeName != "" && cfg.BridgeName != "none" { if cfg.BridgeName != "" && cfg.BridgeName != "none" {
log.Debugf("Creating bridge %s (%s)", cfg.BridgeName, cfg.BridgeAddress) log.Debugf("Creating bridge %s (%s)", cfg.BridgeName, cfg.BridgeAddress)
if err := netconf.ApplyNetworkConfigs(&config.NetworkConfig{ if err := netconf.ApplyNetworkConfigs(&netconf.NetworkConfig{
Interfaces: map[string]config.InterfaceConfig{ Interfaces: map[string]netconf.InterfaceConfig{
cfg.BridgeName: { cfg.BridgeName: {
Address: cfg.BridgeAddress, Address: cfg.BridgeAddress,
MTU: cfg.BridgeMtu, MTU: cfg.BridgeMtu,

View File

@ -8,6 +8,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings" "strings"
"syscall" "syscall"
@ -219,8 +220,9 @@ func RunInit() error {
boot2DockerEnvironment := false boot2DockerEnvironment := false
var shouldSwitchRoot bool var shouldSwitchRoot bool
var cloudConfigBootFile []byte
var metadataFile []byte configFiles := make(map[string][]byte)
initFuncs := []config.CfgFunc{ initFuncs := []config.CfgFunc{
func(c *config.CloudConfig) (*config.CloudConfig, error) { func(c *config.CloudConfig) (*config.CloudConfig, error) {
return c, dfs.PrepareFs(&mountConfig) return c, dfs.PrepareFs(&mountConfig)
@ -300,14 +302,18 @@ func RunInit() error {
return cfg, nil return cfg, nil
}, },
func(cfg *config.CloudConfig) (*config.CloudConfig, error) { func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
var err error filesToCopy := []string{
cloudConfigBootFile, err = ioutil.ReadFile(config.CloudConfigBootFile) config.CloudConfigBootFile,
if err != nil { config.CloudConfigNetworkFile,
log.Error(err) config.MetaDataFile,
} }
metadataFile, err = ioutil.ReadFile(config.MetaDataFile) for _, name := range filesToCopy {
content, err := ioutil.ReadFile(name)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
continue
}
configFiles[name] = content
} }
return cfg, nil return cfg, nil
}, },
@ -323,14 +329,13 @@ func RunInit() error {
}, },
mountOem, mountOem,
func(cfg *config.CloudConfig) (*config.CloudConfig, error) { func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
if err := os.MkdirAll(config.CloudConfigDir, os.ModeDir|0755); err != nil { for name, content := range configFiles {
if err := os.MkdirAll(filepath.Dir(name), os.ModeDir|0755); err != nil {
log.Error(err) log.Error(err)
} }
if err := util.WriteFileAtomic(config.CloudConfigBootFile, cloudConfigBootFile, 400); err != nil { if err := util.WriteFileAtomic(name, content, 400); err != nil {
log.Error(err) log.Error(err)
} }
if err := util.WriteFileAtomic(config.MetaDataFile, metadataFile, 400); err != nil {
log.Error(err)
} }
return cfg, nil return cfg, nil
}, },

36
netconf/ipv4ll_linux.go Normal file → Executable file
View File

@ -10,19 +10,16 @@ import (
"github.com/j-keck/arping" "github.com/j-keck/arping"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl"
) )
func AssignLinkLocalIP(link netlink.Link) error { func AssignLinkLocalIP(link netlink.Link) error {
ifaceName := link.Attrs().Name ifaceName := link.Attrs().Name
iface, err := net.InterfaceByName(ifaceName)
addrs, err := getLinkAddrs(link)
if err != nil { if err != nil {
log.Error("could not get information about interface")
return err return err
} }
addrs, err := iface.Addrs()
if err != nil {
log.Error("Error fetching existing ip on interface")
}
for _, addr := range addrs { for _, addr := range addrs {
if addr.String()[:7] == "169.254" { if addr.String()[:7] == "169.254" {
log.Info("Link Local IP already set on interface") 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") 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 { func getNewIPV4LLAddr(randomNum uint32) net.IP {
byte1 := randomNum & 255 // use least significant 8 bits byte1 := randomNum & 255 // use least significant 8 bits
byte2 := randomNum >> 24 // use most 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) src := rand.NewSource(seed)
return &src, nil 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
}

98
netconf/netconf_linux.go Normal file → Executable file
View File

@ -10,11 +10,10 @@ import (
"sync" "sync"
"syscall" "syscall"
"github.com/flynn/go-shlex" shlex "github.com/flynn/go-shlex"
"github.com/rancher/os/log" "github.com/rancher/os/log"
"github.com/rancher/os/config" glob "github.com/ryanuber/go-glob"
"github.com/ryanuber/go-glob"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
@ -27,7 +26,7 @@ var (
defaultDhcpArgs = []string{"dhcpcd", "-MA4"} defaultDhcpArgs = []string{"dhcpcd", "-MA4"}
) )
func createInterfaces(netCfg *config.NetworkConfig) { func createInterfaces(netCfg *NetworkConfig) {
configured := map[string]bool{} configured := map[string]bool{}
for name, iface := range netCfg.Interfaces { 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() links, err := netlink.LinkList()
if err != nil { if err != nil {
log.Errorf("Failed to list links: %v", err) 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 linkName := link.Attrs().Name
var match config.InterfaceConfig var match InterfaceConfig
exactMatch := false exactMatch := false
found := false found := false
@ -136,25 +135,28 @@ func findMatch(link netlink.Link, netCfg *config.NetworkConfig) (config.Interfac
return match, exactMatch || found return match, exactMatch || found
} }
func populateDefault(netCfg *config.NetworkConfig) { func populateDefault(netCfg *NetworkConfig) {
if netCfg.Interfaces == nil { if netCfg.Interfaces == nil {
netCfg.Interfaces = map[string]config.InterfaceConfig{} netCfg.Interfaces = map[string]InterfaceConfig{}
} }
if len(netCfg.Interfaces) == 0 { if len(netCfg.Interfaces) == 0 {
netCfg.Interfaces["eth*"] = config.InterfaceConfig{ netCfg.Interfaces["eth*"] = InterfaceConfig{
DHCP: true, DHCP: true,
} }
} }
if _, ok := netCfg.Interfaces["lo"]; !ok { if _, ok := netCfg.Interfaces["lo"]; !ok {
netCfg.Interfaces["lo"] = config.InterfaceConfig{ netCfg.Interfaces["lo"] = InterfaceConfig{
Address: "127.0.0.1/8", Addresses: []string{
"127.0.0.1/8",
"::1/128",
},
} }
} }
} }
func ApplyNetworkConfigs(netCfg *config.NetworkConfig) error { func ApplyNetworkConfigs(netCfg *NetworkConfig) error {
populateDefault(netCfg) populateDefault(netCfg)
log.Debugf("Config: %#v", netCfg) log.Debugf("Config: %#v", netCfg)
@ -183,7 +185,7 @@ func ApplyNetworkConfigs(netCfg *config.NetworkConfig) error {
return err return err
} }
func RunDhcp(netCfg *config.NetworkConfig, setHostname, setDNS bool) error { func RunDhcp(netCfg *NetworkConfig, setHostname, setDNS bool) error {
populateDefault(netCfg) populateDefault(netCfg)
links, err := netlink.LinkList() links, err := netlink.LinkList()
@ -212,7 +214,7 @@ func RunDhcp(netCfg *config.NetworkConfig, setHostname, setDNS bool) error {
return err 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) log.Infof("Running DHCP on %s", iface)
args := []string{} args := []string{}
if argstr != "" { if argstr != "" {
@ -243,7 +245,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 { if err := netlink.LinkSetUp(link); err != nil {
log.Errorf("failed to setup link: %v", err) log.Errorf("failed to setup link: %v", err)
return err return err
@ -252,7 +254,7 @@ func linkUp(link netlink.Link, netConf config.InterfaceConfig) error {
return nil 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) addr, err := netlink.ParseAddr(address)
if err != nil { if err != nil {
return err return err
@ -268,7 +270,21 @@ func applyAddress(address string, link netlink.Link, netConf config.InterfaceCon
return nil return nil
} }
func setGateway(gateway string) error { 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(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 == "" { if gateway == "" {
return nil return nil
} }
@ -283,18 +299,33 @@ func setGateway(gateway string) error {
Gw: gatewayIP, Gw: gatewayIP,
} }
if add {
if err := netlink.RouteAdd(&route); err == syscall.EEXIST { if err := netlink.RouteAdd(&route); err == syscall.EEXIST {
//Ignore this error //Ignore this error
} else if err != nil { } else if err != nil {
log.Errorf("gateway set failed: %v", err) log.Errorf("gateway add failed: %v", err)
return err return err
} }
log.Infof("Added default gateway %s", gateway)
} 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("Replaced default gateway %s", gateway)
}
log.Infof("Set default gateway %s", gateway)
return nil return nil
} }
func applyInterfaceConfig(link netlink.Link, netConf config.InterfaceConfig) 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 netConf.Bond != "" {
if err := netlink.LinkSetDown(link); err != nil { if err := netlink.LinkSetDown(link); err != nil {
return err return err
@ -310,6 +341,7 @@ func applyInterfaceConfig(link netlink.Link, netConf config.InterfaceConfig) err
return nil return nil
} }
//TODO: undo
if netConf.Bridge != "" && netConf.Bridge != "true" { if netConf.Bridge != "" && netConf.Bridge != "true" {
b, err := NewBridge(netConf.Bridge) b, err := NewBridge(netConf.Bridge)
if err != nil { if err != nil {
@ -327,6 +359,12 @@ func applyInterfaceConfig(link netlink.Link, netConf config.InterfaceConfig) err
return err return err
} }
} else { } else {
if err := RemoveLinkLocalIP(link); err != nil {
log.Errorf("IPV4LL del failed: %v", err)
return err
}
}
addresses := []string{} addresses := []string{}
if netConf.Address != "" { if netConf.Address != "" {
@ -337,14 +375,24 @@ func applyInterfaceConfig(link netlink.Link, netConf config.InterfaceConfig) err
addresses = append(addresses, netConf.Addresses...) addresses = append(addresses, netConf.Addresses...)
} }
existingAddrs, _ := getLinkAddrs(link)
addrMap := make(map[string]bool)
for _, address := range addresses { for _, address := range addresses {
addrMap[address] = true
log.Infof("Applying %s to %s", address, link.Attrs().Name)
err := applyAddress(address, link, netConf) err := applyAddress(address, link, netConf)
if err != nil { if err != nil {
log.Errorf("Failed to apply address %s to %s: %v", address, link.Attrs().Name, err) 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("leaving %s from %s", addr.String(), link.Attrs().Name)
//removeAddress(addr, link)
}
} }
// TODO: can we set to default?
if netConf.MTU > 0 { if netConf.MTU > 0 {
if err := netlink.LinkSetMTU(link, netConf.MTU); err != nil { if err := netlink.LinkSetMTU(link, netConf.MTU); err != nil {
log.Errorf("set MTU Failed: %v", err) log.Errorf("set MTU Failed: %v", err)
@ -358,14 +406,16 @@ func applyInterfaceConfig(link netlink.Link, netConf config.InterfaceConfig) err
return err return err
} }
if err := setGateway(netConf.Gateway); err != nil { // replace the existing gw with the main ipv4 one
if err := setGateway(netConf.Gateway, true); err != nil {
log.Errorf("Fail to set gateway %s", netConf.Gateway) log.Errorf("Fail to set gateway %s", netConf.Gateway)
} }
//and then add the ipv6 one if it exists
if err := setGateway(netConf.GatewayIpv6); err != nil { if err := setGateway(netConf.GatewayIpv6, true); err != nil {
log.Errorf("Fail to set gateway %s", netConf.GatewayIpv6) log.Errorf("Fail to set gateway %s", netConf.GatewayIpv6)
} }
// 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) runCmds(netConf.PostUp, link.Attrs().Name)
return nil return nil

34
netconf/types.go Executable file
View File

@ -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"`
}

29
scripts/ros Executable file
View File

@ -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

View File

@ -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

54
tests/network_test.go Normal file → Executable file
View File

@ -35,3 +35,57 @@ cat /etc/resolv.conf | grep "nameserver 208.67.220.123"
SCRIPT SCRIPT
sudo bash test-merge`) 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: <LOOPBACK,UP,LOWER_UP> 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <NO-CARRIER,BROADCAST,MULTICAST,UP> 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: <NO-CARRIER,BROADCAST,MULTICAST,UP> 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")
}

View File

@ -48,7 +48,8 @@ github.com/stretchr/testify a1f97990ddc16022ec7610326dd9bce31332c116
github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852
github.com/tchap/go-patricia v2.1.0 github.com/tchap/go-patricia v2.1.0
github.com/vbatts/tar-split v0.9.11 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/gojsonpointer e0fe6f68307607d540ed8eac07a342c33fa1b54a
github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45 github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45
github.com/xeipuuv/gojsonschema ac452913faa25c08bb78810d3e6f88b8a39f8f25 github.com/xeipuuv/gojsonschema ac452913faa25c08bb78810d3e6f88b8a39f8f25

View File

@ -11,17 +11,17 @@ goroot = $(addprefix ../../../,$(1))
unroot = $(subst ../../../,,$(1)) unroot = $(subst ../../../,,$(1))
fmt = $(addprefix fmt-,$(1)) fmt = $(addprefix fmt-,$(1))
all: fmt test all: test
$(call goroot,$(DEPS)): $(call goroot,$(DEPS)):
go get $(call unroot,$@) go get $(call unroot,$@)
.PHONY: $(call testdirs,$(DIRS)) .PHONY: $(call testdirs,$(DIRS))
$(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))): $(call fmt,$(call testdirs,$(DIRS))):
! gofmt -l $(subst fmt-,,$@)/*.go | grep '' ! gofmt -l $(subst fmt-,,$@)/*.go | grep -q .
.PHONY: fmt .PHONY: fmt
fmt: $(call fmt,$(call testdirs,$(DIRS))) fmt: $(call fmt,$(call testdirs,$(DIRS)))

View File

@ -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 and routes, and configure ipsec. Netlink communication requires elevated
privileges, so in most cases this code needs to be run as root. Since 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 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 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 function like AddLink(). This library began its life as a fork of the
netlink functionality in netlink functionality in

View File

@ -13,6 +13,8 @@ type Addr struct {
Label string Label string
Flags int Flags int
Scope int Scope int
Peer *net.IPNet
Broadcast net.IP
} }
// String returns $ip/$netmask $label // String returns $ip/$netmask $label
@ -43,3 +45,10 @@ func (a Addr) Equal(x Addr) bool {
// ignore label for comparison // ignore label for comparison
return a.IP.Equal(x.IP) && sizea == sizeb 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
}

View File

@ -2,11 +2,13 @@ package netlink
import ( import (
"fmt" "fmt"
"log"
"net" "net"
"strings" "strings"
"syscall" "syscall"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
) )
// IFA_FLAGS is a u32 attribute. // IFA_FLAGS is a u32 attribute.
@ -15,24 +17,35 @@ const IFA_FLAGS = 0x8
// AddrAdd will add an IP address to a link device. // AddrAdd will add an IP address to a link device.
// Equivalent to: `ip addr add $addr dev $link` // Equivalent to: `ip addr add $addr dev $link`
func AddrAdd(link Link, addr *Addr) error { 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) // AddrAdd will add an IP address to a link device.
return addrHandle(link, addr, req) // 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. // AddrDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link` // Equivalent to: `ip addr del $addr dev $link`
func AddrDel(link Link, addr *Addr) error { func AddrDel(link Link, addr *Addr) error {
req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK) return pkgHandle.AddrDel(link, addr)
return addrHandle(link, addr, req)
} }
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() base := link.Attrs()
if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
return fmt.Errorf("label must begin with interface name") return fmt.Errorf("label must begin with interface name")
} }
ensureIndex(base) h.ensureIndex(base)
family := nl.GetIPFamily(addr.IP) family := nl.GetIPFamily(addr.IP)
@ -43,25 +56,43 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
msg.Prefixlen = uint8(prefixlen) msg.Prefixlen = uint8(prefixlen)
req.AddData(msg) req.AddData(msg)
var addrData []byte var localAddrData []byte
if family == FAMILY_V4 { if family == FAMILY_V4 {
addrData = addr.IP.To4() localAddrData = addr.IP.To4()
} else { } 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) 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) req.AddData(addressData)
if addr.Flags != 0 { if addr.Flags != 0 {
if addr.Flags <= 0xff {
msg.IfAddrmsg.Flags = uint8(addr.Flags)
} else {
b := make([]byte, 4) b := make([]byte, 4)
native.PutUint32(b, uint32(addr.Flags)) native.PutUint32(b, uint32(addr.Flags))
flagsData := nl.NewRtAttr(IFA_FLAGS, b) flagsData := nl.NewRtAttr(IFA_FLAGS, b)
req.AddData(flagsData) req.AddData(flagsData)
} }
}
if addr.Broadcast != nil {
req.AddData(nl.NewRtAttr(syscall.IFA_BROADCAST, addr.Broadcast))
}
if addr.Label != "" { if addr.Label != "" {
labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label)) labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
@ -76,7 +107,14 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
// Equivalent to: `ip addr show`. // Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family. // The list can be filtered by link and ip family.
func AddrList(link Link, family int) ([]Addr, error) { 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) msg := nl.NewIfInfomsg(family)
req.AddData(msg) req.AddData(msg)
@ -85,33 +123,51 @@ func AddrList(link Link, family int) ([]Addr, error) {
return nil, err return nil, err
} }
index := 0 indexFilter := 0
if link != nil { if link != nil {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
index = base.Index indexFilter = base.Index
} }
var res []Addr var res []Addr
for _, m := range msgs { 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 // Ignore messages from other interfaces
continue continue
} }
if family != FAMILY_ALL && msg.Family != uint8(family) { if family != FAMILY_ALL && msgFamily != family {
continue continue
} }
attrs, err := nl.ParseRouteAttr(m[msg.Len():]) res = append(res, addr)
if err != nil {
return nil, err
} }
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 var local, dst *net.IPNet
var addr Addr
for _, attr := range attrs { for _, attr := range attrs {
switch attr.Attr.Type { switch attr.Attr.Type {
case syscall.IFA_ADDRESS: case syscall.IFA_ADDRESS:
@ -119,11 +175,13 @@ func AddrList(link Link, family int) ([]Addr, error) {
IP: attr.Value, IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
} }
addr.Peer = dst
case syscall.IFA_LOCAL: case syscall.IFA_LOCAL:
local = &net.IPNet{ local = &net.IPNet{
IP: attr.Value, IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
} }
addr.IPNet = local
case syscall.IFA_LABEL: case syscall.IFA_LABEL:
addr.Label = string(attr.Value[:len(attr.Value)-1]) addr.Label = string(attr.Value[:len(attr.Value)-1])
case IFA_FLAGS: case IFA_FLAGS:
@ -139,8 +197,63 @@ func AddrList(link Link, family int) ([]Addr, error) {
} }
addr.Scope = int(msg.Scope) addr.Scope = int(msg.Scope)
res = append(res, addr) return
} }
return res, nil 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
} }

62
vendor/github.com/vishvananda/netlink/bpf_linux.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
package netlink
/*
#include <asm/types.h>
#include <asm/unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
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
}

View File

@ -9,7 +9,7 @@ type Class interface {
Type() string 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 // has a handle and a parent. The root filter of a device should have a
// parent == HANDLE_ROOT. // parent == HANDLE_ROOT.
type ClassAttrs struct { type ClassAttrs struct {
@ -20,7 +20,7 @@ type ClassAttrs struct {
} }
func (q ClassAttrs) String() string { 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 { 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) 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 { type HtbClass struct {
ClassAttrs ClassAttrs
Rate uint64 Rate uint64
@ -50,48 +50,15 @@ type HtbClass struct {
Prio uint32 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 { func (q HtbClass) String() string {
return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer) return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer)
} }
func (class *HtbClass) Attrs() *ClassAttrs { func (q *HtbClass) Attrs() *ClassAttrs {
return &class.ClassAttrs return &q.ClassAttrs
} }
func (class *HtbClass) Type() string { func (q *HtbClass) Type() string {
return "htb" return "htb"
} }

View File

@ -7,18 +7,64 @@ import (
"github.com/vishvananda/netlink/nl" "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. // ClassDel will delete a class from the system.
// Equivalent to: `tc class del $class` // Equivalent to: `tc class del $class`
func ClassDel(class Class) error { 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 // ClassChange will change a class in place
// Equivalent to: `tc class change $class` // Equivalent to: `tc class change $class`
// The parent and handle MUST NOT be changed. // The parent and handle MUST NOT be changed.
func ClassChange(class Class) error { 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. // 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 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. // If a class does not already exist with this parent/handle, a new class is created.
func ClassReplace(class Class) error { 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. // ClassAdd will add a class to the system.
// Equivalent to: `tc class add $class` // Equivalent to: `tc class add $class`
func ClassAdd(class Class) error { 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.RTM_NEWTCLASS,
syscall.NLM_F_CREATE|syscall.NLM_F_EXCL, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
class, class,
) )
} }
func classModify(cmd, flags int, class Class) error { func (h *Handle) classModify(cmd, flags int, class Class) error {
req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK) req := h.newNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
base := class.Attrs() base := class.Attrs()
msg := &nl.TcMsg{ msg := &nl.TcMsg{
Family: nl.FAMILY_ALL, Family: nl.FAMILY_ALL,
@ -73,20 +134,20 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {
opt.Prio = htb.Prio opt.Prio = htb.Prio
// TODO: Handle Debug properly. For now default to 0 // TODO: Handle Debug properly. For now default to 0
/* Calculate {R,C}Tab and set Rate and Ceil */ /* Calculate {R,C}Tab and set Rate and Ceil */
cell_log := -1 cellLog := -1
ccell_log := -1 ccellLog := -1
linklayer := nl.LINKLAYER_ETHERNET linklayer := nl.LINKLAYER_ETHERNET
mtu := 1600 mtu := 1600
var rtab [256]uint32 var rtab [256]uint32
var ctab [256]uint32 var ctab [256]uint32
tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)} tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
if CalcRtable(&tcrate, rtab, cell_log, uint32(mtu), linklayer) < 0 { if CalcRtable(&tcrate, rtab, cellLog, uint32(mtu), linklayer) < 0 {
return errors.New("HTB: failed to calculate rate table.") return errors.New("HTB: failed to calculate rate table")
} }
opt.Rate = tcrate opt.Rate = tcrate
tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)} tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
if CalcRtable(&tcceil, ctab, ccell_log, uint32(mtu), linklayer) < 0 { if CalcRtable(&tcceil, ctab, ccellLog, uint32(mtu), linklayer) < 0 {
return errors.New("HTB: failed to calculate ceil rate table.") return errors.New("HTB: failed to calculate ceil rate table")
} }
opt.Ceil = tcceil opt.Ceil = tcceil
nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize()) 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`. // Equivalent to: `tc class show`.
// Generally returns nothing if link and parent are not specified. // Generally returns nothing if link and parent are not specified.
func ClassList(link Link, parent uint32) ([]Class, error) { 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{ msg := &nl.TcMsg{
Family: nl.FAMILY_ALL, Family: nl.FAMILY_ALL,
Parent: parent, Parent: parent,
} }
if link != nil { if link != nil {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
msg.Ifindex = int32(base.Index) msg.Ifindex = int32(base.Index)
} }
req.AddData(msg) req.AddData(msg)

View File

@ -1,17 +1,13 @@
package netlink package netlink
import ( import "fmt"
"errors"
"fmt"
"github.com/vishvananda/netlink/nl"
)
type Filter interface { type Filter interface {
Attrs() *FilterAttrs Attrs() *FilterAttrs
Type() string 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 // has a handle and a parent. The root filter of a device should have a
// parent == HANDLE_ROOT. // parent == HANDLE_ROOT.
type FilterAttrs struct { 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) 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 // U32 filters on many packet related properties
type U32 struct { type U32 struct {
FilterAttrs FilterAttrs
// Currently only supports redirecting to another interface ClassId uint32
RedirIndex int RedirIndex int
Actions []Action
} }
func (filter *U32) Attrs() *FilterAttrs { func (filter *U32) Attrs() *FilterAttrs {
@ -52,78 +207,27 @@ type FilterFwAttrs struct {
Rate uint32 Rate uint32
AvRate uint32 AvRate uint32
PeakRate uint32 PeakRate uint32
Action int Action TcPolAct
Overhead uint16 Overhead uint16
LinkLayer int LinkLayer int
} }
// FwFilter filters on firewall marks type BpfFilter struct {
type Fw struct {
FilterAttrs FilterAttrs
ClassId uint32 ClassId uint32
Police nl.TcPolice Fd int
InDev string Name string
// TODO Action DirectAction bool
Mask uint32
AvRate uint32
Rtab [256]uint32
Ptab [256]uint32
} }
func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) { func (filter *BpfFilter) Type() string {
var rtab [256]uint32 return "bpf"
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) func (filter *BpfFilter) Attrs() *FilterAttrs {
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 *Fw) Attrs() *FilterAttrs {
return &filter.FilterAttrs return &filter.FilterAttrs
} }
func (filter *Fw) Type() string {
return "fw"
}
// GenericFilter filters represent types that are not currently understood // GenericFilter filters represent types that are not currently understood
// by this netlink library. // by this netlink library.
type GenericFilter struct { type GenericFilter struct {

View File

@ -3,16 +3,93 @@ package netlink
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"syscall" "syscall"
"github.com/vishvananda/netlink/nl" "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. // FilterDel will delete a filter from the system.
// Equivalent to: `tc filter del $filter` // Equivalent to: `tc filter del $filter`
func FilterDel(filter Filter) error { 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() base := filter.Attrs()
msg := &nl.TcMsg{ msg := &nl.TcMsg{
Family: nl.FAMILY_ALL, Family: nl.FAMILY_ALL,
@ -30,8 +107,14 @@ func FilterDel(filter Filter) error {
// FilterAdd will add a filter to the system. // FilterAdd will add a filter to the system.
// Equivalent to: `tc filter add $filter` // Equivalent to: `tc filter add $filter`
func FilterAdd(filter Filter) error { 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() 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() base := filter.Attrs()
msg := &nl.TcMsg{ msg := &nl.TcMsg{
Family: nl.FAMILY_ALL, Family: nl.FAMILY_ALL,
@ -52,17 +135,17 @@ func FilterAdd(filter Filter) error {
} }
sel.Keys = append(sel.Keys, nl.TcU32Key{}) sel.Keys = append(sel.Keys, nl.TcU32Key{})
nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize()) nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
actions := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil) if u32.ClassId != 0 {
table := nl.NewRtAttrChild(actions, nl.TCA_ACT_TAB, nil) nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId))
nl.NewRtAttrChild(table, nl.TCA_KIND, nl.ZeroTerminated("mirred")) }
// redirect to other interface actionsAttr := nl.NewRtAttrChild(options, nl.TCA_U32_ACT, nil)
mir := nl.TcMirred{ // backwards compatibility
Action: nl.TC_ACT_STOLEN, if u32.RedirIndex != 0 {
Eaction: nl.TCA_EGRESS_REDIR, u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...)
Ifindex: uint32(u32.RedirIndex), }
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 { } else if fw, ok := filter.(*Fw); ok {
if fw.Mask != 0 { if fw.Mask != 0 {
b := make([]byte, 4) b := make([]byte, 4)
@ -90,6 +173,21 @@ func FilterAdd(filter Filter) error {
native.PutUint32(b, fw.ClassId) native.PutUint32(b, fw.ClassId)
nl.NewRtAttrChild(options, nl.TCA_FW_CLASSID, b) 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) req.AddData(options)
@ -99,16 +197,23 @@ func FilterAdd(filter Filter) error {
// FilterList gets a list of filters in the system. // FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`. // 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) { 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{ msg := &nl.TcMsg{
Family: nl.FAMILY_ALL, Family: nl.FAMILY_ALL,
Parent: parent, Parent: parent,
} }
if link != nil { if link != nil {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
msg.Ifindex = int32(base.Index) msg.Ifindex = int32(base.Index)
} }
req.AddData(msg) req.AddData(msg)
@ -147,29 +252,34 @@ func FilterList(link Link, parent uint32) ([]Filter, error) {
filter = &U32{} filter = &U32{}
case "fw": case "fw":
filter = &Fw{} filter = &Fw{}
case "bpf":
filter = &BpfFilter{}
default: default:
filter = &GenericFilter{FilterType: filterType} filter = &GenericFilter{FilterType: filterType}
} }
case nl.TCA_OPTIONS: case nl.TCA_OPTIONS:
switch filterType {
case "u32":
data, err := nl.ParseRouteAttr(attr.Value) data, err := nl.ParseRouteAttr(attr.Value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
switch filterType {
case "u32":
detailed, err = parseU32Data(filter, data) detailed, err = parseU32Data(filter, data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
case "fw": case "fw":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
detailed, err = parseFwData(filter, data) detailed, err = parseFwData(filter, data)
if err != nil { if err != nil {
return nil, err 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 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) { func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
native = nl.NativeEndian() native = nl.NativeEndian()
u32 := filter.(*U32) u32 := filter.(*U32)
@ -197,34 +430,17 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
return detailed, nil return detailed, nil
} }
case nl.TCA_U32_ACT: case nl.TCA_U32_ACT:
table, err := nl.ParseRouteAttr(datum.Value) tables, err := nl.ParseRouteAttr(datum.Value)
if err != nil { if err != nil {
return detailed, err return detailed, err
} }
if len(table) != 1 || table[0].Attr.Type != nl.TCA_ACT_TAB { u32.Actions, err = parseActions(tables)
return detailed, fmt.Errorf("Action table not formed properly")
}
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 { if err != nil {
return detailed, err return detailed, err
} }
for _, adatum := range adata { for _, action := range u32.Actions {
switch adatum.Attr.Type { if action, ok := action.(*MirredAction); ok {
case nl.TCA_MIRRED_PARMS: u32.RedirIndex = int(action.Ifindex)
mir := nl.DeserializeTcMirred(adatum.Value)
u32.RedirIndex = int(mir.Ifindex)
}
}
} }
} }
} }
@ -261,6 +477,28 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
return detailed, nil 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 { func AlignToAtm(size uint) uint {
var linksize, cells int var linksize, cells int
cells = int(size / nl.ATM_CELL_PAYLOAD) 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 bps := rate.Rate
mpu := rate.Mpu mpu := rate.Mpu
var sz uint var sz uint
if mtu == 0 { if mtu == 0 {
mtu = 2047 mtu = 2047
} }
if cell_log < 0 { if cellLog < 0 {
cell_log = 0 cellLog = 0
for (mtu >> uint(cell_log)) > 255 { for (mtu >> uint(cellLog)) > 255 {
cell_log++ cellLog++
} }
} }
for i := 0; i < 256; i++ { for i := 0; i < 256; i++ {
sz = AdjustSize(uint((i+1)<<uint32(cell_log)), uint(mpu), linklayer) sz = AdjustSize(uint((i+1)<<uint32(cellLog)), uint(mpu), linklayer)
rtab[i] = uint32(Xmittime(uint64(bps), uint32(sz))) rtab[i] = uint32(Xmittime(uint64(bps), uint32(sz)))
} }
rate.CellAlign = -1 rate.CellAlign = -1
rate.CellLog = uint8(cell_log) rate.CellLog = uint8(cellLog)
rate.Linklayer = uint8(linklayer & nl.TC_LINKLAYER_MASK) rate.Linklayer = uint8(linklayer & nl.TC_LINKLAYER_MASK)
return cell_log return cellLog
} }
func DeserializeRtab(b []byte) [256]uint32 { func DeserializeRtab(b []byte) [256]uint32 {

111
vendor/github.com/vishvananda/netlink/handle_linux.go generated vendored Normal file
View File

@ -0,0 +1,111 @@
package netlink
import (
"fmt"
"syscall"
"time"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
)
// Empty handle used by the netlink package methods
var pkgHandle = &Handle{}
// Handle is an handle for the netlink requests on a
// specific network namespace. All the requests on the
// same netlink family share the same netlink socket,
// which gets released when the handle is deleted.
type Handle struct {
sockets map[int]*nl.SocketHandle
lookupByDump bool
}
// SupportsNetlinkFamily reports whether the passed netlink family is supported by this Handle
func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool {
_, ok := h.sockets[nlFamily]
return ok
}
// NewHandle returns a netlink handle on the current network namespace.
// Caller may specify the netlink families the handle should support.
// If no families are specified, all the families the netlink package
// supports will be automatically added.
func NewHandle(nlFamilies ...int) (*Handle, error) {
return newHandle(netns.None(), netns.None(), nlFamilies...)
}
// SetSocketTimeout sets the send and receive timeout for each socket in the
// netlink handle. Although the socket timeout has granularity of one
// microsecond, the effective granularity is floored by the kernel timer tick,
// which default value is four milliseconds.
func (h *Handle) SetSocketTimeout(to time.Duration) error {
if to < time.Microsecond {
return fmt.Errorf("invalid timeout, minimul value is %s", time.Microsecond)
}
tv := syscall.NsecToTimeval(to.Nanoseconds())
for _, sh := range h.sockets {
fd := sh.Socket.GetFd()
err := syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)
if err != nil {
return err
}
err = syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &tv)
if err != nil {
return err
}
}
return nil
}
// NewHandle returns a netlink handle on the network namespace
// specified by ns. If ns=netns.None(), current network namespace
// will be assumed
func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error) {
return newHandle(ns, netns.None(), nlFamilies...)
}
// NewHandleAtFrom works as NewHandle but allows client to specify the
// new and the origin netns Handle.
func NewHandleAtFrom(newNs, curNs netns.NsHandle) (*Handle, error) {
return newHandle(newNs, curNs)
}
func newHandle(newNs, curNs netns.NsHandle, nlFamilies ...int) (*Handle, error) {
h := &Handle{sockets: map[int]*nl.SocketHandle{}}
fams := nl.SupportedNlFamilies
if len(nlFamilies) != 0 {
fams = nlFamilies
}
for _, f := range fams {
s, err := nl.GetNetlinkSocketAt(newNs, curNs, f)
if err != nil {
return nil, err
}
h.sockets[f] = &nl.SocketHandle{Socket: s}
}
return h, nil
}
// Delete releases the resources allocated to this handle
func (h *Handle) Delete() {
for _, sh := range h.sockets {
sh.Close()
}
h.sockets = nil
}
func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest {
// Do this so that package API still use nl package variable nextSeqNr
if h.sockets == nil {
return nl.NewNetlinkRequest(proto, flags)
}
return &nl.NetlinkRequest{
NlMsghdr: syscall.NlMsghdr{
Len: uint32(syscall.SizeofNlMsghdr),
Type: uint16(proto),
Flags: syscall.NLM_F_REQUEST | uint16(flags),
},
Sockets: h.sockets,
}
}

View File

@ -0,0 +1,218 @@
// +build !linux
package netlink
import (
"net"
"time"
"github.com/vishvananda/netns"
)
type Handle struct{}
func NewHandle(nlFamilies ...int) (*Handle, error) {
return nil, ErrNotImplemented
}
func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error) {
return nil, ErrNotImplemented
}
func NewHandleAtFrom(newNs, curNs netns.NsHandle) (*Handle, error) {
return nil, ErrNotImplemented
}
func (h *Handle) Delete() {}
func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool {
return false
}
func (h *Handle) SetSocketTimeout(to time.Duration) error {
return ErrNotImplemented
}
func (h *Handle) SetPromiscOn(link Link) error {
return ErrNotImplemented
}
func (h *Handle) SetPromiscOff(link Link) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetUp(link Link) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetDown(link Link) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetMTU(link Link, mtu int) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetName(link Link, name string) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetAlias(link Link, name string) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetVfVlan(link Link, vf, vlan int) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetVfTxRate(link Link, vf, rate int) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetMaster(link Link, master *Bridge) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetNoMaster(link Link) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetMasterByIndex(link Link, masterIndex int) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetNsPid(link Link, nspid int) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetNsFd(link Link, fd int) error {
return ErrNotImplemented
}
func (h *Handle) LinkAdd(link Link) error {
return ErrNotImplemented
}
func (h *Handle) LinkDel(link Link) error {
return ErrNotImplemented
}
func (h *Handle) LinkByName(name string) (Link, error) {
return nil, ErrNotImplemented
}
func (h *Handle) LinkByAlias(alias string) (Link, error) {
return nil, ErrNotImplemented
}
func (h *Handle) LinkByIndex(index int) (Link, error) {
return nil, ErrNotImplemented
}
func (h *Handle) LinkList() ([]Link, error) {
return nil, ErrNotImplemented
}
func (h *Handle) LinkSetHairpin(link Link, mode bool) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetGuard(link Link, mode bool) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetFastLeave(link Link, mode bool) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetLearning(link Link, mode bool) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetRootBlock(link Link, mode bool) error {
return ErrNotImplemented
}
func (h *Handle) LinkSetFlood(link Link, mode bool) error {
return ErrNotImplemented
}
func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error {
return ErrNotImplemented
}
func (h *Handle) AddrAdd(link Link, addr *Addr) error {
return ErrNotImplemented
}
func (h *Handle) AddrDel(link Link, addr *Addr) error {
return ErrNotImplemented
}
func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
return nil, ErrNotImplemented
}
func (h *Handle) ClassDel(class Class) error {
return ErrNotImplemented
}
func (h *Handle) ClassChange(class Class) error {
return ErrNotImplemented
}
func (h *Handle) ClassReplace(class Class) error {
return ErrNotImplemented
}
func (h *Handle) ClassAdd(class Class) error {
return ErrNotImplemented
}
func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
return nil, ErrNotImplemented
}
func (h *Handle) FilterDel(filter Filter) error {
return ErrNotImplemented
}
func (h *Handle) FilterAdd(filter Filter) error {
return ErrNotImplemented
}
func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
return nil, ErrNotImplemented
}
func (h *Handle) NeighAdd(neigh *Neigh) error {
return ErrNotImplemented
}
func (h *Handle) NeighSet(neigh *Neigh) error {
return ErrNotImplemented
}
func (h *Handle) NeighAppend(neigh *Neigh) error {
return ErrNotImplemented
}
func (h *Handle) NeighDel(neigh *Neigh) error {
return ErrNotImplemented
}
func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
return nil, ErrNotImplemented
}
func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
return nil, ErrNotImplemented
}

View File

@ -3,7 +3,6 @@ package netlink
import ( import (
"fmt" "fmt"
"net" "net"
"syscall"
) )
// Link represents a link device from netlink. Shared link attributes // Link represents a link device from netlink. Shared link attributes
@ -27,10 +26,50 @@ type LinkAttrs struct {
Name string Name string
HardwareAddr net.HardwareAddr HardwareAddr net.HardwareAddr
Flags net.Flags Flags net.Flags
RawFlags uint32
ParentIndex int // index of the parent link device ParentIndex int // index of the parent link device
MasterIndex int // must be the index of a bridge MasterIndex int // must be the index of a bridge
Namespace interface{} // nil | NsPid | NsFd Namespace interface{} // nil | NsPid | NsFd
Alias string Alias string
Statistics *LinkStatistics
Promisc int
Xdp *LinkXdp
EncapType string
Protinfo *Protinfo
OperState LinkOperState
}
// LinkOperState represents the values of the IFLA_OPERSTATE link
// attribute, which contains the RFC2863 state of the interface.
type LinkOperState uint8
const (
OperUnknown = iota // Status can't be determined.
OperNotPresent // Some component is missing.
OperDown // Down.
OperLowerLayerDown // Down due to state of lower layer.
OperTesting // In some test mode.
OperDormant // Not up but pending an external event.
OperUp // Up, ready to send packets.
)
func (s LinkOperState) String() string {
switch s {
case OperNotPresent:
return "not-present"
case OperDown:
return "down"
case OperLowerLayerDown:
return "lower-layer-down"
case OperTesting:
return "testing"
case OperDormant:
return "dormant"
case OperUp:
return "up"
default:
return "unknown"
}
} }
// NewLinkAttrs returns LinkAttrs structure filled with default values // NewLinkAttrs returns LinkAttrs structure filled with default values
@ -40,6 +79,99 @@ func NewLinkAttrs() LinkAttrs {
} }
} }
type LinkStatistics LinkStatistics64
/*
Ref: struct rtnl_link_stats {...}
*/
type LinkStatistics32 struct {
RxPackets uint32
TxPackets uint32
RxBytes uint32
TxBytes uint32
RxErrors uint32
TxErrors uint32
RxDropped uint32
TxDropped uint32
Multicast uint32
Collisions uint32
RxLengthErrors uint32
RxOverErrors uint32
RxCrcErrors uint32
RxFrameErrors uint32
RxFifoErrors uint32
RxMissedErrors uint32
TxAbortedErrors uint32
TxCarrierErrors uint32
TxFifoErrors uint32
TxHeartbeatErrors uint32
TxWindowErrors uint32
RxCompressed uint32
TxCompressed uint32
}
func (s32 LinkStatistics32) to64() *LinkStatistics64 {
return &LinkStatistics64{
RxPackets: uint64(s32.RxPackets),
TxPackets: uint64(s32.TxPackets),
RxBytes: uint64(s32.RxBytes),
TxBytes: uint64(s32.TxBytes),
RxErrors: uint64(s32.RxErrors),
TxErrors: uint64(s32.TxErrors),
RxDropped: uint64(s32.RxDropped),
TxDropped: uint64(s32.TxDropped),
Multicast: uint64(s32.Multicast),
Collisions: uint64(s32.Collisions),
RxLengthErrors: uint64(s32.RxLengthErrors),
RxOverErrors: uint64(s32.RxOverErrors),
RxCrcErrors: uint64(s32.RxCrcErrors),
RxFrameErrors: uint64(s32.RxFrameErrors),
RxFifoErrors: uint64(s32.RxFifoErrors),
RxMissedErrors: uint64(s32.RxMissedErrors),
TxAbortedErrors: uint64(s32.TxAbortedErrors),
TxCarrierErrors: uint64(s32.TxCarrierErrors),
TxFifoErrors: uint64(s32.TxFifoErrors),
TxHeartbeatErrors: uint64(s32.TxHeartbeatErrors),
TxWindowErrors: uint64(s32.TxWindowErrors),
RxCompressed: uint64(s32.RxCompressed),
TxCompressed: uint64(s32.TxCompressed),
}
}
/*
Ref: struct rtnl_link_stats64 {...}
*/
type LinkStatistics64 struct {
RxPackets uint64
TxPackets uint64
RxBytes uint64
TxBytes uint64
RxErrors uint64
TxErrors uint64
RxDropped uint64
TxDropped uint64
Multicast uint64
Collisions uint64
RxLengthErrors uint64
RxOverErrors uint64
RxCrcErrors uint64
RxFrameErrors uint64
RxFifoErrors uint64
RxMissedErrors uint64
TxAbortedErrors uint64
TxCarrierErrors uint64
TxFifoErrors uint64
TxHeartbeatErrors uint64
TxWindowErrors uint64
RxCompressed uint64
TxCompressed uint64
}
type LinkXdp struct {
Fd int
Attached bool
}
// Device links cannot be created via netlink. These links // Device links cannot be created via netlink. These links
// are links created by udev like 'lo' and 'etho0' // are links created by udev like 'lo' and 'etho0'
type Device struct { type Device struct {
@ -142,16 +274,13 @@ func (macvtap Macvtap) Type() string {
} }
type TuntapMode uint16 type TuntapMode uint16
type TuntapFlag uint16
const (
TUNTAP_MODE_TUN TuntapMode = syscall.IFF_TUN
TUNTAP_MODE_TAP TuntapMode = syscall.IFF_TAP
)
// Tuntap links created via /dev/tun/tap, but can be destroyed via netlink // Tuntap links created via /dev/tun/tap, but can be destroyed via netlink
type Tuntap struct { type Tuntap struct {
LinkAttrs LinkAttrs
Mode TuntapMode Mode TuntapMode
Flags TuntapFlag
} }
func (tuntap *Tuntap) Attrs() *LinkAttrs { func (tuntap *Tuntap) Attrs() *LinkAttrs {
@ -227,6 +356,7 @@ type IPVlanMode uint16
const ( const (
IPVLAN_MODE_L2 IPVlanMode = iota IPVLAN_MODE_L2 IPVlanMode = iota
IPVLAN_MODE_L3 IPVLAN_MODE_L3
IPVLAN_MODE_L3S
IPVLAN_MODE_MAX IPVLAN_MODE_MAX
) )
@ -265,31 +395,31 @@ func StringToBondMode(s string) BondMode {
// Possible BondMode // Possible BondMode
const ( const (
BOND_MODE_802_3AD BondMode = iota BOND_MODE_BALANCE_RR BondMode = iota
BOND_MODE_BALANCE_RR
BOND_MODE_ACTIVE_BACKUP BOND_MODE_ACTIVE_BACKUP
BOND_MODE_BALANCE_XOR BOND_MODE_BALANCE_XOR
BOND_MODE_BROADCAST BOND_MODE_BROADCAST
BOND_MODE_802_3AD
BOND_MODE_BALANCE_TLB BOND_MODE_BALANCE_TLB
BOND_MODE_BALANCE_ALB BOND_MODE_BALANCE_ALB
BOND_MODE_UNKNOWN BOND_MODE_UNKNOWN
) )
var bondModeToString = map[BondMode]string{ var bondModeToString = map[BondMode]string{
BOND_MODE_802_3AD: "802.3ad",
BOND_MODE_BALANCE_RR: "balance-rr", BOND_MODE_BALANCE_RR: "balance-rr",
BOND_MODE_ACTIVE_BACKUP: "active-backup", BOND_MODE_ACTIVE_BACKUP: "active-backup",
BOND_MODE_BALANCE_XOR: "balance-xor", BOND_MODE_BALANCE_XOR: "balance-xor",
BOND_MODE_BROADCAST: "broadcast", BOND_MODE_BROADCAST: "broadcast",
BOND_MODE_802_3AD: "802.3ad",
BOND_MODE_BALANCE_TLB: "balance-tlb", BOND_MODE_BALANCE_TLB: "balance-tlb",
BOND_MODE_BALANCE_ALB: "balance-alb", BOND_MODE_BALANCE_ALB: "balance-alb",
} }
var StringToBondModeMap = map[string]BondMode{ var StringToBondModeMap = map[string]BondMode{
"802.3ad": BOND_MODE_802_3AD,
"balance-rr": BOND_MODE_BALANCE_RR, "balance-rr": BOND_MODE_BALANCE_RR,
"active-backup": BOND_MODE_ACTIVE_BACKUP, "active-backup": BOND_MODE_ACTIVE_BACKUP,
"balance-xor": BOND_MODE_BALANCE_XOR, "balance-xor": BOND_MODE_BALANCE_XOR,
"broadcast": BOND_MODE_BROADCAST, "broadcast": BOND_MODE_BROADCAST,
"802.3ad": BOND_MODE_802_3AD,
"balance-tlb": BOND_MODE_BALANCE_TLB, "balance-tlb": BOND_MODE_BALANCE_TLB,
"balance-alb": BOND_MODE_BALANCE_ALB, "balance-alb": BOND_MODE_BALANCE_ALB,
} }
@ -425,7 +555,7 @@ const (
BOND_AD_SELECT_COUNT BOND_AD_SELECT_COUNT
) )
// BondAdInfo // BondAdInfo represents ad info for bond
type BondAdInfo struct { type BondAdInfo struct {
AggregatorId int AggregatorId int
NumPorts int NumPorts int
@ -526,7 +656,7 @@ func (bond *Bond) Type() string {
return "bond" return "bond"
} }
// GreTap devices must specify LocalIP and RemoteIP on create // Gretap devices must specify LocalIP and RemoteIP on create
type Gretap struct { type Gretap struct {
LinkAttrs LinkAttrs
IKey uint32 IKey uint32
@ -553,6 +683,54 @@ func (gretap *Gretap) Type() string {
return "gretap" return "gretap"
} }
type Iptun struct {
LinkAttrs
Ttl uint8
Tos uint8
PMtuDisc uint8
Link uint32
Local net.IP
Remote net.IP
}
func (iptun *Iptun) Attrs() *LinkAttrs {
return &iptun.LinkAttrs
}
func (iptun *Iptun) Type() string {
return "ipip"
}
type Vti struct {
LinkAttrs
IKey uint32
OKey uint32
Link uint32
Local net.IP
Remote net.IP
}
func (vti *Vti) Attrs() *LinkAttrs {
return &vti.LinkAttrs
}
func (iptun *Vti) Type() string {
return "vti"
}
type Vrf struct {
LinkAttrs
Table uint32
}
func (vrf *Vrf) Attrs() *LinkAttrs {
return &vrf.LinkAttrs
}
func (vrf *Vrf) Type() string {
return "vrf"
}
// iproute2 supported devices; // iproute2 supported devices;
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap | // vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |

View File

@ -10,9 +10,25 @@ import (
"unsafe" "unsafe"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
)
const (
SizeofLinkStats32 = 0x5c
SizeofLinkStats64 = 0xd8
IFLA_STATS64 = 0x17 // syscall pkg does not contain this one
)
const (
TUNTAP_MODE_TUN TuntapMode = syscall.IFF_TUN
TUNTAP_MODE_TAP TuntapMode = syscall.IFF_TAP
TUNTAP_DEFAULTS TuntapFlag = syscall.IFF_TUN_EXCL | syscall.IFF_ONE_QUEUE
TUNTAP_VNET_HDR TuntapFlag = syscall.IFF_VNET_HDR
TUNTAP_TUN_EXCL TuntapFlag = syscall.IFF_TUN_EXCL
TUNTAP_NO_PI TuntapFlag = syscall.IFF_NO_PI
TUNTAP_ONE_QUEUE TuntapFlag = syscall.IFF_ONE_QUEUE
) )
var native = nl.NativeEndian()
var lookupByDump = false var lookupByDump = false
var macvlanModes = [...]uint32{ var macvlanModes = [...]uint32{
@ -33,12 +49,65 @@ func ensureIndex(link *LinkAttrs) {
} }
} }
func (h *Handle) ensureIndex(link *LinkAttrs) {
if link != nil && link.Index == 0 {
newlink, _ := h.LinkByName(link.Name)
if newlink != nil {
link.Index = newlink.Attrs().Index
}
}
}
func (h *Handle) SetPromiscOn(link Link) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_PROMISC
msg.Flags = syscall.IFF_UP
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
func SetPromiscOn(link Link) error {
return pkgHandle.SetPromiscOn(link)
}
func (h *Handle) SetPromiscOff(link Link) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_PROMISC
msg.Flags = 0 & ^syscall.IFF_UP
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
func SetPromiscOff(link Link) error {
return pkgHandle.SetPromiscOff(link)
}
// LinkSetUp enables the link device. // LinkSetUp enables the link device.
// Equivalent to: `ip link set $link up` // Equivalent to: `ip link set $link up`
func LinkSetUp(link Link) error { func LinkSetUp(link Link) error {
return pkgHandle.LinkSetUp(link)
}
// LinkSetUp enables the link device.
// Equivalent to: `ip link set $link up`
func (h *Handle) LinkSetUp(link Link) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_UP msg.Change = syscall.IFF_UP
@ -53,9 +122,15 @@ func LinkSetUp(link Link) error {
// LinkSetDown disables link device. // LinkSetDown disables link device.
// Equivalent to: `ip link set $link down` // Equivalent to: `ip link set $link down`
func LinkSetDown(link Link) error { func LinkSetDown(link Link) error {
return pkgHandle.LinkSetDown(link)
}
// LinkSetDown disables link device.
// Equivalent to: `ip link set $link down`
func (h *Handle) LinkSetDown(link Link) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_UP msg.Change = syscall.IFF_UP
@ -70,9 +145,15 @@ func LinkSetDown(link Link) error {
// LinkSetMTU sets the mtu of the link device. // LinkSetMTU sets the mtu of the link device.
// Equivalent to: `ip link set $link mtu $mtu` // Equivalent to: `ip link set $link mtu $mtu`
func LinkSetMTU(link Link, mtu int) error { func LinkSetMTU(link Link, mtu int) error {
return pkgHandle.LinkSetMTU(link, mtu)
}
// LinkSetMTU sets the mtu of the link device.
// Equivalent to: `ip link set $link mtu $mtu`
func (h *Handle) LinkSetMTU(link Link, mtu int) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -91,9 +172,15 @@ func LinkSetMTU(link Link, mtu int) error {
// LinkSetName sets the name of the link device. // LinkSetName sets the name of the link device.
// Equivalent to: `ip link set $link name $name` // Equivalent to: `ip link set $link name $name`
func LinkSetName(link Link, name string) error { func LinkSetName(link Link, name string) error {
return pkgHandle.LinkSetName(link, name)
}
// LinkSetName sets the name of the link device.
// Equivalent to: `ip link set $link name $name`
func (h *Handle) LinkSetName(link Link, name string) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -109,9 +196,15 @@ func LinkSetName(link Link, name string) error {
// LinkSetAlias sets the alias of the link device. // LinkSetAlias sets the alias of the link device.
// Equivalent to: `ip link set dev $link alias $name` // Equivalent to: `ip link set dev $link alias $name`
func LinkSetAlias(link Link, name string) error { func LinkSetAlias(link Link, name string) error {
return pkgHandle.LinkSetAlias(link, name)
}
// LinkSetAlias sets the alias of the link device.
// Equivalent to: `ip link set dev $link alias $name`
func (h *Handle) LinkSetAlias(link Link, name string) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -127,9 +220,15 @@ func LinkSetAlias(link Link, name string) error {
// LinkSetHardwareAddr sets the hardware address of the link device. // LinkSetHardwareAddr sets the hardware address of the link device.
// Equivalent to: `ip link set $link address $hwaddr` // Equivalent to: `ip link set $link address $hwaddr`
func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
return pkgHandle.LinkSetHardwareAddr(link, hwaddr)
}
// LinkSetHardwareAddr sets the hardware address of the link device.
// Equivalent to: `ip link set $link address $hwaddr`
func (h *Handle) LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -145,9 +244,15 @@ func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
// LinkSetVfHardwareAddr sets the hardware address of a vf for the link. // LinkSetVfHardwareAddr sets the hardware address of a vf for the link.
// Equivalent to: `ip link set $link vf $vf mac $hwaddr` // Equivalent to: `ip link set $link vf $vf mac $hwaddr`
func LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error { func LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error {
return pkgHandle.LinkSetVfHardwareAddr(link, vf, hwaddr)
}
// LinkSetVfHardwareAddr sets the hardware address of a vf for the link.
// Equivalent to: `ip link set $link vf $vf mac $hwaddr`
func (h *Handle) LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -169,9 +274,15 @@ func LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error {
// LinkSetVfVlan sets the vlan of a vf for the link. // LinkSetVfVlan sets the vlan of a vf for the link.
// Equivalent to: `ip link set $link vf $vf vlan $vlan` // Equivalent to: `ip link set $link vf $vf vlan $vlan`
func LinkSetVfVlan(link Link, vf, vlan int) error { func LinkSetVfVlan(link Link, vf, vlan int) error {
return pkgHandle.LinkSetVfVlan(link, vf, vlan)
}
// LinkSetVfVlan sets the vlan of a vf for the link.
// Equivalent to: `ip link set $link vf $vf vlan $vlan`
func (h *Handle) LinkSetVfVlan(link Link, vf, vlan int) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -190,33 +301,81 @@ func LinkSetVfVlan(link Link, vf, vlan int) error {
return err return err
} }
// LinkSetVfTxRate sets the tx rate of a vf for the link.
// Equivalent to: `ip link set $link vf $vf rate $rate`
func LinkSetVfTxRate(link Link, vf, rate int) error {
return pkgHandle.LinkSetVfTxRate(link, vf, rate)
}
// LinkSetVfTxRate sets the tx rate of a vf for the link.
// Equivalent to: `ip link set $link vf $vf rate $rate`
func (h *Handle) LinkSetVfTxRate(link Link, vf, rate int) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index)
req.AddData(msg)
data := nl.NewRtAttr(nl.IFLA_VFINFO_LIST, nil)
info := nl.NewRtAttrChild(data, nl.IFLA_VF_INFO, nil)
vfmsg := nl.VfTxRate{
Vf: uint32(vf),
Rate: uint32(rate),
}
nl.NewRtAttrChild(info, nl.IFLA_VF_TX_RATE, vfmsg.Serialize())
req.AddData(data)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetMaster sets the master of the link device. // LinkSetMaster sets the master of the link device.
// Equivalent to: `ip link set $link master $master` // Equivalent to: `ip link set $link master $master`
func LinkSetMaster(link Link, master *Bridge) error { func LinkSetMaster(link Link, master *Bridge) error {
return pkgHandle.LinkSetMaster(link, master)
}
// LinkSetMaster sets the master of the link device.
// Equivalent to: `ip link set $link master $master`
func (h *Handle) LinkSetMaster(link Link, master *Bridge) error {
index := 0 index := 0
if master != nil { if master != nil {
masterBase := master.Attrs() masterBase := master.Attrs()
ensureIndex(masterBase) h.ensureIndex(masterBase)
index = masterBase.Index index = masterBase.Index
} }
if index <= 0 { if index <= 0 {
return fmt.Errorf("Device does not exist") return fmt.Errorf("Device does not exist")
} }
return LinkSetMasterByIndex(link, index) return h.LinkSetMasterByIndex(link, index)
} }
// LinkSetNoMaster removes the master of the link device. // LinkSetNoMaster removes the master of the link device.
// Equivalent to: `ip link set $link nomaster` // Equivalent to: `ip link set $link nomaster`
func LinkSetNoMaster(link Link) error { func LinkSetNoMaster(link Link) error {
return LinkSetMasterByIndex(link, 0) return pkgHandle.LinkSetNoMaster(link)
}
// LinkSetNoMaster removes the master of the link device.
// Equivalent to: `ip link set $link nomaster`
func (h *Handle) LinkSetNoMaster(link Link) error {
return h.LinkSetMasterByIndex(link, 0)
} }
// LinkSetMasterByIndex sets the master of the link device. // LinkSetMasterByIndex sets the master of the link device.
// Equivalent to: `ip link set $link master $master` // Equivalent to: `ip link set $link master $master`
func LinkSetMasterByIndex(link Link, masterIndex int) error { func LinkSetMasterByIndex(link Link, masterIndex int) error {
return pkgHandle.LinkSetMasterByIndex(link, masterIndex)
}
// LinkSetMasterByIndex sets the master of the link device.
// Equivalent to: `ip link set $link master $master`
func (h *Handle) LinkSetMasterByIndex(link Link, masterIndex int) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -236,9 +395,16 @@ func LinkSetMasterByIndex(link Link, masterIndex int) error {
// pid must be a pid of a running process. // pid must be a pid of a running process.
// Equivalent to: `ip link set $link netns $pid` // Equivalent to: `ip link set $link netns $pid`
func LinkSetNsPid(link Link, nspid int) error { func LinkSetNsPid(link Link, nspid int) error {
return pkgHandle.LinkSetNsPid(link, nspid)
}
// LinkSetNsPid puts the device into a new network namespace. The
// pid must be a pid of a running process.
// Equivalent to: `ip link set $link netns $pid`
func (h *Handle) LinkSetNsPid(link Link, nspid int) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -258,9 +424,16 @@ func LinkSetNsPid(link Link, nspid int) error {
// fd must be an open file descriptor to a network namespace. // fd must be an open file descriptor to a network namespace.
// Similar to: `ip link set $link netns $ns` // Similar to: `ip link set $link netns $ns`
func LinkSetNsFd(link Link, fd int) error { func LinkSetNsFd(link Link, fd int) error {
return pkgHandle.LinkSetNsFd(link, fd)
}
// LinkSetNsFd puts the device into a new network namespace. The
// fd must be an open file descriptor to a network namespace.
// Similar to: `ip link set $link netns $ns`
func (h *Handle) LinkSetNsFd(link Link, fd int) error {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -276,6 +449,23 @@ func LinkSetNsFd(link Link, fd int) error {
return err return err
} }
// LinkSetXdpFd adds a bpf function to the driver. The fd must be a bpf
// program loaded with bpf(type=BPF_PROG_TYPE_XDP)
func LinkSetXdpFd(link Link, fd int) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index)
req.AddData(msg)
addXdpAttrs(&LinkXdp{Fd: fd}, req)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
func boolAttr(val bool) []byte { func boolAttr(val bool) []byte {
var v uint8 var v uint8
if val { if val {
@ -329,7 +519,7 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_UDP_CSUM, boolAttr(vxlan.UDPCSum)) nl.NewRtAttrChild(data, nl.IFLA_VXLAN_UDP_CSUM, boolAttr(vxlan.UDPCSum))
} }
if vxlan.GBP { if vxlan.GBP {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, boolAttr(vxlan.GBP)) nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, []byte{})
} }
if vxlan.NoAge { if vxlan.NoAge {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0)) nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
@ -340,7 +530,7 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit))) nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit)))
} }
if vxlan.Port > 0 { if vxlan.Port > 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 { if vxlan.PortLow > 0 || vxlan.PortHigh > 0 {
pr := vxlanPortRange{uint16(vxlan.PortLow), uint16(vxlan.PortHigh)} 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 // 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` // Equivalent to: `ip link add $link`
func LinkAdd(link Link) error { 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: set mtu and hardware address
// TODO: support extra data for macvlan // TODO: support extra data for macvlan
base := link.Attrs() base := link.Attrs()
@ -448,9 +645,7 @@ func LinkAdd(link Link) error {
if tuntap, ok := link.(*Tuntap); ok { if tuntap, ok := link.(*Tuntap); ok {
// TODO: support user // TODO: support user
// TODO: support group // TODO: support group
// TODO: support non- one_queue // TODO: multi_queue
// TODO: support pi | vnet_hdr | multi_queue
// TODO: support non- exclusive
// TODO: support non- persistent // TODO: support non- persistent
if tuntap.Mode < syscall.IFF_TUN || tuntap.Mode > syscall.IFF_TAP { if tuntap.Mode < syscall.IFF_TUN || tuntap.Mode > syscall.IFF_TAP {
return fmt.Errorf("Tuntap.Mode %v unknown!", tuntap.Mode) return fmt.Errorf("Tuntap.Mode %v unknown!", tuntap.Mode)
@ -461,10 +656,13 @@ func LinkAdd(link Link) error {
} }
defer file.Close() defer file.Close()
var req ifReq var req ifReq
req.Flags |= syscall.IFF_ONE_QUEUE if tuntap.Flags == 0 {
req.Flags |= syscall.IFF_TUN_EXCL req.Flags = uint16(TUNTAP_DEFAULTS)
copy(req.Name[:15], base.Name) } else {
req.Flags = uint16(tuntap.Flags)
}
req.Flags |= uint16(tuntap.Mode) 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))) _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&req)))
if errno != 0 { if errno != 0 {
return fmt.Errorf("Tuntap IOCTL TUNSETIFF failed, errno %v", errno) return fmt.Errorf("Tuntap IOCTL TUNSETIFF failed, errno %v", errno)
@ -473,17 +671,17 @@ func LinkAdd(link Link) error {
if errno != 0 { if errno != 0 {
return fmt.Errorf("Tuntap IOCTL TUNSETPERSIST failed, errno %v", errno) 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 // can't set master during create, so set it afterwards
if base.MasterIndex != 0 { if base.MasterIndex != 0 {
// TODO: verify MasterIndex is actually a bridge? // TODO: verify MasterIndex is actually a bridge?
return LinkSetMasterByIndex(link, base.MasterIndex) return h.LinkSetMasterByIndex(link, base.MasterIndex)
} }
return nil 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) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
// TODO: make it shorter // TODO: make it shorter
@ -545,6 +743,10 @@ func LinkAdd(link Link) error {
req.AddData(attr) req.AddData(attr)
} }
if base.Xdp != nil {
addXdpAttrs(base.Xdp, req)
}
linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil) linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil)
nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) 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) data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode])) 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 { } else if gretap, ok := link.(*Gretap); ok {
addGretapAttrs(gretap, linkInfo) 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) req.AddData(linkInfo)
@ -588,12 +801,12 @@ func LinkAdd(link Link) error {
return err return err
} }
ensureIndex(base) h.ensureIndex(base)
// can't set master during create, so set it afterwards // can't set master during create, so set it afterwards
if base.MasterIndex != 0 { if base.MasterIndex != 0 {
// TODO: verify MasterIndex is actually a bridge? // TODO: verify MasterIndex is actually a bridge?
return LinkSetMasterByIndex(link, base.MasterIndex) return h.LinkSetMasterByIndex(link, base.MasterIndex)
} }
return nil return nil
} }
@ -602,11 +815,18 @@ func LinkAdd(link Link) error {
// the link object for it to be deleted. The other values are ignored. // the link object for it to be deleted. The other values are ignored.
// Equivalent to: `ip link del $link` // Equivalent to: `ip link del $link`
func LinkDel(link Link) error { 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() 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 := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -616,8 +836,8 @@ func LinkDel(link Link) error {
return err return err
} }
func linkByNameDump(name string) (Link, error) { func (h *Handle) linkByNameDump(name string) (Link, error) {
links, err := LinkList() links, err := h.LinkList()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -630,8 +850,8 @@ func linkByNameDump(name string) (Link, error) {
return nil, fmt.Errorf("Link %s not found", name) return nil, fmt.Errorf("Link %s not found", name)
} }
func linkByAliasDump(alias string) (Link, error) { func (h *Handle) linkByAliasDump(alias string) (Link, error) {
links, err := LinkList() links, err := h.LinkList()
if err != nil { if err != nil {
return nil, err 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. // LinkByName finds a link by name and returns a pointer to the object.
func LinkByName(name string) (Link, error) { func LinkByName(name string) (Link, error) {
if lookupByDump { return pkgHandle.LinkByName(name)
return linkByNameDump(name)
} }
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) // 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 := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
req.AddData(msg) req.AddData(msg)
@ -662,8 +887,8 @@ func LinkByName(name string) (Link, error) {
if err == syscall.EINVAL { if err == syscall.EINVAL {
// older kernels don't support looking up via IFLA_IFNAME // older kernels don't support looking up via IFLA_IFNAME
// so fall back to dumping all links // so fall back to dumping all links
lookupByDump = true h.lookupByDump = true
return linkByNameDump(name) return h.linkByNameDump(name)
} }
return link, err 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. // 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 // If there are multiple links with the alias it returns the first one
func LinkByAlias(alias string) (Link, error) { func LinkByAlias(alias string) (Link, error) {
if lookupByDump { return pkgHandle.LinkByAlias(alias)
return linkByAliasDump(alias)
} }
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK) // 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 := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
req.AddData(msg) req.AddData(msg)
@ -688,8 +919,8 @@ func LinkByAlias(alias string) (Link, error) {
if err == syscall.EINVAL { if err == syscall.EINVAL {
// older kernels don't support looking up via IFLA_IFALIAS // older kernels don't support looking up via IFLA_IFALIAS
// so fall back to dumping all links // so fall back to dumping all links
lookupByDump = true h.lookupByDump = true
return linkByAliasDump(alias) return h.linkByAliasDump(alias)
} }
return link, err 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. // LinkByIndex finds a link by index and returns a pointer to the object.
func LinkByIndex(index int) (Link, error) { 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 := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(index) msg.Index = int32(index)
@ -722,7 +958,7 @@ func execGetLink(req *nl.NetlinkRequest) (Link, error) {
return nil, fmt.Errorf("Link not found") return nil, fmt.Errorf("Link not found")
case len(msgs) == 1: case len(msgs) == 1:
return linkDeserialize(msgs[0]) return LinkDeserialize(nil, msgs[0])
default: default:
return nil, fmt.Errorf("More than one link found") 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 // linkDeserialize deserializes a raw message received from netlink into
// a link object. // a link object.
func linkDeserialize(m []byte) (Link, error) { func LinkDeserialize(hdr *syscall.NlMsghdr, m []byte) (Link, error) {
msg := nl.DeserializeIfInfomsg(m) msg := nl.DeserializeIfInfomsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():]) attrs, err := nl.ParseRouteAttr(m[msg.Len():])
@ -739,9 +975,16 @@ func linkDeserialize(m []byte) (Link, error) {
return nil, err return nil, err
} }
base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)} base := LinkAttrs{Index: int(msg.Index), RawFlags: msg.Flags, Flags: linkFlags(msg.Flags), EncapType: msg.EncapType()}
var link Link if msg.Flags&syscall.IFF_PROMISC != 0 {
linkType := "" base.Promisc = 1
}
var (
link Link
stats32 []byte
stats64 []byte
linkType string
)
for _, attr := range attrs { for _, attr := range attrs {
switch attr.Attr.Type { switch attr.Attr.Type {
case syscall.IFLA_LINKINFO: case syscall.IFLA_LINKINFO:
@ -776,6 +1019,12 @@ func linkDeserialize(m []byte) (Link, error) {
link = &Macvtap{} link = &Macvtap{}
case "gretap": case "gretap":
link = &Gretap{} link = &Gretap{}
case "ipip":
link = &Iptun{}
case "vti":
link = &Vti{}
case "vrf":
link = &Vrf{}
default: default:
link = &GenericLink{LinkType: linkType} link = &GenericLink{LinkType: linkType}
} }
@ -799,6 +1048,12 @@ func linkDeserialize(m []byte) (Link, error) {
parseMacvtapData(link, data) parseMacvtapData(link, data)
case "gretap": case "gretap":
parseGretapData(link, data) 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])) base.TxQLen = int(native.Uint32(attr.Value[0:4]))
case syscall.IFLA_IFALIAS: case syscall.IFLA_IFALIAS:
base.Alias = string(attr.Value[:len(attr.Value)-1]) 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 // Links that don't have IFLA_INFO_KIND are hardware devices
if link == nil { if link == nil {
link = &Device{} link = &Device{}
@ -838,9 +1121,15 @@ func linkDeserialize(m []byte) (Link, error) {
// LinkList gets a list of link devices. // LinkList gets a list of link devices.
// Equivalent to: `ip link show` // Equivalent to: `ip link show`
func LinkList() ([]Link, error) { 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 // NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need
// to get the message ourselves to parse link type. // 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) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
req.AddData(msg) req.AddData(msg)
@ -852,7 +1141,7 @@ func LinkList() ([]Link, error) {
var res []Link var res []Link
for _, m := range msgs { for _, m := range msgs {
link, err := linkDeserialize(m) link, err := LinkDeserialize(nil, m)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -872,7 +1161,17 @@ type LinkUpdate struct {
// LinkSubscribe takes a chan down which notifications will be sent // LinkSubscribe takes a chan down which notifications will be sent
// when links change. Close the 'done' chan to stop subscription. // when links change. Close the 'done' chan to stop subscription.
func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { 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 { if err != nil {
return err return err
} }
@ -891,7 +1190,7 @@ func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error {
} }
for _, m := range msgs { for _, m := range msgs {
ifmsg := nl.DeserializeIfInfomsg(m.Data) ifmsg := nl.DeserializeIfInfomsg(m.Data)
link, err := linkDeserialize(m.Data) link, err := LinkDeserialize(&m.Header, m.Data)
if err != nil { if err != nil {
return return
} }
@ -904,33 +1203,57 @@ func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error {
} }
func LinkSetHairpin(link Link, mode bool) 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 { 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 { 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 { 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 { 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 { 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() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
msg.Index = int32(base.Index) msg.Index = int32(base.Index)
@ -989,14 +1312,14 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
case nl.IFLA_VXLAN_UDP_CSUM: case nl.IFLA_VXLAN_UDP_CSUM:
vxlan.UDPCSum = int8(datum.Value[0]) != 0 vxlan.UDPCSum = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_GBP: case nl.IFLA_VXLAN_GBP:
vxlan.GBP = int8(datum.Value[0]) != 0 vxlan.GBP = true
case nl.IFLA_VXLAN_AGEING: case nl.IFLA_VXLAN_AGEING:
vxlan.Age = int(native.Uint32(datum.Value[0:4])) vxlan.Age = int(native.Uint32(datum.Value[0:4]))
vxlan.NoAge = vxlan.Age == 0 vxlan.NoAge = vxlan.Age == 0
case nl.IFLA_VXLAN_LIMIT: case nl.IFLA_VXLAN_LIMIT:
vxlan.Limit = int(native.Uint32(datum.Value[0:4])) vxlan.Limit = int(native.Uint32(datum.Value[0:4]))
case nl.IFLA_VXLAN_PORT: 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: case nl.IFLA_VXLAN_PORT_RANGE:
buf := bytes.NewBuffer(datum.Value[0:4]) buf := bytes.NewBuffer(datum.Value[0:4])
var pr vxlanPortRange var pr vxlanPortRange
@ -1119,26 +1442,6 @@ func linkFlags(rawFlags uint32) net.Flags {
return f 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) { func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) 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])
}
}
}

View File

@ -67,30 +67,62 @@ func (msg *Ndmsg) Len() int {
// NeighAdd will add an IP to MAC mapping to the ARP table // NeighAdd will add an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh add ....` // Equivalent to: `ip neigh add ....`
func NeighAdd(neigh *Neigh) error { 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 // NeighSet will add or replace an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh replace....` // Equivalent to: `ip neigh replace....`
func NeighSet(neigh *Neigh) error { 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 // NeighAppend will append an entry to FDB
// Equivalent to: `bridge fdb append...` // Equivalent to: `bridge fdb append...`
func NeighAppend(neigh *Neigh) error { 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 { 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) return neighHandle(neigh, req)
} }
// NeighDel will delete an IP address from a link device. // NeighDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link` // Equivalent to: `ip addr del $addr dev $link`
func NeighDel(neigh *Neigh) error { 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) return neighHandle(neigh, req)
} }
@ -119,8 +151,10 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
dstData := nl.NewRtAttr(NDA_DST, ipData) dstData := nl.NewRtAttr(NDA_DST, ipData)
req.AddData(dstData) req.AddData(dstData)
if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
req.AddData(hwData) req.AddData(hwData)
}
_, err := req.Execute(syscall.NETLINK_ROUTE, 0) _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err return err
@ -130,10 +164,36 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
// Equivalent to: `ip neighbor show`. // Equivalent to: `ip neighbor show`.
// The list can be filtered by link and ip family. // The list can be filtered by link and ip family.
func NeighList(linkIndex, family int) ([]Neigh, error) { 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{ msg := Ndmsg{
Family: uint8(family), Family: uint8(family),
Index: uint32(linkIndex), Index: uint32(linkIndex),
Flags: uint8(flags),
} }
req.AddData(&msg) req.AddData(&msg)

View File

@ -9,16 +9,13 @@
package netlink package netlink
import ( import (
"errors"
"net" "net"
"github.com/vishvananda/netlink/nl"
) )
const ( var (
// Family type definitions // ErrNotImplemented is returned when a requested feature is not implemented.
FAMILY_ALL = nl.FAMILY_ALL ErrNotImplemented = errors.New("not implemented")
FAMILY_V4 = nl.FAMILY_V4
FAMILY_V6 = nl.FAMILY_V6
) )
// ParseIPNet parses a string in ip/net format and returns a net.IPNet. // ParseIPNet parses a string in ip/net format and returns a net.IPNet.

11
vendor/github.com/vishvananda/netlink/netlink_linux.go generated vendored Normal file
View File

@ -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
)

View File

@ -2,43 +2,109 @@
package netlink package netlink
import ( import "net"
"errors"
)
var ( func LinkSetUp(link Link) error {
ErrNotImplemented = errors.New("not implemented")
)
func LinkSetUp(link *Link) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetDown(link *Link) error { func LinkSetDown(link Link) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetMTU(link *Link, mtu int) error { func LinkSetMTU(link Link, mtu int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetMaster(link *Link, master *Link) error { func LinkSetMaster(link Link, master *Link) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetNsPid(link *Link, nspid int) error { func LinkSetNsPid(link Link, nspid int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetNsFd(link *Link, fd int) error { func LinkSetNsFd(link Link, fd int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkAdd(link *Link) error { func LinkSetName(link Link, name string) error {
return ErrNotImplemented 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 return ErrNotImplemented
} }
@ -70,15 +136,15 @@ func LinkList() ([]Link, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func AddrAdd(link *Link, addr *Addr) error { func AddrAdd(link Link, addr *Addr) error {
return ErrNotImplemented return ErrNotImplemented
} }
func AddrDel(link *Link, addr *Addr) error { func AddrDel(link Link, addr *Addr) error {
return ErrNotImplemented return ErrNotImplemented
} }
func AddrList(link *Link, family int) ([]Addr, error) { func AddrList(link Link, family int) ([]Addr, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
@ -90,7 +156,7 @@ func RouteDel(route *Route) error {
return ErrNotImplemented return ErrNotImplemented
} }
func RouteList(link *Link, family int) ([]Route, error) { func RouteList(link Link, family int) ([]Route, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
@ -138,6 +204,10 @@ func NeighList(linkIndex, family int) ([]Neigh, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func NeighDeserialize(m []byte) (*Ndmsg, *Neigh, error) { func NeighDeserialize(m []byte) (*Neigh, error) {
return nil, nil, ErrNotImplemented return nil, ErrNotImplemented
}
func SocketGet(local, remote net.Addr) (*Socket, error) {
return nil, ErrNotImplemented
} }

View File

@ -1,13 +1,35 @@
package nl package nl
import ( import (
"syscall"
"unsafe" "unsafe"
) )
const ( const (
DEFAULT_CHANGE = 0xFFFFFFFF DEFAULT_CHANGE = 0xFFFFFFFF
// doesn't exist in syscall // 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 ( const (
@ -89,11 +111,6 @@ const (
IFLA_IPVLAN_MAX = IFLA_IPVLAN_MODE IFLA_IPVLAN_MAX = IFLA_IPVLAN_MODE
) )
const (
// not defined in syscall
IFLA_NET_NS_FD = 28
)
const ( const (
IFLA_MACVLAN_UNSPEC = iota IFLA_MACVLAN_UNSPEC = iota
IFLA_MACVLAN_MODE IFLA_MACVLAN_MODE
@ -394,3 +411,44 @@ func DeserializeVfRssQueryEn(b []byte) *VfRssQueryEn {
func (msg *VfRssQueryEn) Serialize() []byte { func (msg *VfRssQueryEn) Serialize() []byte {
return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:] 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
)

36
vendor/github.com/vishvananda/netlink/nl/mpls_linux.go generated vendored Normal file
View File

@ -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
}

View File

@ -6,9 +6,13 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"net" "net"
"runtime"
"sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
"unsafe" "unsafe"
"github.com/vishvananda/netns"
) )
const ( const (
@ -16,8 +20,12 @@ const (
FAMILY_ALL = syscall.AF_UNSPEC FAMILY_ALL = syscall.AF_UNSPEC
FAMILY_V4 = syscall.AF_INET FAMILY_V4 = syscall.AF_INET
FAMILY_V6 = syscall.AF_INET6 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 var nextSeqNr uint32
// GetIPFamily returns the family type of a net.IP. // GetIPFamily returns the family type of a net.IP.
@ -93,6 +101,147 @@ func (msg *IfInfomsg) Len() int {
return syscall.SizeofIfInfomsg 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 { func rtaAlignOf(attrlen int) int {
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
} }
@ -172,6 +321,7 @@ func (a *RtAttr) Serialize() []byte {
type NetlinkRequest struct { type NetlinkRequest struct {
syscall.NlMsghdr syscall.NlMsghdr
Data []NetlinkRequestData Data []NetlinkRequestData
Sockets map[int]*SocketHandle
} }
// Serialize the Netlink Request into a byte array // 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. // 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. // by resType.
func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) { func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) {
s, err := getNetlinkSocket(sockType) 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 { if err != nil {
return nil, err return nil, err
} }
defer s.Close() defer s.Close()
} else {
s.Lock()
defer s.Unlock()
}
if err := s.Send(req); err != nil { if err := s.Send(req); err != nil {
return nil, err return nil, err
@ -231,7 +399,10 @@ done:
} }
for _, m := range msgs { for _, m := range msgs {
if m.Header.Seq != req.Seq { 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 { if m.Header.Pid != pid {
return nil, fmt.Errorf("Wrong pid %d, expected %d", 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 { type NetlinkSocket struct {
fd int fd int
lsa syscall.SockaddrNetlink lsa syscall.SockaddrNetlink
sync.Mutex
} }
func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
@ -295,6 +467,71 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
return s, nil 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) // Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
// and subscribe it to multicast groups passed in variable argument list. // and subscribe it to multicast groups passed in variable argument list.
// Returns the netlink socket on which Receive() method can be called // 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 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() { func (s *NetlinkSocket) Close() {
syscall.Close(s.fd) syscall.Close(s.fd)
s.fd = -1
} }
func (s *NetlinkSocket) GetFd() int { func (s *NetlinkSocket) GetFd() int {
@ -330,6 +580,9 @@ func (s *NetlinkSocket) GetFd() int {
} }
func (s *NetlinkSocket) Send(request *NetlinkRequest) error { 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 { if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil {
return err return err
} }
@ -337,6 +590,9 @@ func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
} }
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, 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()) rb := make([]byte, syscall.Getpagesize())
nr, _, err := syscall.Recvfrom(s.fd, rb, 0) nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
if err != nil { if err != nil {
@ -401,6 +657,13 @@ func Uint32Attr(v uint32) []byte {
return bytes 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) { func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) {
var attrs []syscall.NetlinkRouteAttr var attrs []syscall.NetlinkRouteAttr
for len(b) >= syscall.SizeofRtAttr { 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 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()
}
}

View File

@ -0,0 +1,11 @@
// +build !linux
package nl
import "encoding/binary"
var SupportedNlFamilies = []int{}
func NativeEndian() binary.ByteOrder {
return nil
}

View File

@ -40,3 +40,41 @@ func DeserializeRtMsg(b []byte) *RtMsg {
func (msg *RtMsg) Serialize() []byte { func (msg *RtMsg) Serialize() []byte {
return (*(*[syscall.SizeofRtMsg]byte)(unsafe.Pointer(msg)))[:] 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
}

View File

@ -35,3 +35,34 @@ const (
FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */ FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */
FR_ACT_PROHIBIT /* Drop with EACCES */ 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
)

View File

@ -49,6 +49,15 @@ const (
TCAA_MAX = 1 TCAA_MAX = 1
) )
const (
TCA_ACT_UNSPEC = iota
TCA_ACT_KIND
TCA_ACT_OPTIONS
TCA_ACT_INDEX
TCA_ACT_STATS
TCA_ACT_MAX
)
const ( const (
TCA_PRIO_UNSPEC = iota TCA_PRIO_UNSPEC = iota
TCA_PRIO_MQ TCA_PRIO_MQ
@ -69,7 +78,8 @@ const (
SizeofTcHtbGlob = 0x14 SizeofTcHtbGlob = 0x14
SizeofTcU32Key = 0x10 SizeofTcU32Key = 0x10
SizeofTcU32Sel = 0x10 // without keys SizeofTcU32Sel = 0x10 // without keys
SizeofTcMirred = 0x1c SizeofTcGen = 0x14
SizeofTcMirred = SizeofTcGen + 0x08
SizeofTcPolice = 2*SizeofTcRateSpec + 0x20 SizeofTcPolice = 2*SizeofTcRateSpec + 0x20
) )
@ -506,6 +516,81 @@ func (x *TcU32Sel) Serialize() []byte {
return buf 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 ( const (
TCA_ACT_MIRRED = 8 TCA_ACT_MIRRED = 8
) )
@ -517,31 +602,6 @@ const (
TCA_MIRRED_MAX = TCA_MIRRED_PARMS 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 { // struct tc_mirred {
// tc_gen; // tc_gen;
// int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ // int eaction; /* one of IN/EGRESS_MIRROR/REDIR */
@ -549,11 +609,7 @@ const (
// }; // };
type TcMirred struct { type TcMirred struct {
Index uint32 TcGen
Capab uint32
Action int32
Refcnt int32
Bindcnt int32
Eaction int32 Eaction int32
Ifindex uint32 Ifindex uint32
} }
@ -570,14 +626,6 @@ func (x *TcMirred) Serialize() []byte {
return (*(*[SizeofTcMirred]byte)(unsafe.Pointer(x)))[:] 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 { // struct tc_police {
// __u32 index; // __u32 index;
// int action; // int action;

View File

@ -11,9 +11,15 @@ const (
XFRM_INF = ^uint64(0) XFRM_INF = ^uint64(0)
) )
type XfrmMsgType uint8
type XfrmMsg interface {
Type() XfrmMsgType
}
// Message Types // Message Types
const ( const (
XFRM_MSG_BASE = 0x10 XFRM_MSG_BASE XfrmMsgType = 0x10
XFRM_MSG_NEWSA = 0x10 XFRM_MSG_NEWSA = 0x10
XFRM_MSG_DELSA = 0x11 XFRM_MSG_DELSA = 0x11
XFRM_MSG_GETSA = 0x12 XFRM_MSG_GETSA = 0x12
@ -78,6 +84,21 @@ const (
SizeofXfrmLifetimeCfg = 0x40 SizeofXfrmLifetimeCfg = 0x40
SizeofXfrmLifetimeCur = 0x20 SizeofXfrmLifetimeCur = 0x20
SizeofXfrmId = 0x18 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 { // typedef union {
@ -256,3 +277,20 @@ func DeserializeXfrmId(b []byte) *XfrmId {
func (msg *XfrmId) Serialize() []byte { func (msg *XfrmId) Serialize() []byte {
return (*(*[SizeofXfrmId]byte)(unsafe.Pointer(msg)))[:] 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)))[:]
}

View File

@ -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)))[:]
}

View File

@ -8,9 +8,24 @@ const (
SizeofXfrmUsersaId = 0x18 SizeofXfrmUsersaId = 0x18
SizeofXfrmStats = 0x0c SizeofXfrmStats = 0x0c
SizeofXfrmUsersaInfo = 0xe0 SizeofXfrmUsersaInfo = 0xe0
SizeofXfrmUserSpiInfo = 0xe8
SizeofXfrmAlgo = 0x44 SizeofXfrmAlgo = 0x44
SizeofXfrmAlgoAuth = 0x48 SizeofXfrmAlgoAuth = 0x48
SizeofXfrmAlgoAEAD = 0x48
SizeofXfrmEncapTmpl = 0x18 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 { // struct xfrm_usersa_id {
@ -118,6 +133,30 @@ func (msg *XfrmUsersaInfo) Serialize() []byte {
return (*(*[SizeofXfrmUsersaInfo]byte)(unsafe.Pointer(msg)))[:] 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 { // struct xfrm_algo {
// char alg_name[64]; // char alg_name[64];
// unsigned int alg_key_len; /* in bits */ // unsigned int alg_key_len; /* in bits */
@ -193,6 +232,35 @@ func (msg *XfrmAlgoAuth) Serialize() []byte {
// char alg_key[0]; // 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 { // struct xfrm_encap_tmpl {
// __u16 encap_type; // __u16 encap_type;
// __be16 encap_sport; // __be16 encap_sport;
@ -219,3 +287,48 @@ func DeserializeXfrmEncapTmpl(b []byte) *XfrmEncapTmpl {
func (msg *XfrmEncapTmpl) Serialize() []byte { func (msg *XfrmEncapTmpl) Serialize() []byte {
return (*(*[SizeofXfrmEncapTmpl]byte)(unsafe.Pointer(msg)))[:] 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)))[:]
}

32
vendor/github.com/vishvananda/netlink/order.go generated vendored Normal file
View File

@ -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)
}

View File

@ -46,8 +46,5 @@ func boolToByte(x bool) []byte {
} }
func byteToBool(x byte) bool { func byteToBool(x byte) bool {
if uint8(x) != 0 { return uint8(x) != 0
return true
}
return false
} }

View File

@ -8,10 +8,14 @@ import (
) )
func LinkGetProtinfo(link Link) (Protinfo, error) { func LinkGetProtinfo(link Link) (Protinfo, error) {
return pkgHandle.LinkGetProtinfo(link)
}
func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
var pi Protinfo 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) msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
req.AddData(msg) req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0) msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0)
@ -36,6 +40,15 @@ func LinkGetProtinfo(link Link) (Protinfo, error) {
if err != nil { if err != nil {
return pi, err return pi, err
} }
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 var pi Protinfo
for _, info := range infos { for _, info := range infos {
switch info.Attr.Type { switch info.Attr.Type {
@ -53,8 +66,5 @@ func LinkGetProtinfo(link Link) (Protinfo, error) {
pi.Flood = byteToBool(info.Value[0]) pi.Flood = byteToBool(info.Value[0])
} }
} }
return pi, nil return &pi
}
}
return pi, fmt.Errorf("Device with index %d not found", base.Index)
} }

View File

@ -8,16 +8,21 @@ import (
const ( const (
HANDLE_NONE = 0 HANDLE_NONE = 0
HANDLE_INGRESS = 0xFFFFFFF1 HANDLE_INGRESS = 0xFFFFFFF1
HANDLE_CLSACT = HANDLE_INGRESS
HANDLE_ROOT = 0xFFFFFFFF HANDLE_ROOT = 0xFFFFFFFF
PRIORITY_MAP_LEN = 16 PRIORITY_MAP_LEN = 16
) )
const (
HANDLE_MIN_INGRESS = 0xFFFFFFF2
HANDLE_MIN_EGRESS = 0xFFFFFFF3
)
type Qdisc interface { type Qdisc interface {
Attrs() *QdiscAttrs Attrs() *QdiscAttrs
Type() string 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 // has a handle, a parent and a refcnt. The root qdisc of a device should
// have parent == HANDLE_ROOT. // have parent == HANDLE_ROOT.
type QdiscAttrs struct { type QdiscAttrs struct {
@ -28,7 +33,7 @@ type QdiscAttrs struct {
} }
func (q QdiscAttrs) String() string { 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 { func MakeHandle(major, minor uint16) uint32 {
@ -149,7 +154,7 @@ type NetemQdiscAttrs struct {
func (q NetemQdiscAttrs) String() string { func (q NetemQdiscAttrs) String() string {
return fmt.Sprintf( 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, q.Latency, q.Limit, q.Loss, q.Gap, q.Duplicate, q.Jitter,
) )
} }
@ -171,70 +176,6 @@ type Netem struct {
CorruptCorr uint32 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 { func (qdisc *Netem) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs return &qdisc.QdiscAttrs
} }
@ -246,10 +187,11 @@ func (qdisc *Netem) Type() string {
// Tbf is a classless qdisc that rate limits based on tokens // Tbf is a classless qdisc that rate limits based on tokens
type Tbf struct { type Tbf struct {
QdiscAttrs QdiscAttrs
// TODO: handle 64bit rate properly
Rate uint64 Rate uint64
Limit uint32 Limit uint32
Buffer uint32 Buffer uint32
Peakrate uint64
Minburst uint32
// TODO: handle other settings // TODO: handle other settings
} }

View File

@ -10,24 +10,109 @@ import (
"github.com/vishvananda/netlink/nl" "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. // QdiscDel will delete a qdisc from the system.
// Equivalent to: `tc qdisc del $qdisc` // Equivalent to: `tc qdisc del $qdisc`
func QdiscDel(qdisc Qdisc) error { 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 // QdiscChange will change a qdisc in place
// Equivalent to: `tc qdisc change $qdisc` // Equivalent to: `tc qdisc change $qdisc`
// The parent and handle MUST NOT be changed. // The parent and handle MUST NOT be changed.
func QdiscChange(qdisc Qdisc) error { 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. // QdiscReplace will replace a qdisc to the system.
// Equivalent to: `tc qdisc replace $qdisc` // Equivalent to: `tc qdisc replace $qdisc`
// The handle MUST change. // The handle MUST change.
func QdiscReplace(qdisc Qdisc) error { 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.RTM_NEWQDISC,
syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE,
qdisc) qdisc)
@ -36,14 +121,20 @@ func QdiscReplace(qdisc Qdisc) error {
// QdiscAdd will add a qdisc to the system. // QdiscAdd will add a qdisc to the system.
// Equivalent to: `tc qdisc add $qdisc` // Equivalent to: `tc qdisc add $qdisc`
func QdiscAdd(qdisc Qdisc) error { 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.RTM_NEWQDISC,
syscall.NLM_F_CREATE|syscall.NLM_F_EXCL, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
qdisc) qdisc)
} }
func qdiscModify(cmd, flags int, qdisc Qdisc) error { func (h *Handle) qdiscModify(cmd, flags int, qdisc Qdisc) error {
req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK) req := h.newNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
base := qdisc.Attrs() base := qdisc.Attrs()
msg := &nl.TcMsg{ msg := &nl.TcMsg{
Family: nl.FAMILY_ALL, Family: nl.FAMILY_ALL,
@ -77,11 +168,20 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize()) options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize())
} else if tbf, ok := qdisc.(*Tbf); ok { } else if tbf, ok := qdisc.(*Tbf); ok {
opt := nl.TcTbfQopt{} opt := nl.TcTbfQopt{}
// TODO: handle rate > uint32
opt.Rate.Rate = uint32(tbf.Rate) opt.Rate.Rate = uint32(tbf.Rate)
opt.Peakrate.Rate = uint32(tbf.Peakrate)
opt.Limit = tbf.Limit opt.Limit = tbf.Limit
opt.Buffer = tbf.Buffer opt.Buffer = tbf.Buffer
nl.NewRtAttrChild(options, nl.TCA_TBF_PARMS, opt.Serialize()) 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 { } else if htb, ok := qdisc.(*Htb); ok {
opt := nl.TcHtbGlob{} opt := nl.TcHtbGlob{}
opt.Version = htb.Version opt.Version = htb.Version
@ -139,11 +239,18 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
// Equivalent to: `tc qdisc show`. // Equivalent to: `tc qdisc show`.
// The list can be filtered by link. // The list can be filtered by link.
func QdiscList(link Link) ([]Qdisc, error) { 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) index := int32(0)
if link != nil { if link != nil {
base := link.Attrs() base := link.Attrs()
ensureIndex(base) h.ensureIndex(base)
index = int32(base.Index) index = int32(base.Index)
} }
msg := &nl.TcMsg{ msg := &nl.TcMsg{
@ -320,10 +427,15 @@ func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
case nl.TCA_TBF_PARMS: case nl.TCA_TBF_PARMS:
opt := nl.DeserializeTcTbfQopt(datum.Value) opt := nl.DeserializeTcTbfQopt(datum.Value)
tbf.Rate = uint64(opt.Rate.Rate) tbf.Rate = uint64(opt.Rate.Rate)
tbf.Peakrate = uint64(opt.Peakrate.Rate)
tbf.Limit = opt.Limit tbf.Limit = opt.Limit
tbf.Buffer = opt.Buffer tbf.Buffer = opt.Buffer
case nl.TCA_TBF_RATE64: 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 return nil
@ -334,9 +446,9 @@ const (
) )
var ( var (
tickInUsec float64 = 0.0 tickInUsec float64
clockFactor float64 = 0.0 clockFactor float64
hz float64 = 0.0 hz float64
) )
func initClock() { func initClock() {

View File

@ -3,26 +3,27 @@ package netlink
import ( import (
"fmt" "fmt"
"net" "net"
"syscall" "strings"
) )
// Scope is an enum representing a route scope. // Scope is an enum representing a route scope.
type Scope uint8 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 type NextHopFlag int
const ( type Destination interface {
FLAG_ONLINK NextHopFlag = syscall.RTNH_F_ONLINK Family() int
FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE 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. // Route represents a netlink route.
type Route struct { type Route struct {
@ -32,17 +33,43 @@ type Route struct {
Dst *net.IPNet Dst *net.IPNet
Src net.IP Src net.IP
Gw net.IP Gw net.IP
MultiPath []*NexthopInfo
Protocol int Protocol int
Priority int Priority int
Table int Table int
Type int Type int
Tos int Tos int
Flags int Flags int
MPLSDst *int
NewDst Destination
Encap Encap
} }
func (r Route) String() string { func (r Route) String() string {
return fmt.Sprintf("{Ifindex: %d Dst: %s Src: %s Gw: %s Flags: %s}", r.LinkIndex, r.Dst, elems := []string{}
r.Src, r.Gw, r.ListFlags()) 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) { func (r *Route) SetFlag(flag NextHopFlag) {
@ -58,23 +85,32 @@ type flagString struct {
s string 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 // RouteUpdate is sent when a route changes - type is RTM_NEWROUTE or RTM_DELROUTE
type RouteUpdate struct { type RouteUpdate struct {
Type uint16 Type uint16
Route 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, " "))
}

View File

@ -3,13 +3,23 @@ package netlink
import ( import (
"fmt" "fmt"
"net" "net"
"strings"
"syscall" "syscall"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
) )
// RtAttr is shared so it is in netlink_linux.go // 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 ( const (
RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota) RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
RT_FILTER_SCOPE RT_FILTER_SCOPE
@ -23,22 +33,145 @@ const (
RT_FILTER_TABLE 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. // RouteAdd will add a route to the system.
// Equivalent to: `ip route add $route` // Equivalent to: `ip route add $route`
func RouteAdd(route *Route) error { func RouteAdd(route *Route) error {
req := nl.NewNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) return pkgHandle.RouteAdd(route)
return routeHandle(route, req, nl.NewRtMsg()) }
// 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. // RouteDel will delete a route from the system.
// Equivalent to: `ip route del $route` // Equivalent to: `ip route del $route`
func RouteDel(route *Route) error { func RouteDel(route *Route) error {
req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK) return pkgHandle.RouteDel(route)
return routeHandle(route, req, nl.NewRtDelMsg())
} }
func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { // RouteDel will delete a route from the system.
if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil { // 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") 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() dstData = route.Dst.IP.To16()
} }
rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData)) 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 { 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)) 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 > 0 {
if route.Table >= 256 { if route.Table >= 256 {
msg.Table = syscall.RT_TABLE_UNSPEC 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.Type = uint8(route.Type)
} }
msg.Flags = uint32(route.Flags)
msg.Scope = uint8(route.Scope) msg.Scope = uint8(route.Scope)
msg.Family = uint8(family) msg.Family = uint8(family)
req.AddData(msg) req.AddData(msg)
@ -139,19 +348,32 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
// Equivalent to: `ip route show`. // Equivalent to: `ip route show`.
// The list can be filtered by link and ip family. // The list can be filtered by link and ip family.
func RouteList(link Link, family int) ([]Route, error) { 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 var routeFilter *Route
if link != nil { if link != nil {
routeFilter = &Route{ routeFilter = &Route{
LinkIndex: link.Attrs().Index, 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. // RouteListFiltered gets a list of routes in the system filtered with specified rules.
// All rules must be defined in RouteFilter struct // All rules must be defined in RouteFilter struct
func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) { 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) infmsg := nl.NewIfInfomsg(family)
req.AddData(infmsg) req.AddData(infmsg)
@ -197,7 +419,13 @@ func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, e
continue continue
case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src): case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
continue continue
case filterMask&RT_FILTER_DST != 0 && filter.Dst != nil: 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 { if route.Dst == nil {
continue continue
} }
@ -208,6 +436,8 @@ func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, e
} }
} }
} }
}
}
res = append(res, route) res = append(res, route)
} }
return res, nil return res, nil
@ -230,6 +460,7 @@ func deserializeRoute(m []byte) (Route, error) {
} }
native := nl.NativeEndian() native := nl.NativeEndian()
var encap, encapType syscall.NetlinkRouteAttr
for _, attr := range attrs { for _, attr := range attrs {
switch attr.Attr.Type { switch attr.Attr.Type {
case syscall.RTA_GATEWAY: case syscall.RTA_GATEWAY:
@ -237,10 +468,18 @@ func deserializeRoute(m []byte) (Route, error) {
case syscall.RTA_PREFSRC: case syscall.RTA_PREFSRC:
route.Src = net.IP(attr.Value) route.Src = net.IP(attr.Value)
case syscall.RTA_DST: case syscall.RTA_DST:
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{ route.Dst = &net.IPNet{
IP: attr.Value, IP: attr.Value,
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
} }
}
case syscall.RTA_OIF: case syscall.RTA_OIF:
route.LinkIndex = int(native.Uint32(attr.Value[0:4])) route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
case syscall.RTA_IIF: case syscall.RTA_IIF:
@ -249,15 +488,113 @@ func deserializeRoute(m []byte) (Route, error) {
route.Priority = int(native.Uint32(attr.Value[0:4])) route.Priority = int(native.Uint32(attr.Value[0:4]))
case syscall.RTA_TABLE: case syscall.RTA_TABLE:
route.Table = int(native.Uint32(attr.Value[0:4])) 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 return route, nil
} }
// RouteGet gets a route to a specific destination from the host system. // RouteGet gets a route to a specific destination from the host system.
// Equivalent to: 'ip route get'. // Equivalent to: 'ip route get'.
func RouteGet(destination net.IP) ([]Route, error) { 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) family := nl.GetIPFamily(destination)
var destinationData []byte var destinationData []byte
var bitlen uint8 var bitlen uint8
@ -296,7 +633,17 @@ func RouteGet(destination net.IP) ([]Route, error) {
// RouteSubscribe takes a chan down which notifications will be sent // RouteSubscribe takes a chan down which notifications will be sent
// when routes are added or deleted. Close the 'done' chan to stop subscription. // when routes are added or deleted. Close the 'done' chan to stop subscription.
func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error { 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 { if err != nil {
return err return err
} }

View File

@ -0,0 +1,11 @@
// +build !linux
package netlink
func (r *Route) ListFlags() []string {
return []string{}
}
func (n *NexthopInfo) ListFlags() []string {
return []string{}
}

View File

@ -3,13 +3,10 @@ package netlink
import ( import (
"fmt" "fmt"
"net" "net"
"github.com/vishvananda/netlink/nl"
) )
// Rule represents a netlink rule. // Rule represents a netlink rule.
type Rule struct { type Rule struct {
*nl.RtMsg
Priority int Priority int
Table int Table int
Mark int Mark int

View File

@ -11,14 +11,26 @@ import (
// RuleAdd adds a rule to the system. // RuleAdd adds a rule to the system.
// Equivalent to: ip rule add // Equivalent to: ip rule add
func RuleAdd(rule *Rule) error { 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) return ruleHandle(rule, req)
} }
// RuleDel deletes a rule from the system. // RuleDel deletes a rule from the system.
// Equivalent to: ip rule del // Equivalent to: ip rule del
func RuleDel(rule *Rule) error { 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) return ruleHandle(rule, req)
} }
@ -70,41 +82,46 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
req.AddData(rtAttrs[i]) req.AddData(rtAttrs[i])
} }
var ( native := nl.NativeEndian()
b = make([]byte, 4)
native = nl.NativeEndian()
)
if rule.Priority >= 0 { if rule.Priority >= 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Priority)) native.PutUint32(b, uint32(rule.Priority))
req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b)) req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b))
} }
if rule.Mark >= 0 { if rule.Mark >= 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Mark)) native.PutUint32(b, uint32(rule.Mark))
req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b)) req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b))
} }
if rule.Mask >= 0 { if rule.Mask >= 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Mask)) native.PutUint32(b, uint32(rule.Mask))
req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b)) req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b))
} }
if rule.Flow >= 0 { if rule.Flow >= 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Flow)) native.PutUint32(b, uint32(rule.Flow))
req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b)) req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b))
} }
if rule.TunID > 0 { if rule.TunID > 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.TunID)) native.PutUint32(b, uint32(rule.TunID))
req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b)) req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b))
} }
if rule.Table >= 256 { if rule.Table >= 256 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Table)) native.PutUint32(b, uint32(rule.Table))
req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b)) req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b))
} }
if msg.Table > 0 { if msg.Table > 0 {
if rule.SuppressPrefixlen >= 0 { if rule.SuppressPrefixlen >= 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.SuppressPrefixlen)) native.PutUint32(b, uint32(rule.SuppressPrefixlen))
req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b)) req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b))
} }
if rule.SuppressIfgroup >= 0 { if rule.SuppressIfgroup >= 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.SuppressIfgroup)) native.PutUint32(b, uint32(rule.SuppressIfgroup))
req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b)) 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 { if rule.Goto >= 0 {
msg.Type = nl.FR_ACT_NOP msg.Type = nl.FR_ACT_NOP
b := make([]byte, 4)
native.PutUint32(b, uint32(rule.Goto)) native.PutUint32(b, uint32(rule.Goto))
req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b)) 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. // RuleList lists rules in the system.
// Equivalent to: ip rule list // Equivalent to: ip rule list
func RuleList(family int) ([]Rule, error) { 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) msg := nl.NewIfInfomsg(family)
req.AddData(msg) req.AddData(msg)
@ -147,7 +171,6 @@ func RuleList(family int) ([]Rule, error) {
} }
rule := NewRule() rule := NewRule()
rule.RtMsg = msg
for j := range attrs { for j := range attrs {
switch attrs[j].Attr.Type { switch attrs[j].Attr.Type {

27
vendor/github.com/vishvananda/netlink/socket.go generated vendored Normal file
View File

@ -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
}

159
vendor/github.com/vishvananda/netlink/socket_linux.go generated vendored Normal file
View File

@ -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
}

View File

@ -13,7 +13,7 @@ const (
XFRM_PROTO_ESP Proto = syscall.IPPROTO_ESP XFRM_PROTO_ESP Proto = syscall.IPPROTO_ESP
XFRM_PROTO_AH Proto = syscall.IPPROTO_AH XFRM_PROTO_AH Proto = syscall.IPPROTO_AH
XFRM_PROTO_HAO Proto = syscall.IPPROTO_DSTOPTS 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 XFRM_PROTO_IPSEC_ANY Proto = syscall.IPPROTO_RAW
) )
@ -62,3 +62,13 @@ func (m Mode) String() string {
} }
return fmt.Sprintf("%d", m) 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)
}

View File

@ -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
}

View File

@ -43,17 +43,32 @@ type XfrmPolicyTmpl struct {
Src net.IP Src net.IP
Proto Proto Proto Proto
Mode Mode Mode Mode
Spi int
Reqid 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 // XfrmPolicy represents an ipsec policy. It represents the overlay network
// and has a list of XfrmPolicyTmpls representing the base addresses of // and has a list of XfrmPolicyTmpls representing the base addresses of
// the policy. // the policy.
type XfrmPolicy struct { type XfrmPolicy struct {
Dst *net.IPNet Dst *net.IPNet
Src *net.IPNet Src *net.IPNet
Proto Proto
DstPort int
SrcPort int
Dir Dir Dir Dir
Priority int Priority int
Index int Index int
Mark *XfrmMark
Tmpls []XfrmPolicyTmpl 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)
}

View File

@ -7,19 +7,55 @@ import (
) )
func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) { func selFromPolicy(sel *nl.XfrmSelector, policy *XfrmPolicy) {
sel.Family = uint16(nl.FAMILY_V4)
if policy.Dst != nil {
sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP)) sel.Family = uint16(nl.GetIPFamily(policy.Dst.IP))
sel.Daddr.FromIP(policy.Dst.IP) sel.Daddr.FromIP(policy.Dst.IP)
sel.Saddr.FromIP(policy.Src.IP)
prefixlenD, _ := policy.Dst.Mask.Size() prefixlenD, _ := policy.Dst.Mask.Size()
sel.PrefixlenD = uint8(prefixlenD) sel.PrefixlenD = uint8(prefixlenD)
}
if policy.Src != nil {
sel.Saddr.FromIP(policy.Src.IP)
prefixlenS, _ := policy.Src.Mask.Size() prefixlenS, _ := policy.Src.Mask.Size()
sel.PrefixlenS = uint8(prefixlenS) 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. // XfrmPolicyAdd will add an xfrm policy to the system.
// Equivalent to: `ip xfrm policy add $policy` // Equivalent to: `ip xfrm policy add $policy`
func XfrmPolicyAdd(policy *XfrmPolicy) error { 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{} msg := &nl.XfrmUserpolicyInfo{}
selFromPolicy(&msg.Sel, policy) selFromPolicy(&msg.Sel, policy)
@ -39,6 +75,7 @@ func XfrmPolicyAdd(policy *XfrmPolicy) error {
userTmpl.XfrmId.Daddr.FromIP(tmpl.Dst) userTmpl.XfrmId.Daddr.FromIP(tmpl.Dst)
userTmpl.Saddr.FromIP(tmpl.Src) userTmpl.Saddr.FromIP(tmpl.Src)
userTmpl.XfrmId.Proto = uint8(tmpl.Proto) userTmpl.XfrmId.Proto = uint8(tmpl.Proto)
userTmpl.XfrmId.Spi = nl.Swap32(uint32(tmpl.Spi))
userTmpl.Mode = uint8(tmpl.Mode) userTmpl.Mode = uint8(tmpl.Mode)
userTmpl.Reqid = uint32(tmpl.Reqid) userTmpl.Reqid = uint32(tmpl.Reqid)
userTmpl.Aalgos = ^uint32(0) userTmpl.Aalgos = ^uint32(0)
@ -49,6 +86,10 @@ func XfrmPolicyAdd(policy *XfrmPolicy) error {
tmpls := nl.NewRtAttr(nl.XFRMA_TMPL, tmplData) tmpls := nl.NewRtAttr(nl.XFRMA_TMPL, tmplData)
req.AddData(tmpls) 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) _, err := req.Execute(syscall.NETLINK_XFRM, 0)
return err return err
@ -58,15 +99,14 @@ func XfrmPolicyAdd(policy *XfrmPolicy) error {
// the Tmpls are ignored when matching the policy to delete. // the Tmpls are ignored when matching the policy to delete.
// Equivalent to: `ip xfrm policy del $policy` // Equivalent to: `ip xfrm policy del $policy`
func XfrmPolicyDel(policy *XfrmPolicy) error { func XfrmPolicyDel(policy *XfrmPolicy) error {
req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELPOLICY, syscall.NLM_F_ACK) return pkgHandle.XfrmPolicyDel(policy)
}
msg := &nl.XfrmUserpolicyId{} // XfrmPolicyDel will delete an xfrm policy from the system. Note that
selFromPolicy(&msg.Sel, policy) // the Tmpls are ignored when matching the policy to delete.
msg.Index = uint32(policy.Index) // Equivalent to: `ip xfrm policy del $policy`
msg.Dir = uint8(policy.Dir) func (h *Handle) XfrmPolicyDel(policy *XfrmPolicy) error {
req.AddData(msg) _, err := h.xfrmPolicyGetOrDelete(policy, nl.XFRM_MSG_DELPOLICY)
_, err := req.Execute(syscall.NETLINK_XFRM, 0)
return err return err
} }
@ -74,7 +114,14 @@ func XfrmPolicyDel(policy *XfrmPolicy) error {
// Equivalent to: `ip xfrm policy show`. // Equivalent to: `ip xfrm policy show`.
// The list can be filtered by ip family. // The list can be filtered by ip family.
func XfrmPolicyList(family int) ([]XfrmPolicy, error) { 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) msg := nl.NewIfInfomsg(family)
req.AddData(msg) req.AddData(msg)
@ -86,16 +133,94 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
var res []XfrmPolicy var res []XfrmPolicy
for _, m := range msgs { for _, m := range msgs {
if policy, err := parseXfrmPolicy(m, family); err == nil {
res = append(res, *policy)
} else if err == familyError {
continue
} else {
return nil, err
}
}
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) msg := nl.DeserializeXfrmUserpolicyInfo(m)
// This is mainly for the policy dump
if family != FAMILY_ALL && family != int(msg.Sel.Family) { if family != FAMILY_ALL && family != int(msg.Sel.Family) {
continue return nil, familyError
} }
var policy XfrmPolicy var policy XfrmPolicy
policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD) policy.Dst = msg.Sel.Daddr.ToIPNet(msg.Sel.PrefixlenD)
policy.Src = msg.Sel.Saddr.ToIPNet(msg.Sel.PrefixlenS) 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.Priority = int(msg.Priority)
policy.Index = int(msg.Index) policy.Index = int(msg.Index)
policy.Dir = Dir(msg.Dir) policy.Dir = Dir(msg.Dir)
@ -116,12 +241,17 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
resTmpl.Src = tmpl.Saddr.ToIP() resTmpl.Src = tmpl.Saddr.ToIP()
resTmpl.Proto = Proto(tmpl.XfrmId.Proto) resTmpl.Proto = Proto(tmpl.XfrmId.Proto)
resTmpl.Mode = Mode(tmpl.Mode) resTmpl.Mode = Mode(tmpl.Mode)
resTmpl.Spi = int(nl.Swap32(tmpl.XfrmId.Spi))
resTmpl.Reqid = int(tmpl.Reqid) resTmpl.Reqid = int(tmpl.Reqid)
policy.Tmpls = append(policy.Tmpls, resTmpl) 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
} }
} }
res = append(res, policy)
} return &policy, nil
return res, nil
} }

View File

@ -1,6 +1,7 @@
package netlink package netlink
import ( import (
"fmt"
"net" "net"
) )
@ -9,9 +10,21 @@ type XfrmStateAlgo struct {
Name string Name string
Key []byte Key []byte
TruncateLen int // Auth only 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 type EncapType uint8
const ( const (
@ -22,14 +35,14 @@ const (
func (e EncapType) String() string { func (e EncapType) String() string {
switch e { switch e {
case XFRM_ENCAP_ESPINUDP_NONIKE: case XFRM_ENCAP_ESPINUDP_NONIKE:
return "espinudp-nonike" return "espinudp-non-ike"
case XFRM_ENCAP_ESPINUDP: case XFRM_ENCAP_ESPINUDP:
return "espinudp" return "espinudp"
} }
return "unknown" 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 XfrmStateEncap struct {
Type EncapType Type EncapType
SrcPort int SrcPort int
@ -37,6 +50,23 @@ type XfrmStateEncap struct {
OriginalAddress net.IP 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 // XfrmState represents the state of an ipsec policy. It optionally
// contains an XfrmStateAlgo for encryption and one for authentication. // contains an XfrmStateAlgo for encryption and one for authentication.
type XfrmState struct { type XfrmState struct {
@ -47,7 +77,32 @@ type XfrmState struct {
Spi int Spi int
Reqid int Reqid int
ReplayWindow int ReplayWindow int
Limits XfrmStateLimits
Mark *XfrmMark
Auth *XfrmStateAlgo Auth *XfrmStateAlgo
Crypt *XfrmStateAlgo Crypt *XfrmStateAlgo
Aead *XfrmStateAlgo
Encap *XfrmStateEncap 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)
} }

View File

@ -3,6 +3,7 @@ package netlink
import ( import (
"fmt" "fmt"
"syscall" "syscall"
"unsafe"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
) )
@ -34,28 +35,95 @@ func writeStateAlgoAuth(a *XfrmStateAlgo) []byte {
return algo.Serialize() 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. // XfrmStateAdd will add an xfrm state to the system.
// Equivalent to: `ip xfrm state add $state` // Equivalent to: `ip xfrm state add $state`
func XfrmStateAdd(state *XfrmState) error { 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 // A state with spi 0 can't be deleted so don't allow it to be set
if state.Spi == 0 { if state.Spi == 0 {
return fmt.Errorf("Spi must be set when adding xfrm state.") 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 := xfrmUsersaInfoFromXfrmState(state)
msg.Family = uint16(nl.GetIPFamily(state.Dst))
msg.Id.Daddr.FromIP(state.Dst) if state.ESN {
msg.Saddr.FromIP(state.Src) if state.ReplayWindow == 0 {
msg.Id.Proto = uint8(state.Proto) return fmt.Errorf("ESN flag set without ReplayWindow")
msg.Mode = uint8(state.Mode) }
msg.Id.Spi = nl.Swap32(uint32(state.Spi)) msg.Flags |= nl.XFRM_STATE_ESN
msg.Reqid = uint32(state.Reqid) msg.ReplayWindow = 0
msg.ReplayWindow = uint8(state.ReplayWindow) }
msg.Lft.SoftByteLimit = nl.XFRM_INF
msg.Lft.HardByteLimit = nl.XFRM_INF limitsToLft(state.Limits, &msg.Lft)
msg.Lft.SoftPacketLimit = nl.XFRM_INF
msg.Lft.HardPacketLimit = nl.XFRM_INF
req.AddData(msg) req.AddData(msg)
if state.Auth != nil { if state.Auth != nil {
@ -66,6 +134,10 @@ func XfrmStateAdd(state *XfrmState) error {
out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt)) out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt))
req.AddData(out) req.AddData(out)
} }
if state.Aead != nil {
out := nl.NewRtAttr(nl.XFRMA_ALG_AEAD, writeStateAlgoAead(state.Aead))
req.AddData(out)
}
if state.Encap != nil { if state.Encap != nil {
encapData := make([]byte, nl.SizeofXfrmEncapTmpl) encapData := make([]byte, nl.SizeofXfrmEncapTmpl)
encap := nl.DeserializeXfrmEncapTmpl(encapData) encap := nl.DeserializeXfrmEncapTmpl(encapData)
@ -76,39 +148,75 @@ func XfrmStateAdd(state *XfrmState) error {
out := nl.NewRtAttr(nl.XFRMA_ENCAP, encapData) out := nl.NewRtAttr(nl.XFRMA_ENCAP, encapData)
req.AddData(out) 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) _, err := req.Execute(syscall.NETLINK_XFRM, 0)
return err 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 // XfrmStateDel will delete an xfrm state from the system. Note that
// the Algos are ignored when matching the state to delete. // the Algos are ignored when matching the state to delete.
// Equivalent to: `ip xfrm state del $state` // Equivalent to: `ip xfrm state del $state`
func XfrmStateDel(state *XfrmState) error { func XfrmStateDel(state *XfrmState) error {
req := nl.NewNetlinkRequest(nl.XFRM_MSG_DELSA, syscall.NLM_F_ACK) return pkgHandle.XfrmStateDel(state)
}
msg := &nl.XfrmUsersaId{} // XfrmStateDel will delete an xfrm state from the system. Note that
msg.Daddr.FromIP(state.Dst) // the Algos are ignored when matching the state to delete.
msg.Family = uint16(nl.GetIPFamily(state.Dst)) // Equivalent to: `ip xfrm state del $state`
msg.Proto = uint8(state.Proto) func (h *Handle) XfrmStateDel(state *XfrmState) error {
msg.Spi = nl.Swap32(uint32(state.Spi)) _, err := h.xfrmStateGetOrDelete(state, nl.XFRM_MSG_DELSA)
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)
return err 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. // XfrmStateList gets a list of xfrm states in the system.
// Equivalent to: `ip xfrm state show`. // Equivalent to: `ip xfrm state show`.
// The list can be filtered by ip family. // The list can be filtered by ip family.
func XfrmStateList(family int) ([]XfrmState, error) { func (h *Handle) XfrmStateList(family int) ([]XfrmState, error) {
req := nl.NewNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP) req := h.newNetlinkRequest(nl.XFRM_MSG_GETSA, syscall.NLM_F_DUMP)
msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWSA) msgs, err := req.Execute(syscall.NETLINK_XFRM, nl.XFRM_MSG_NEWSA)
if err != nil { if err != nil {
@ -117,12 +225,79 @@ func XfrmStateList(family int) ([]XfrmState, error) {
var res []XfrmState var res []XfrmState
for _, m := range msgs { for _, m := range msgs {
msg := nl.DeserializeXfrmUsersaInfo(m) if state, err := parseXfrmState(m, family); err == nil {
res = append(res, *state)
if family != FAMILY_ALL && family != int(msg.Family) { } else if err == familyError {
continue continue
} else {
return nil, err
}
}
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 var state XfrmState
state.Dst = msg.Id.Daddr.ToIP() state.Dst = msg.Id.Daddr.ToIP()
@ -132,8 +307,22 @@ func XfrmStateList(family int) ([]XfrmState, error) {
state.Spi = int(nl.Swap32(msg.Id.Spi)) state.Spi = int(nl.Swap32(msg.Id.Spi))
state.Reqid = int(msg.Reqid) state.Reqid = int(msg.Reqid)
state.ReplayWindow = int(msg.ReplayWindow) state.ReplayWindow = int(msg.ReplayWindow)
lftToLimits(&msg.Lft, &state.Limits)
attrs, err := nl.ParseRouteAttr(m[msg.Len():]) 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 { if err != nil {
return nil, err return nil, err
} }
@ -162,6 +351,12 @@ func XfrmStateList(family int) ([]XfrmState, error) {
state.Auth.Name = nl.BytesToString(algo.AlgName[:]) state.Auth.Name = nl.BytesToString(algo.AlgName[:])
state.Auth.Key = algo.AlgKey state.Auth.Key = algo.AlgKey
state.Auth.TruncateLen = int(algo.AlgTruncLen) 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: case nl.XFRMA_ENCAP:
encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:]) encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:])
state.Encap = new(XfrmStateEncap) state.Encap = new(XfrmStateEncap)
@ -169,10 +364,81 @@ func XfrmStateList(family int) ([]XfrmState, error) {
state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport)) state.Encap.SrcPort = int(nl.Swap16(encap.EncapSport))
state.Encap.DstPort = int(nl.Swap16(encap.EncapDport)) state.Encap.DstPort = int(nl.Swap16(encap.EncapDport))
state.Encap.OriginalAddress = encap.EncapOa.ToIP() 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
} }
res = append(res, state)
// 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)
} }
return res, 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 (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
} }

192
vendor/github.com/vishvananda/netns/LICENSE generated vendored Normal file
View File

@ -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.

51
vendor/github.com/vishvananda/netns/README.md generated vendored Normal file
View File

@ -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)
}
```

80
vendor/github.com/vishvananda/netns/netns.go generated vendored Normal file
View File

@ -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)
}

222
vendor/github.com/vishvananda/netns/netns_linux.go generated vendored Normal file
View File

@ -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-<id>.scope/
filepath.Join(cgroupRoot, "system.slice", "docker-"+id+".scope", "tasks"),
// Even more recent docker versions under cgroup/systemd/docker/<id>/
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
}

View File

@ -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
}