Commit 367cdb1a authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #46580 from Huawei-PaaS/kube-proxy-ipvs-pr

Automatic merge from submit-queue (batch tested with PRs 51377, 46580, 50998, 51466, 49749) Implement IPVS-based in-cluster service load balancing **What this PR does / why we need it**: Implement IPVS-based in-cluster service load balancing. It can provide some performance enhancement and some other benefits to kube-proxy while comparing iptables and userspace mode. Besides, it also support more sophisticated load balancing algorithms than iptables (least conns, weighted, hash and so on). **Which issue this PR fixes** #17470 #44063 **Special notes for your reviewer**: * Since the PR is a bit large, I splitted it and move the commits related to ipvs util pkg to PR #48994. Hopefully can make it easier to review. @thockin @quinton-hoole @kevin-wangzefeng @deepak-vij @haibinxie @dhilipkumars @fisherxu **Release note**: ```release-note Implement IPVS-based in-cluster service load balancing ```
parents 759ba487 5ed2b44c
...@@ -1057,6 +1057,10 @@ ...@@ -1057,6 +1057,10 @@
"Rev": "e30f1e79f3cd72542f2026ceec18d3bd67ab859c" "Rev": "e30f1e79f3cd72542f2026ceec18d3bd67ab859c"
}, },
{ {
"ImportPath": "github.com/docker/libnetwork/ipvs",
"Rev": "c3393f1d3e1ba69b596eca45a9f1da9398e17ca5"
},
{
"ImportPath": "github.com/docker/spdystream", "ImportPath": "github.com/docker/spdystream",
"Rev": "449fdfce4d962303d702fec724ef0ad181c92528" "Rev": "449fdfce4d962303d702fec724ef0ad181c92528"
}, },
...@@ -2527,11 +2531,15 @@ ...@@ -2527,11 +2531,15 @@
}, },
{ {
"ImportPath": "github.com/vishvananda/netlink", "ImportPath": "github.com/vishvananda/netlink",
"Rev": "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270" "Rev": "8d7f7aad193e778023f06a843a26ffc9615ca840"
}, },
{ {
"ImportPath": "github.com/vishvananda/netlink/nl", "ImportPath": "github.com/vishvananda/netlink/nl",
"Rev": "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270" "Rev": "8d7f7aad193e778023f06a843a26ffc9615ca840"
},
{
"ImportPath": "github.com/vishvananda/netns",
"Rev": "86bef332bfc3b59b7624a600bd53009ce91a9829"
}, },
{ {
"ImportPath": "github.com/vmware/govmomi", "ImportPath": "github.com/vmware/govmomi",
......
...@@ -21,4 +21,5 @@ CROSS_BUILD_COPY qemu-ARCH-static /usr/bin/ ...@@ -21,4 +21,5 @@ CROSS_BUILD_COPY qemu-ARCH-static /usr/bin/
RUN clean-install \ RUN clean-install \
iptables \ iptables \
ebtables \ ebtables \
conntrack conntrack \
module-init-tools
...@@ -18,6 +18,7 @@ go_library( ...@@ -18,6 +18,7 @@ go_library(
"//pkg/apis/componentconfig/v1alpha1:go_default_library", "//pkg/apis/componentconfig/v1alpha1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubelet/qos:go_default_library", "//pkg/kubelet/qos:go_default_library",
"//pkg/master/ports:go_default_library", "//pkg/master/ports:go_default_library",
...@@ -25,11 +26,13 @@ go_library( ...@@ -25,11 +26,13 @@ go_library(
"//pkg/proxy/config:go_default_library", "//pkg/proxy/config:go_default_library",
"//pkg/proxy/healthcheck:go_default_library", "//pkg/proxy/healthcheck:go_default_library",
"//pkg/proxy/iptables:go_default_library", "//pkg/proxy/iptables:go_default_library",
"//pkg/proxy/ipvs:go_default_library",
"//pkg/proxy/userspace:go_default_library", "//pkg/proxy/userspace:go_default_library",
"//pkg/proxy/winuserspace:go_default_library", "//pkg/proxy/winuserspace:go_default_library",
"//pkg/util/configz:go_default_library", "//pkg/util/configz:go_default_library",
"//pkg/util/dbus:go_default_library", "//pkg/util/dbus:go_default_library",
"//pkg/util/iptables:go_default_library", "//pkg/util/iptables:go_default_library",
"//pkg/util/ipvs:go_default_library",
"//pkg/util/mount:go_default_library", "//pkg/util/mount:go_default_library",
"//pkg/util/netsh:go_default_library", "//pkg/util/netsh:go_default_library",
"//pkg/util/node:go_default_library", "//pkg/util/node:go_default_library",
......
...@@ -287,6 +287,9 @@ iptables: ...@@ -287,6 +287,9 @@ iptables:
masqueradeBit: 17 masqueradeBit: 17
minSyncPeriod: 10s minSyncPeriod: 10s
syncPeriod: 60s syncPeriod: 60s
ipvs:
minSyncPeriod: 10s
syncPeriod: 60s
kind: KubeProxyConfiguration kind: KubeProxyConfiguration
metricsBindAddress: "%s" metricsBindAddress: "%s"
mode: "iptables" mode: "iptables"
...@@ -347,8 +350,13 @@ udpTimeoutMilliseconds: 123ms ...@@ -347,8 +350,13 @@ udpTimeoutMilliseconds: 123ms
MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second}, MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second},
SyncPeriod: metav1.Duration{Duration: 60 * time.Second}, SyncPeriod: metav1.Duration{Duration: 60 * time.Second},
}, },
IPVS: componentconfig.KubeProxyIPVSConfiguration{
MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second},
SyncPeriod: metav1.Duration{Duration: 60 * time.Second},
},
MetricsBindAddress: tc.metricsBindAddress, MetricsBindAddress: tc.metricsBindAddress,
Mode: "iptables", Mode: "iptables",
// TODO: IPVS
OOMScoreAdj: utilpointer.Int32Ptr(17), OOMScoreAdj: utilpointer.Int32Ptr(17),
PortRange: "2-7", PortRange: "2-7",
ResourceContainer: "/foo", ResourceContainer: "/foo",
......
...@@ -52,6 +52,19 @@ type KubeProxyIPTablesConfiguration struct { ...@@ -52,6 +52,19 @@ type KubeProxyIPTablesConfiguration struct {
MinSyncPeriod metav1.Duration MinSyncPeriod metav1.Duration
} }
// KubeProxyIPVSConfiguration contains ipvs-related configuration
// details for the Kubernetes proxy server.
type KubeProxyIPVSConfiguration struct {
// syncPeriod is the period that ipvs rules are refreshed (e.g. '5s', '1m',
// '2h22m'). Must be greater than 0.
SyncPeriod metav1.Duration
// minSyncPeriod is the minimum period that ipvs rules are refreshed (e.g. '5s', '1m',
// '2h22m').
MinSyncPeriod metav1.Duration
// ipvs scheduler
Scheduler string
}
// KubeProxyConntrackConfiguration contains conntrack settings for // KubeProxyConntrackConfiguration contains conntrack settings for
// the Kubernetes proxy server. // the Kubernetes proxy server.
type KubeProxyConntrackConfiguration struct { type KubeProxyConntrackConfiguration struct {
...@@ -112,6 +125,8 @@ type KubeProxyConfiguration struct { ...@@ -112,6 +125,8 @@ type KubeProxyConfiguration struct {
ClientConnection ClientConnectionConfiguration ClientConnection ClientConnectionConfiguration
// iptables contains iptables-related configuration options. // iptables contains iptables-related configuration options.
IPTables KubeProxyIPTablesConfiguration IPTables KubeProxyIPTablesConfiguration
// ipvs contains ipvs-related configuration options.
IPVS KubeProxyIPVSConfiguration
// oomScoreAdj is the oom-score-adj value for kube-proxy process. Values must be within // oomScoreAdj is the oom-score-adj value for kube-proxy process. Values must be within
// the range [-1000, 1000] // the range [-1000, 1000]
OOMScoreAdj *int32 OOMScoreAdj *int32
......
...@@ -52,6 +52,19 @@ type KubeProxyIPTablesConfiguration struct { ...@@ -52,6 +52,19 @@ type KubeProxyIPTablesConfiguration struct {
MinSyncPeriod metav1.Duration `json:"minSyncPeriod"` MinSyncPeriod metav1.Duration `json:"minSyncPeriod"`
} }
// KubeProxyIPVSConfiguration contains ipvs-related configuration
// details for the Kubernetes proxy server.
type KubeProxyIPVSConfiguration struct {
// syncPeriod is the period that ipvs rules are refreshed (e.g. '5s', '1m',
// '2h22m'). Must be greater than 0.
SyncPeriod metav1.Duration `json:"syncPeriod"`
// minSyncPeriod is the minimum period that ipvs rules are refreshed (e.g. '5s', '1m',
// '2h22m').
MinSyncPeriod metav1.Duration `json:"minSyncPeriod"`
// ipvs scheduler
Scheduler string `json:"scheduler"`
}
// KubeProxyConntrackConfiguration contains conntrack settings for // KubeProxyConntrackConfiguration contains conntrack settings for
// the Kubernetes proxy server. // the Kubernetes proxy server.
type KubeProxyConntrackConfiguration struct { type KubeProxyConntrackConfiguration struct {
...@@ -112,6 +125,8 @@ type KubeProxyConfiguration struct { ...@@ -112,6 +125,8 @@ type KubeProxyConfiguration struct {
ClientConnection ClientConnectionConfiguration `json:"clientConnection"` ClientConnection ClientConnectionConfiguration `json:"clientConnection"`
// iptables contains iptables-related configuration options. // iptables contains iptables-related configuration options.
IPTables KubeProxyIPTablesConfiguration `json:"iptables"` IPTables KubeProxyIPTablesConfiguration `json:"iptables"`
// ipvs contains ipvs-related configuration options.
IPVS KubeProxyIPVSConfiguration `json:"ipvs"`
// oomScoreAdj is the oom-score-adj value for kube-proxy process. Values must be within // oomScoreAdj is the oom-score-adj value for kube-proxy process. Values must be within
// the range [-1000, 1000] // the range [-1000, 1000]
OOMScoreAdj *int32 `json:"oomScoreAdj"` OOMScoreAdj *int32 `json:"oomScoreAdj"`
......
...@@ -44,6 +44,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { ...@@ -44,6 +44,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_componentconfig_KubeProxyConntrackConfiguration_To_v1alpha1_KubeProxyConntrackConfiguration, Convert_componentconfig_KubeProxyConntrackConfiguration_To_v1alpha1_KubeProxyConntrackConfiguration,
Convert_v1alpha1_KubeProxyIPTablesConfiguration_To_componentconfig_KubeProxyIPTablesConfiguration, Convert_v1alpha1_KubeProxyIPTablesConfiguration_To_componentconfig_KubeProxyIPTablesConfiguration,
Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration, Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration,
Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration,
Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration,
Convert_v1alpha1_KubeSchedulerConfiguration_To_componentconfig_KubeSchedulerConfiguration, Convert_v1alpha1_KubeSchedulerConfiguration_To_componentconfig_KubeSchedulerConfiguration,
Convert_componentconfig_KubeSchedulerConfiguration_To_v1alpha1_KubeSchedulerConfiguration, Convert_componentconfig_KubeSchedulerConfiguration_To_v1alpha1_KubeSchedulerConfiguration,
Convert_v1alpha1_LeaderElectionConfiguration_To_componentconfig_LeaderElectionConfiguration, Convert_v1alpha1_LeaderElectionConfiguration_To_componentconfig_LeaderElectionConfiguration,
...@@ -93,6 +95,9 @@ func autoConvert_v1alpha1_KubeProxyConfiguration_To_componentconfig_KubeProxyCon ...@@ -93,6 +95,9 @@ func autoConvert_v1alpha1_KubeProxyConfiguration_To_componentconfig_KubeProxyCon
if err := Convert_v1alpha1_KubeProxyIPTablesConfiguration_To_componentconfig_KubeProxyIPTablesConfiguration(&in.IPTables, &out.IPTables, s); err != nil { if err := Convert_v1alpha1_KubeProxyIPTablesConfiguration_To_componentconfig_KubeProxyIPTablesConfiguration(&in.IPTables, &out.IPTables, s); err != nil {
return err return err
} }
if err := Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(&in.IPVS, &out.IPVS, s); err != nil {
return err
}
out.OOMScoreAdj = (*int32)(unsafe.Pointer(in.OOMScoreAdj)) out.OOMScoreAdj = (*int32)(unsafe.Pointer(in.OOMScoreAdj))
out.Mode = componentconfig.ProxyMode(in.Mode) out.Mode = componentconfig.ProxyMode(in.Mode)
out.PortRange = in.PortRange out.PortRange = in.PortRange
...@@ -124,6 +129,9 @@ func autoConvert_componentconfig_KubeProxyConfiguration_To_v1alpha1_KubeProxyCon ...@@ -124,6 +129,9 @@ func autoConvert_componentconfig_KubeProxyConfiguration_To_v1alpha1_KubeProxyCon
if err := Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(&in.IPTables, &out.IPTables, s); err != nil { if err := Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(&in.IPTables, &out.IPTables, s); err != nil {
return err return err
} }
if err := Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(&in.IPVS, &out.IPVS, s); err != nil {
return err
}
out.OOMScoreAdj = (*int32)(unsafe.Pointer(in.OOMScoreAdj)) out.OOMScoreAdj = (*int32)(unsafe.Pointer(in.OOMScoreAdj))
out.Mode = ProxyMode(in.Mode) out.Mode = ProxyMode(in.Mode)
out.PortRange = in.PortRange out.PortRange = in.PortRange
...@@ -195,6 +203,30 @@ func Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProx ...@@ -195,6 +203,30 @@ func Convert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProx
return autoConvert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(in, out, s) return autoConvert_componentconfig_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(in, out, s)
} }
func autoConvert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(in *KubeProxyIPVSConfiguration, out *componentconfig.KubeProxyIPVSConfiguration, s conversion.Scope) error {
out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod
out.Scheduler = in.Scheduler
return nil
}
// Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration is an autogenerated conversion function.
func Convert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(in *KubeProxyIPVSConfiguration, out *componentconfig.KubeProxyIPVSConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_KubeProxyIPVSConfiguration_To_componentconfig_KubeProxyIPVSConfiguration(in, out, s)
}
func autoConvert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(in *componentconfig.KubeProxyIPVSConfiguration, out *KubeProxyIPVSConfiguration, s conversion.Scope) error {
out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod
out.Scheduler = in.Scheduler
return nil
}
// Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration is an autogenerated conversion function.
func Convert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(in *componentconfig.KubeProxyIPVSConfiguration, out *KubeProxyIPVSConfiguration, s conversion.Scope) error {
return autoConvert_componentconfig_KubeProxyIPVSConfiguration_To_v1alpha1_KubeProxyIPVSConfiguration(in, out, s)
}
func autoConvert_v1alpha1_KubeSchedulerConfiguration_To_componentconfig_KubeSchedulerConfiguration(in *KubeSchedulerConfiguration, out *componentconfig.KubeSchedulerConfiguration, s conversion.Scope) error { func autoConvert_v1alpha1_KubeSchedulerConfiguration_To_componentconfig_KubeSchedulerConfiguration(in *KubeSchedulerConfiguration, out *componentconfig.KubeSchedulerConfiguration, s conversion.Scope) error {
out.Port = int32(in.Port) out.Port = int32(in.Port)
out.Address = in.Address out.Address = in.Address
......
...@@ -53,6 +53,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { ...@@ -53,6 +53,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
return nil return nil
}, InType: reflect.TypeOf(&KubeProxyIPTablesConfiguration{})}, }, InType: reflect.TypeOf(&KubeProxyIPTablesConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*KubeProxyIPVSConfiguration).DeepCopyInto(out.(*KubeProxyIPVSConfiguration))
return nil
}, InType: reflect.TypeOf(&KubeProxyIPVSConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*KubeSchedulerConfiguration).DeepCopyInto(out.(*KubeSchedulerConfiguration)) in.(*KubeSchedulerConfiguration).DeepCopyInto(out.(*KubeSchedulerConfiguration))
return nil return nil
}, InType: reflect.TypeOf(&KubeSchedulerConfiguration{})}, }, InType: reflect.TypeOf(&KubeSchedulerConfiguration{})},
...@@ -85,6 +89,7 @@ func (in *KubeProxyConfiguration) DeepCopyInto(out *KubeProxyConfiguration) { ...@@ -85,6 +89,7 @@ func (in *KubeProxyConfiguration) DeepCopyInto(out *KubeProxyConfiguration) {
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
out.ClientConnection = in.ClientConnection out.ClientConnection = in.ClientConnection
in.IPTables.DeepCopyInto(&out.IPTables) in.IPTables.DeepCopyInto(&out.IPTables)
out.IPVS = in.IPVS
if in.OOMScoreAdj != nil { if in.OOMScoreAdj != nil {
in, out := &in.OOMScoreAdj, &out.OOMScoreAdj in, out := &in.OOMScoreAdj, &out.OOMScoreAdj
if *in == nil { if *in == nil {
...@@ -165,6 +170,24 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopy() *KubeProxyIPTablesConfigura ...@@ -165,6 +170,24 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopy() *KubeProxyIPTablesConfigura
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeProxyIPVSConfiguration) DeepCopyInto(out *KubeProxyIPVSConfiguration) {
*out = *in
out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeProxyIPVSConfiguration.
func (in *KubeProxyIPVSConfiguration) DeepCopy() *KubeProxyIPVSConfiguration {
if in == nil {
return nil
}
out := new(KubeProxyIPVSConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeSchedulerConfiguration) DeepCopyInto(out *KubeSchedulerConfiguration) { func (in *KubeSchedulerConfiguration) DeepCopyInto(out *KubeSchedulerConfiguration) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
......
...@@ -65,6 +65,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { ...@@ -65,6 +65,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
return nil return nil
}, InType: reflect.TypeOf(&KubeProxyIPTablesConfiguration{})}, }, InType: reflect.TypeOf(&KubeProxyIPTablesConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*KubeProxyIPVSConfiguration).DeepCopyInto(out.(*KubeProxyIPVSConfiguration))
return nil
}, InType: reflect.TypeOf(&KubeProxyIPVSConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*KubeSchedulerConfiguration).DeepCopyInto(out.(*KubeSchedulerConfiguration)) in.(*KubeSchedulerConfiguration).DeepCopyInto(out.(*KubeSchedulerConfiguration))
return nil return nil
}, InType: reflect.TypeOf(&KubeSchedulerConfiguration{})}, }, InType: reflect.TypeOf(&KubeSchedulerConfiguration{})},
...@@ -206,6 +210,7 @@ func (in *KubeProxyConfiguration) DeepCopyInto(out *KubeProxyConfiguration) { ...@@ -206,6 +210,7 @@ func (in *KubeProxyConfiguration) DeepCopyInto(out *KubeProxyConfiguration) {
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
out.ClientConnection = in.ClientConnection out.ClientConnection = in.ClientConnection
in.IPTables.DeepCopyInto(&out.IPTables) in.IPTables.DeepCopyInto(&out.IPTables)
out.IPVS = in.IPVS
if in.OOMScoreAdj != nil { if in.OOMScoreAdj != nil {
in, out := &in.OOMScoreAdj, &out.OOMScoreAdj in, out := &in.OOMScoreAdj, &out.OOMScoreAdj
if *in == nil { if *in == nil {
...@@ -286,6 +291,24 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopy() *KubeProxyIPTablesConfigura ...@@ -286,6 +291,24 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopy() *KubeProxyIPTablesConfigura
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeProxyIPVSConfiguration) DeepCopyInto(out *KubeProxyIPVSConfiguration) {
*out = *in
out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeProxyIPVSConfiguration.
func (in *KubeProxyIPVSConfiguration) DeepCopy() *KubeProxyIPVSConfiguration {
if in == nil {
return nil
}
out := new(KubeProxyIPVSConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeSchedulerConfiguration) DeepCopyInto(out *KubeSchedulerConfiguration) { func (in *KubeSchedulerConfiguration) DeepCopyInto(out *KubeSchedulerConfiguration) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
......
...@@ -127,6 +127,12 @@ const ( ...@@ -127,6 +127,12 @@ const (
// Taint nodes based on their condition status for 'NetworkUnavailable', // Taint nodes based on their condition status for 'NetworkUnavailable',
// 'MemoryPressure', 'OutOfDisk' and 'DiskPressure'. // 'MemoryPressure', 'OutOfDisk' and 'DiskPressure'.
TaintNodesByCondition utilfeature.Feature = "TaintNodesByCondition" TaintNodesByCondition utilfeature.Feature = "TaintNodesByCondition"
// owner: @haibinxie
// alpha: v1.8
//
// Implement IPVS-based in-cluster service load balancing
SupportIPVSProxyMode utilfeature.Feature = "SupportIPVSProxyMode"
) )
func init() { func init() {
...@@ -164,4 +170,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS ...@@ -164,4 +170,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
// inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed // inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side: // unintentionally on either side:
apiextensionsfeatures.CustomResourceValidation: {Default: false, PreRelease: utilfeature.Alpha}, apiextensionsfeatures.CustomResourceValidation: {Default: false, PreRelease: utilfeature.Alpha},
SupportIPVSProxyMode: {Default: false, PreRelease: utilfeature.Alpha},
} }
...@@ -28,6 +28,7 @@ filegroup( ...@@ -28,6 +28,7 @@ filegroup(
"//pkg/proxy/config:all-srcs", "//pkg/proxy/config:all-srcs",
"//pkg/proxy/healthcheck:all-srcs", "//pkg/proxy/healthcheck:all-srcs",
"//pkg/proxy/iptables:all-srcs", "//pkg/proxy/iptables:all-srcs",
"//pkg/proxy/ipvs:all-srcs",
"//pkg/proxy/userspace:all-srcs", "//pkg/proxy/userspace:all-srcs",
"//pkg/proxy/util:all-srcs", "//pkg/proxy/util:all-srcs",
"//pkg/proxy/winuserspace:all-srcs", "//pkg/proxy/winuserspace:all-srcs",
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"proxier_test.go",
],
"//conditions:default": [],
}),
library = ":go_default_library",
tags = ["automanaged"],
deps = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//pkg/api:go_default_library",
"//pkg/proxy:go_default_library",
"//pkg/proxy/util:go_default_library",
"//pkg/util/iptables:go_default_library",
"//pkg/util/iptables/testing:go_default_library",
"//pkg/util/ipvs:go_default_library",
"//pkg/util/ipvs/testing:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
"//conditions:default": [],
}),
)
go_library(
name = "go_default_library",
srcs = ["proxier.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/helper:go_default_library",
"//pkg/api/service:go_default_library",
"//pkg/features:go_default_library",
"//pkg/proxy:go_default_library",
"//pkg/proxy/healthcheck:go_default_library",
"//pkg/proxy/util:go_default_library",
"//pkg/util/iptables:go_default_library",
"//pkg/util/ipvs:go_default_library",
"//pkg/util/sysctl:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
...@@ -27,6 +27,7 @@ filegroup( ...@@ -27,6 +27,7 @@ filegroup(
"//pkg/util/io:all-srcs", "//pkg/util/io:all-srcs",
"//pkg/util/ipconfig:all-srcs", "//pkg/util/ipconfig:all-srcs",
"//pkg/util/iptables:all-srcs", "//pkg/util/iptables:all-srcs",
"//pkg/util/ipvs:all-srcs",
"//pkg/util/keymutex:all-srcs", "//pkg/util/keymutex:all-srcs",
"//pkg/util/labels:all-srcs", "//pkg/util/labels:all-srcs",
"//pkg/util/limitwriter:all-srcs", "//pkg/util/limitwriter:all-srcs",
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"ipvs_test.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"ipvs_linux_test.go",
],
"//conditions:default": [],
}),
library = ":go_default_library",
tags = ["automanaged"],
deps = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
"//conditions:default": [],
}),
)
go_library(
name = "go_default_library",
srcs = [
"ipvs.go",
"ipvs_unsupported.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"ipvs_linux.go",
],
"//conditions:default": [],
}),
tags = ["automanaged"],
deps = [
"//vendor/k8s.io/utils/exec:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/util/ipvs/testing:all-srcs",
],
tags = ["automanaged"],
)
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"net"
"strconv"
)
// Interface is an injectable interface for running ipvs commands. Implementations must be goroutine-safe.
type Interface interface {
// Flush clears all virtual servers in system. return occurred error immediately.
Flush() error
// EnsureVirtualServerAddressBind checks if virtual server's address is bound to dummy interface and, if not, binds it. If the address is already bound, return true.
EnsureVirtualServerAddressBind(serv *VirtualServer, dev string) (exist bool, err error)
// UnbindVirtualServerAddress checks if virtual server's address is bound to dummy interface and, if so, unbinds it.
UnbindVirtualServerAddress(serv *VirtualServer, dev string) error
// AddVirtualServer creates the specified virtual server.
AddVirtualServer(*VirtualServer) error
// UpdateVirtualServer updates an already existing virtual server. If the virtual server does not exist, return error.
UpdateVirtualServer(*VirtualServer) error
// DeleteVirtualServer deletes the specified virtual server. If the virtual server does not exist, return error.
DeleteVirtualServer(*VirtualServer) error
// Given a partial virtual server, GetVirtualServer will return the specified virtual server information in the system.
GetVirtualServer(*VirtualServer) (*VirtualServer, error)
// GetVirtualServers lists all virtual servers in the system.
GetVirtualServers() ([]*VirtualServer, error)
// AddRealServer creates the specified real server for the specified virtual server.
AddRealServer(*VirtualServer, *RealServer) error
// GetRealServers returns all real servers for the specified virtual server.
GetRealServers(*VirtualServer) ([]*RealServer, error)
// DeleteRealServer deletes the specified real server from the specified virtual server.
DeleteRealServer(*VirtualServer, *RealServer) error
}
// VirtualServer is an user-oriented definition of an IPVS virtual server in its entirety.
type VirtualServer struct {
Address net.IP
Protocol string
Port uint16
Scheduler string
Flags ServiceFlags
Timeout uint32
}
// ServiceFlags is used to specify session affinity, ip hash etc.
type ServiceFlags uint32
const (
// FlagPersistent specify IPVS service session affinity
FlagPersistent = 0x1
)
// Equal check the equality of virtual server.
// We don't use struct == since it doesn't work because of slice.
func (svc *VirtualServer) Equal(other *VirtualServer) bool {
return svc.Address.Equal(other.Address) &&
svc.Protocol == other.Protocol &&
svc.Port == other.Port &&
svc.Scheduler == other.Scheduler &&
svc.Flags == other.Flags &&
svc.Timeout == other.Timeout
}
func (svc *VirtualServer) String() string {
return net.JoinHostPort(svc.Address.String(), strconv.Itoa(int(svc.Port))) + "/" + svc.Protocol
}
// RealServer is an user-oriented definition of an IPVS real server in its entirety.
type RealServer struct {
Address net.IP
Port uint16
Weight int
}
func (dest *RealServer) String() string {
return net.JoinHostPort(dest.Address.String(), strconv.Itoa(int(dest.Port)))
}
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"errors"
"fmt"
"net"
"strings"
"syscall"
"github.com/docker/libnetwork/ipvs"
"github.com/golang/glog"
utilexec "k8s.io/utils/exec"
)
const cmdIP = "ip"
// runner implements Interface.
type runner struct {
exec utilexec.Interface
ipvsHandle *ipvs.Handle
}
// New returns a new Interface which will call ipvs APIs.
func New(exec utilexec.Interface) Interface {
ihandle, err := ipvs.New("")
if err != nil {
glog.Errorf("IPVS interface can't be initialized, error: %v", err)
return nil
}
return &runner{
exec: exec,
ipvsHandle: ihandle,
}
}
// EnsureVirtualServerAddressBind is part of Interface.
func (runner *runner) EnsureVirtualServerAddressBind(vs *VirtualServer, dummyDev string) (exist bool, err error) {
addr := vs.Address.String() + "/32"
args := []string{"addr", "add", addr, "dev", dummyDev}
out, err := runner.exec.Command(cmdIP, args...).CombinedOutput()
if err != nil {
// "exit status 2" will be returned if the address is already bound to dummy device
if ee, ok := err.(utilexec.ExitError); ok {
if ee.Exited() && ee.ExitStatus() == 2 {
return true, nil
}
}
return false, fmt.Errorf("error bind address: %s to dummy interface: %s, err: %v: %s", vs.Address.String(), dummyDev, err, out)
}
return false, nil
}
// UnbindVirtualServerAddress is part of Interface.
func (runner *runner) UnbindVirtualServerAddress(vs *VirtualServer, dummyDev string) error {
addr := vs.Address.String() + "/32"
args := []string{"addr", "del", addr, "dev", dummyDev}
out, err := runner.exec.Command(cmdIP, args...).CombinedOutput()
if err != nil {
return fmt.Errorf("error unbind address: %s from dummy interface: %s, err: %v: %s", vs.Address.String(), dummyDev, err, out)
}
return nil
}
// AddVirtualServer is part of Interface.
func (runner *runner) AddVirtualServer(vs *VirtualServer) error {
eSvc, err := toBackendService(vs)
if err != nil {
return err
}
return runner.ipvsHandle.NewService(eSvc)
}
// UpdateVirtualServer is part of Interface.
func (runner *runner) UpdateVirtualServer(vs *VirtualServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
return runner.ipvsHandle.UpdateService(bSvc)
}
// DeleteVirtualServer is part of Interface.
func (runner *runner) DeleteVirtualServer(vs *VirtualServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
return runner.ipvsHandle.DelService(bSvc)
}
// GetVirtualServer is part of Interface.
func (runner *runner) GetVirtualServer(vs *VirtualServer) (*VirtualServer, error) {
bSvc, err := toBackendService(vs)
if err != nil {
return nil, err
}
ipvsService, err := runner.ipvsHandle.GetService(bSvc)
if err != nil {
return nil, err
}
virtualServer, err := toVirtualServer(ipvsService)
if err != nil {
return nil, err
}
return virtualServer, nil
}
// GetVirtualServers is part of Interface.
func (runner *runner) GetVirtualServers() ([]*VirtualServer, error) {
ipvsServices, err := runner.ipvsHandle.GetServices()
if err != nil {
return nil, err
}
vss := make([]*VirtualServer, 0)
for _, ipvsService := range ipvsServices {
vs, err := toVirtualServer(ipvsService)
if err != nil {
return nil, err
}
vss = append(vss, vs)
}
return vss, nil
}
// Flush is part of Interface. Currently we delete IPVS services one by one
func (runner *runner) Flush() error {
vss, err := runner.GetVirtualServers()
if err != nil {
return err
}
for _, vs := range vss {
err := runner.DeleteVirtualServer(vs)
// TODO: aggregate errors?
if err != nil {
return err
}
}
return nil
}
// AddRealServer is part of Interface.
func (runner *runner) AddRealServer(vs *VirtualServer, rs *RealServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
bDst, err := toBackendDestination(rs)
if err != nil {
return err
}
return runner.ipvsHandle.NewDestination(bSvc, bDst)
}
// DeleteRealServer is part of Interface.
func (runner *runner) DeleteRealServer(vs *VirtualServer, rs *RealServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
bDst, err := toBackendDestination(rs)
if err != nil {
return err
}
return runner.ipvsHandle.DelDestination(bSvc, bDst)
}
// GetRealServers is part of Interface.
func (runner *runner) GetRealServers(vs *VirtualServer) ([]*RealServer, error) {
bSvc, err := toBackendService(vs)
if err != nil {
return nil, err
}
bDestinations, err := runner.ipvsHandle.GetDestinations(bSvc)
if err != nil {
return nil, err
}
realServers := make([]*RealServer, 0)
for _, dest := range bDestinations {
dst, err := toRealServer(dest)
// TODO: aggregate errors?
if err != nil {
return nil, err
}
realServers = append(realServers, dst)
}
return realServers, nil
}
// toVirtualServer converts an IPVS service representation to the equivalent virtual server structure.
func toVirtualServer(svc *ipvs.Service) (*VirtualServer, error) {
if svc == nil {
return nil, errors.New("ipvs svc should not be empty")
}
vs := &VirtualServer{
Address: svc.Address,
Port: svc.Port,
Scheduler: svc.SchedName,
Protocol: protocolNumbeToString(ProtoType(svc.Protocol)),
Flags: ServiceFlags(svc.Flags),
Timeout: svc.Timeout,
}
if vs.Address == nil {
if svc.AddressFamily == syscall.AF_INET {
vs.Address = net.IPv4zero
} else {
vs.Address = net.IPv6zero
}
}
return vs, nil
}
// toRealServer converts an IPVS destination representation to the equivalent real server structure.
func toRealServer(dst *ipvs.Destination) (*RealServer, error) {
if dst == nil {
return nil, errors.New("ipvs destination should not be empty")
}
return &RealServer{
Address: dst.Address,
Port: dst.Port,
Weight: dst.Weight,
}, nil
}
// toBackendService converts an IPVS real server representation to the equivalent "backend" service structure.
func toBackendService(vs *VirtualServer) (*ipvs.Service, error) {
if vs == nil {
return nil, errors.New("virtual server should not be empty")
}
bakSvc := &ipvs.Service{
Address: vs.Address,
Protocol: stringToProtocolNumber(vs.Protocol),
Port: vs.Port,
SchedName: vs.Scheduler,
Flags: uint32(vs.Flags),
Timeout: vs.Timeout,
}
if ip4 := vs.Address.To4(); ip4 != nil {
bakSvc.AddressFamily = syscall.AF_INET
bakSvc.Netmask = 0xffffffff
} else {
bakSvc.AddressFamily = syscall.AF_INET6
bakSvc.Netmask = 128
}
return bakSvc, nil
}
// toBackendDestination converts an IPVS real server representation to the equivalent "backend" destination structure.
func toBackendDestination(rs *RealServer) (*ipvs.Destination, error) {
if rs == nil {
return nil, errors.New("real server should not be empty")
}
return &ipvs.Destination{
Address: rs.Address,
Port: rs.Port,
Weight: rs.Weight,
}, nil
}
// stringToProtocolNumber returns the protocol value for the given name
func stringToProtocolNumber(protocol string) uint16 {
switch strings.ToLower(protocol) {
case "tcp":
return uint16(syscall.IPPROTO_TCP)
case "udp":
return uint16(syscall.IPPROTO_UDP)
}
return uint16(0)
}
// protocolNumbeToString returns the name for the given protocol value.
func protocolNumbeToString(proto ProtoType) string {
switch proto {
case syscall.IPPROTO_TCP:
return "TCP"
case syscall.IPPROTO_UDP:
return "UDP"
}
return ""
}
// ProtoType is IPVS service protocol type
type ProtoType uint16
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"net"
"testing"
)
func TestVirtualServerEqual(t *testing.T) {
Tests := []struct {
svcA *VirtualServer
svcB *VirtualServer
equal bool
reason string
}{
{
svcA: &VirtualServer{
Address: net.ParseIP("10.20.30.40"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("10.20.30.41"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "IPv4 address not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2017::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "IPv6 address not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "TCP",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2012::beeef"),
Protocol: "UDP",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "Protocol not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "TCP",
Port: 80,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "TCP",
Port: 8080,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "Port not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "wlc",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "Scheduler not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 2,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 3,
Timeout: 0,
},
equal: false,
reason: "Flags not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 10800,
},
equal: false,
reason: "Timeout not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 0x1,
Timeout: 10800,
},
svcB: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 0x1,
Timeout: 10800,
},
equal: true,
reason: "All fields equal",
},
}
for i := range Tests {
equal := Tests[i].svcA.Equal(Tests[i].svcB)
if equal != Tests[i].equal {
t.Errorf("case: %d got %v, expected %v, reason: %s", i, equal, Tests[i].equal, Tests[i].reason)
}
}
}
func TestFrontendServiceString(t *testing.T) {
Tests := []struct {
svc *VirtualServer
expected string
}{
{
svc: &VirtualServer{
Address: net.ParseIP("10.20.30.40"),
Protocol: "TCP",
Port: 80,
},
expected: "10.20.30.40:80/TCP",
},
{
svc: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "UDP",
Port: 8080,
},
expected: "[2012::beef]:8080/UDP",
},
{
svc: &VirtualServer{
Address: net.ParseIP("10.20.30.41"),
Protocol: "ESP",
Port: 1234,
},
expected: "10.20.30.41:1234/ESP",
},
}
for i := range Tests {
if Tests[i].expected != Tests[i].svc.String() {
t.Errorf("case: %d got %v, expected %v", i, Tests[i].svc.String(), Tests[i].expected)
}
}
}
func TestFrontendDestinationString(t *testing.T) {
Tests := []struct {
svc *RealServer
expected string
}{
{
svc: &RealServer{
Address: net.ParseIP("10.20.30.40"),
Port: 80,
},
expected: "10.20.30.40:80",
},
{
svc: &RealServer{
Address: net.ParseIP("2012::beef"),
Port: 8080,
},
expected: "[2012::beef]:8080",
},
}
for i := range Tests {
if Tests[i].expected != Tests[i].svc.String() {
t.Errorf("case: %d got %v, expected %v", i, Tests[i].svc.String(), Tests[i].expected)
}
}
}
// +build !linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"fmt"
utilexec "k8s.io/utils/exec"
)
// New returns a dummy Interface for unsupported platform.
func New(utilexec.Interface) Interface {
return &runner{}
}
type runner struct {
}
func (runner *runner) Flush() error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) EnsureVirtualServerAddressBind(*VirtualServer, string) (bool, error) {
return false, fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) UnbindVirtualServerAddress(*VirtualServer, string) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) AddVirtualServer(*VirtualServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) UpdateVirtualServer(*VirtualServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) DeleteVirtualServer(*VirtualServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) GetVirtualServer(*VirtualServer) (*VirtualServer, error) {
return nil, fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) GetVirtualServers() ([]*VirtualServer, error) {
return nil, fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) AddRealServer(*VirtualServer, *RealServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) GetRealServers(*VirtualServer) ([]*RealServer, error) {
return nil, fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) DeleteRealServer(*VirtualServer, *RealServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
var _ = Interface(&runner{})
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["fake.go"],
tags = ["automanaged"],
deps = ["//pkg/util/ipvs:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"fmt"
utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
)
//FakeIPVS no-op implementation of ipvs Interface
type FakeIPVS struct {
Scheduler string
Services map[serviceKey]*utilipvs.VirtualServer
Destinations map[serviceKey][]*utilipvs.RealServer
}
type serviceKey struct {
IP string
Port uint16
Protocol string
}
func (s *serviceKey) String() string {
return fmt.Sprintf("%s:%d/%s", s.IP, s.Port, s.Protocol)
}
//NewFake creates a fake ipvs strucuter
func NewFake() *FakeIPVS {
return &FakeIPVS{
Services: make(map[serviceKey]*utilipvs.VirtualServer),
Destinations: make(map[serviceKey][]*utilipvs.RealServer),
}
}
func toServiceKey(serv *utilipvs.VirtualServer) serviceKey {
return serviceKey{
IP: serv.Address.To4().String(),
Port: serv.Port,
Protocol: serv.Protocol,
}
}
//EnsureVirtualServerAddressBind is a fake implementation
func (*FakeIPVS) EnsureVirtualServerAddressBind(serv *utilipvs.VirtualServer, dev string) (exist bool, err error) {
return true, nil
}
//UnbindVirtualServerAddress is a fake implementation
func (*FakeIPVS) UnbindVirtualServerAddress(serv *utilipvs.VirtualServer, dev string) error {
return nil
}
//AddVirtualServer is a fake implementation
func (f *FakeIPVS) AddVirtualServer(serv *utilipvs.VirtualServer) error {
if serv == nil {
return fmt.Errorf("Failed to add service: service can't be nil")
}
key := toServiceKey(serv)
f.Services[key] = serv
// make sure no destination present when creating new service
f.Destinations = make(map[serviceKey][]*utilipvs.RealServer)
return nil
}
//UpdateVirtualServer is a fake implementation
func (f *FakeIPVS) UpdateVirtualServer(serv *utilipvs.VirtualServer) error {
if serv == nil {
return fmt.Errorf("Failed to update service, service can't be nil")
}
return nil
}
//DeleteVirtualServer is a fake implementation
func (f *FakeIPVS) DeleteVirtualServer(serv *utilipvs.VirtualServer) error {
if serv == nil {
return fmt.Errorf("Failed to delete service: service can't be nil")
}
key := toServiceKey(serv)
delete(f.Services, key)
// clear specific destinations as well
f.Destinations[key] = nil
return nil
}
//GetVirtualServer is a fake implementation
func (f *FakeIPVS) GetVirtualServer(serv *utilipvs.VirtualServer) (*utilipvs.VirtualServer, error) {
if serv == nil {
return nil, fmt.Errorf("Failed to get service: service can't be nil")
}
key := toServiceKey(serv)
svc, found := f.Services[key]
if found {
return svc, nil
}
return nil, fmt.Errorf("Not found serv: %v", key.String())
}
//GetVirtualServers is a fake implementation
func (f *FakeIPVS) GetVirtualServers() ([]*utilipvs.VirtualServer, error) {
res := make([]*utilipvs.VirtualServer, 0)
for _, svc := range f.Services {
res = append(res, svc)
}
return res, nil
}
//Flush is a fake implementation
func (f *FakeIPVS) Flush() error {
// directly drop old data
f.Services = nil
f.Destinations = nil
return nil
}
//AddRealServer is a fake implementation
func (f *FakeIPVS) AddRealServer(serv *utilipvs.VirtualServer, dest *utilipvs.RealServer) error {
if serv == nil || dest == nil {
return fmt.Errorf("Failed to add destination for service, neither service nor destination shouldn't be nil")
}
key := toServiceKey(serv)
if _, ok := f.Services[key]; !ok {
return fmt.Errorf("Failed to add destination for service %v, service not found", key.String())
}
dests := f.Destinations[key]
if dests == nil {
dests = make([]*utilipvs.RealServer, 0)
f.Destinations[key] = dests
}
f.Destinations[key] = append(f.Destinations[key], dest)
return nil
}
//GetRealServers is a fake implementation
func (f *FakeIPVS) GetRealServers(serv *utilipvs.VirtualServer) ([]*utilipvs.RealServer, error) {
if serv == nil {
return nil, fmt.Errorf("Failed to get destination for nil service")
}
key := toServiceKey(serv)
if _, ok := f.Services[key]; !ok {
return nil, fmt.Errorf("Failed to get destinations for service %v, service not found", key.String())
}
return f.Destinations[key], nil
}
//DeleteRealServer is a fake implementation
func (*FakeIPVS) DeleteRealServer(serv *utilipvs.VirtualServer, dest *utilipvs.RealServer) error {
if serv == nil || dest == nil {
return fmt.Errorf("Failed to delete destination, neither service nor destination can't be nil")
}
return nil
}
var _ = utilipvs.Interface(&FakeIPVS{})
...@@ -140,6 +140,7 @@ filegroup( ...@@ -140,6 +140,7 @@ filegroup(
"//vendor/github.com/docker/go-connections/sockets:all-srcs", "//vendor/github.com/docker/go-connections/sockets:all-srcs",
"//vendor/github.com/docker/go-connections/tlsconfig:all-srcs", "//vendor/github.com/docker/go-connections/tlsconfig:all-srcs",
"//vendor/github.com/docker/go-units:all-srcs", "//vendor/github.com/docker/go-units:all-srcs",
"//vendor/github.com/docker/libnetwork/ipvs:all-srcs",
"//vendor/github.com/docker/spdystream:all-srcs", "//vendor/github.com/docker/spdystream:all-srcs",
"//vendor/github.com/elazarl/go-bindata-assetfs:all-srcs", "//vendor/github.com/elazarl/go-bindata-assetfs:all-srcs",
"//vendor/github.com/elazarl/goproxy:all-srcs", "//vendor/github.com/elazarl/goproxy:all-srcs",
...@@ -306,6 +307,7 @@ filegroup( ...@@ -306,6 +307,7 @@ filegroup(
"//vendor/github.com/syndtr/gocapability/capability:all-srcs", "//vendor/github.com/syndtr/gocapability/capability:all-srcs",
"//vendor/github.com/ugorji/go/codec:all-srcs", "//vendor/github.com/ugorji/go/codec:all-srcs",
"//vendor/github.com/vishvananda/netlink:all-srcs", "//vendor/github.com/vishvananda/netlink:all-srcs",
"//vendor/github.com/vishvananda/netns:all-srcs",
"//vendor/github.com/vmware/govmomi:all-srcs", "//vendor/github.com/vmware/govmomi:all-srcs",
"//vendor/github.com/vmware/photon-controller-go-sdk/SSPI:all-srcs", "//vendor/github.com/vmware/photon-controller-go-sdk/SSPI:all-srcs",
"//vendor/github.com/vmware/photon-controller-go-sdk/photon:all-srcs", "//vendor/github.com/vmware/photon-controller-go-sdk/photon:all-srcs",
......
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"constants.go",
"ipvs.go",
"netlink.go",
],
"//conditions:default": [],
}),
visibility = ["//visibility:public"],
deps = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/github.com/Sirupsen/logrus:go_default_library",
"//vendor/github.com/vishvananda/netlink/nl:go_default_library",
"//vendor/github.com/vishvananda/netns:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// +build linux
package ipvs
const (
genlCtrlID = 0x10
)
// GENL control commands
const (
genlCtrlCmdUnspec uint8 = iota
genlCtrlCmdNewFamily
genlCtrlCmdDelFamily
genlCtrlCmdGetFamily
)
// GENL family attributes
const (
genlCtrlAttrUnspec int = iota
genlCtrlAttrFamilyID
genlCtrlAttrFamilyName
)
// IPVS genl commands
const (
ipvsCmdUnspec uint8 = iota
ipvsCmdNewService
ipvsCmdSetService
ipvsCmdDelService
ipvsCmdGetService
ipvsCmdNewDest
ipvsCmdSetDest
ipvsCmdDelDest
ipvsCmdGetDest
ipvsCmdNewDaemon
ipvsCmdDelDaemon
ipvsCmdGetDaemon
ipvsCmdSetConfig
ipvsCmdGetConfig
ipvsCmdSetInfo
ipvsCmdGetInfo
ipvsCmdZero
ipvsCmdFlush
)
// Attributes used in the first level of commands
const (
ipvsCmdAttrUnspec int = iota
ipvsCmdAttrService
ipvsCmdAttrDest
ipvsCmdAttrDaemon
ipvsCmdAttrTimeoutTCP
ipvsCmdAttrTimeoutTCPFin
ipvsCmdAttrTimeoutUDP
)
// Attributes used to describe a service. Used inside nested attribute
// ipvsCmdAttrService
const (
ipvsSvcAttrUnspec int = iota
ipvsSvcAttrAddressFamily
ipvsSvcAttrProtocol
ipvsSvcAttrAddress
ipvsSvcAttrPort
ipvsSvcAttrFWMark
ipvsSvcAttrSchedName
ipvsSvcAttrFlags
ipvsSvcAttrTimeout
ipvsSvcAttrNetmask
ipvsSvcAttrStats
ipvsSvcAttrPEName
)
// Attributes used to describe a destination (real server). Used
// inside nested attribute ipvsCmdAttrDest.
const (
ipvsDestAttrUnspec int = iota
ipvsDestAttrAddress
ipvsDestAttrPort
ipvsDestAttrForwardingMethod
ipvsDestAttrWeight
ipvsDestAttrUpperThreshold
ipvsDestAttrLowerThreshold
ipvsDestAttrActiveConnections
ipvsDestAttrInactiveConnections
ipvsDestAttrPersistentConnections
ipvsDestAttrStats
ipvsDestAttrAddressFamily
)
// IPVS Svc Statistics constancs
const (
ipvsSvcStatsUnspec int = iota
ipvsSvcStatsConns
ipvsSvcStatsPktsIn
ipvsSvcStatsPktsOut
ipvsSvcStatsBytesIn
ipvsSvcStatsBytesOut
ipvsSvcStatsCPS
ipvsSvcStatsPPSIn
ipvsSvcStatsPPSOut
ipvsSvcStatsBPSIn
ipvsSvcStatsBPSOut
)
// Destination forwarding methods
const (
// ConnectionFlagFwdmask indicates the mask in the connection
// flags which is used by forwarding method bits.
ConnectionFlagFwdMask = 0x0007
// ConnectionFlagMasq is used for masquerade forwarding method.
ConnectionFlagMasq = 0x0000
// ConnectionFlagLocalNode is used for local node forwarding
// method.
ConnectionFlagLocalNode = 0x0001
// ConnectionFlagTunnel is used for tunnel mode forwarding
// method.
ConnectionFlagTunnel = 0x0002
// ConnectionFlagDirectRoute is used for direct routing
// forwarding method.
ConnectionFlagDirectRoute = 0x0003
)
const (
// RoundRobin distributes jobs equally amongst the available
// real servers.
RoundRobin = "rr"
// LeastConnection assigns more jobs to real servers with
// fewer active jobs.
LeastConnection = "lc"
// DestinationHashing assigns jobs to servers through looking
// up a statically assigned hash table by their destination IP
// addresses.
DestinationHashing = "dh"
// SourceHashing assigns jobs to servers through looking up
// a statically assigned hash table by their source IP
// addresses.
SourceHashing = "sh"
)
// +build linux
package ipvs
import (
"net"
"syscall"
"fmt"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
)
// Service defines an IPVS service in its entirety.
type Service struct {
// Virtual service address.
Address net.IP
Protocol uint16
Port uint16
FWMark uint32 // Firewall mark of the service.
// Virtual service options.
SchedName string
Flags uint32
Timeout uint32
Netmask uint32
AddressFamily uint16
PEName string
Stats SvcStats
}
// SvcStats defines an IPVS service statistics
type SvcStats struct {
Connections uint32
PacketsIn uint32
PacketsOut uint32
BytesIn uint64
BytesOut uint64
CPS uint32
BPSOut uint32
PPSIn uint32
PPSOut uint32
BPSIn uint32
}
// Destination defines an IPVS destination (real server) in its
// entirety.
type Destination struct {
Address net.IP
Port uint16
Weight int
ConnectionFlags uint32
AddressFamily uint16
UpperThreshold uint32
LowerThreshold uint32
}
// Handle provides a namespace specific ipvs handle to program ipvs
// rules.
type Handle struct {
seq uint32
sock *nl.NetlinkSocket
}
// New provides a new ipvs handle in the namespace pointed to by the
// passed path. It will return a valid handle or an error in case an
// error occurred while creating the handle.
func New(path string) (*Handle, error) {
setup()
n := netns.None()
if path != "" {
var err error
n, err = netns.GetFromPath(path)
if err != nil {
return nil, err
}
}
defer n.Close()
sock, err := nl.GetNetlinkSocketAt(n, netns.None(), syscall.NETLINK_GENERIC)
if err != nil {
return nil, err
}
return &Handle{sock: sock}, nil
}
// Close closes the ipvs handle. The handle is invalid after Close
// returns.
func (i *Handle) Close() {
if i.sock != nil {
i.sock.Close()
}
}
// NewService creates a new ipvs service in the passed handle.
func (i *Handle) NewService(s *Service) error {
return i.doCmd(s, nil, ipvsCmdNewService)
}
// IsServicePresent queries for the ipvs service in the passed handle.
func (i *Handle) IsServicePresent(s *Service) bool {
return nil == i.doCmd(s, nil, ipvsCmdGetService)
}
// UpdateService updates an already existing service in the passed
// handle.
func (i *Handle) UpdateService(s *Service) error {
return i.doCmd(s, nil, ipvsCmdSetService)
}
// DelService deletes an already existing service in the passed
// handle.
func (i *Handle) DelService(s *Service) error {
return i.doCmd(s, nil, ipvsCmdDelService)
}
// NewDestination creates a new real server in the passed ipvs
// service which should already be existing in the passed handle.
func (i *Handle) NewDestination(s *Service, d *Destination) error {
return i.doCmd(s, d, ipvsCmdNewDest)
}
// UpdateDestination updates an already existing real server in the
// passed ipvs service in the passed handle.
func (i *Handle) UpdateDestination(s *Service, d *Destination) error {
return i.doCmd(s, d, ipvsCmdSetDest)
}
// DelDestination deletes an already existing real server in the
// passed ipvs service in the passed handle.
func (i *Handle) DelDestination(s *Service, d *Destination) error {
return i.doCmd(s, d, ipvsCmdDelDest)
}
// GetServices returns an array of services configured on the Node
func (i *Handle) GetServices() ([]*Service, error) {
return i.doGetServicesCmd(nil)
}
// GetDestinations returns an array of Destinations configured for this Service
func (i *Handle) GetDestinations(s *Service) ([]*Destination, error) {
return i.doGetDestinationsCmd(s, nil)
}
// GetService gets details of a specific IPVS services, useful in updating statisics etc.,
func (i *Handle) GetService(s *Service) (*Service, error) {
res, err := i.doGetServicesCmd(s)
if err != nil {
return nil, err
}
// We are looking for exactly one service otherwise error out
if len(res) != 1 {
return nil, fmt.Errorf("Expected only one service obtained=%d", len(res))
}
return res[0], nil
}
language: go language: go
before_script:
# make sure we keep path in tact when we sudo
- sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
# modprobe ip_gre or else the first gre device can't be deleted
- sudo modprobe ip_gre
# modprobe nf_conntrack for the conntrack testing
- sudo modprobe nf_conntrack
- sudo modprobe nf_conntrack_netlink
- sudo modprobe nf_conntrack_ipv4
- sudo modprobe nf_conntrack_ipv6
install: install:
- go get github.com/vishvananda/netns - go get github.com/vishvananda/netns
...@@ -4,33 +4,57 @@ go_library( ...@@ -4,33 +4,57 @@ go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"addr.go", "addr.go",
"class.go",
"conntrack_unspecified.go",
"filter.go", "filter.go",
"genetlink_unspecified.go",
"handle_unspecified.go",
"link.go", "link.go",
"neigh.go", "neigh.go",
"netlink.go", "netlink.go",
"netlink_unspecified.go", "netlink_unspecified.go",
"order.go",
"protinfo.go", "protinfo.go",
"qdisc.go", "qdisc.go",
"route.go", "route.go",
"route_unspecified.go",
"rule.go",
"socket.go",
"xfrm.go", "xfrm.go",
"xfrm_policy.go", "xfrm_policy.go",
"xfrm_state.go", "xfrm_state.go",
] + select({ ] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [ "@io_bazel_rules_go//go/platform:linux_amd64": [
"addr_linux.go", "addr_linux.go",
"bpf_linux.go",
"bridge_linux.go",
"class_linux.go",
"conntrack_linux.go",
"filter_linux.go", "filter_linux.go",
"genetlink_linux.go",
"gtp_linux.go",
"handle_linux.go",
"link_linux.go", "link_linux.go",
"link_tuntap_linux.go",
"neigh_linux.go", "neigh_linux.go",
"netlink_linux.go",
"protinfo_linux.go", "protinfo_linux.go",
"qdisc_linux.go", "qdisc_linux.go",
"route_linux.go", "route_linux.go",
"rule_linux.go",
"socket_linux.go",
"xfrm_monitor_linux.go",
"xfrm_policy_linux.go", "xfrm_policy_linux.go",
"xfrm_state_linux.go", "xfrm_state_linux.go",
], ],
"//conditions:default": [], "//conditions:default": [],
}), }),
cgo = True,
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = ["//vendor/github.com/vishvananda/netlink/nl:go_default_library"], deps = [
"//vendor/github.com/vishvananda/netlink/nl:go_default_library",
"//vendor/github.com/vishvananda/netns:go_default_library",
],
) )
filegroup( filegroup(
......
...@@ -11,17 +11,17 @@ goroot = $(addprefix ../../../,$(1)) ...@@ -11,17 +11,17 @@ goroot = $(addprefix ../../../,$(1))
unroot = $(subst ../../../,,$(1)) unroot = $(subst ../../../,,$(1))
fmt = $(addprefix fmt-,$(1)) fmt = $(addprefix fmt-,$(1))
all: fmt 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)))
......
...@@ -8,7 +8,7 @@ the kernel. It can be used to add and remove interfaces, set ip addresses ...@@ -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
...@@ -38,15 +38,18 @@ Add a new bridge and add eth1 into it: ...@@ -38,15 +38,18 @@ Add a new bridge and add eth1 into it:
package main package main
import ( import (
"net" "fmt"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
func main() { func main() {
la := netlink.NewLinkAttrs() la := netlink.NewLinkAttrs()
la.Name = "foo" la.Name = "foo"
mybridge := &netlink.Bridge{la}} mybridge := &netlink.Bridge{LinkAttrs: la}
_ := netlink.LinkAdd(mybridge) err := netlink.LinkAdd(mybridge)
if err != nil {
fmt.Printf("could not add %s: %v\n", la.Name, err)
}
eth1, _ := netlink.LinkByName("eth1") eth1, _ := netlink.LinkByName("eth1")
netlink.LinkSetMaster(eth1, mybridge) netlink.LinkSetMaster(eth1, mybridge)
} }
...@@ -63,7 +66,6 @@ Add a new ip address to loopback: ...@@ -63,7 +66,6 @@ Add a new ip address to loopback:
package main package main
import ( import (
"net"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
......
...@@ -11,11 +11,17 @@ import ( ...@@ -11,11 +11,17 @@ import (
type Addr struct { type Addr struct {
*net.IPNet *net.IPNet
Label string Label string
Flags int
Scope int
Peer *net.IPNet
Broadcast net.IP
PreferedLft int
ValidLft int
} }
// String returns $ip/$netmask $label // String returns $ip/$netmask $label
func (a Addr) String() string { func (a Addr) String() string {
return fmt.Sprintf("%s %s", a.IPNet, a.Label) return strings.TrimSpace(fmt.Sprintf("%s %s", a.IPNet, a.Label))
} }
// ParseAddr parses the string representation of an address in the // ParseAddr parses the string representation of an address in the
...@@ -41,3 +47,10 @@ func (a Addr) Equal(x Addr) bool { ...@@ -41,3 +47,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
}
...@@ -2,56 +2,111 @@ package netlink ...@@ -2,56 +2,111 @@ 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.
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)
}
// AddrReplace will replace (or, if not present, add) an IP address on a link device.
// Equivalent to: `ip addr replace $addr dev $link`
func AddrReplace(link Link, addr *Addr) error {
return pkgHandle.AddrReplace(link, addr)
}
// AddrReplace will replace (or, if not present, add) an IP address on a link device.
// Equivalent to: `ip addr replace $addr dev $link`
func (h *Handle) AddrReplace(link Link, addr *Addr) error {
req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE|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) }
// 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 addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { 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)
msg := nl.NewIfAddrmsg(family) msg := nl.NewIfAddrmsg(family)
msg.Index = uint32(base.Index) msg.Index = uint32(base.Index)
msg.Scope = uint8(addr.Scope)
prefixlen, _ := addr.Mask.Size() prefixlen, _ := addr.Mask.Size()
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 <= 0xff {
msg.IfAddrmsg.Flags = uint8(addr.Flags)
} else {
b := make([]byte, 4)
native.PutUint32(b, uint32(addr.Flags))
flagsData := nl.NewRtAttr(IFA_FLAGS, b)
req.AddData(flagsData)
}
}
if addr.Broadcast != nil {
req.AddData(nl.NewRtAttr(syscall.IFA_BROADCAST, addr.Broadcast))
}
if addr.Label != "" { if addr.Label != "" {
labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label)) labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
req.AddData(labelData) req.AddData(labelData)
...@@ -65,7 +120,14 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { ...@@ -65,7 +120,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)
...@@ -74,29 +136,51 @@ func AddrList(link Link, family int) ([]Addr, error) { ...@@ -74,29 +136,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
} }
attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if family != FAMILY_ALL && msgFamily != family {
if err != nil { continue
return nil, err }
res = append(res, addr)
}
return res, nil
}
func parseAddr(m []byte) (addr Addr, family, index int, err error) {
msg := nl.DeserializeIfAddrmsg(m)
family = -1
index = -1
attrs, err1 := nl.ParseRouteAttr(m[msg.Len():])
if err1 != nil {
err = err1
return
} }
family = int(msg.Family)
index = int(msg.Index)
var local, dst *net.IPNet 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:
...@@ -104,13 +188,23 @@ func AddrList(link Link, family int) ([]Addr, error) { ...@@ -104,13 +188,23 @@ 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_BROADCAST:
addr.Broadcast = attr.Value
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:
addr.Flags = int(native.Uint32(attr.Value[0:4]))
case nl.IFA_CACHEINFO:
ci := nl.DeserializeIfaCacheInfo(attr.Value)
addr.PreferedLft = int(ci.IfaPrefered)
addr.ValidLft = int(ci.IfaValid)
} }
} }
...@@ -120,9 +214,75 @@ func AddrList(link Link, family int) ([]Addr, error) { ...@@ -120,9 +214,75 @@ func AddrList(link Link, family int) ([]Addr, error) {
} else { } else {
addr.IPNet = dst addr.IPNet = dst
} }
addr.Scope = int(msg.Scope)
res = append(res, addr) return
}
type AddrUpdate struct {
LinkAddress net.IPNet
LinkIndex int
Flags int
Scope int
PreferedLft int
ValidLft 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
} }
return res, nil 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,
Flags: addr.Flags,
Scope: addr.Scope,
PreferedLft: addr.PreferedLft,
ValidLft: addr.ValidLft}
}
}
}()
return nil
} }
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
}
package netlink
import (
"fmt"
"syscall"
"github.com/vishvananda/netlink/nl"
)
// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
return pkgHandle.BridgeVlanList()
}
// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK)
if err != nil {
return nil, err
}
ret := make(map[int32][]*nl.BridgeVlanInfo)
for _, m := range msgs {
msg := nl.DeserializeIfInfomsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.IFLA_AF_SPEC:
//nested attr
nestAttrs, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, fmt.Errorf("failed to parse nested attr %v", err)
}
for _, nestAttr := range nestAttrs {
switch nestAttr.Attr.Type {
case nl.IFLA_BRIDGE_VLAN_INFO:
vlanInfo := nl.DeserializeBridgeVlanInfo(nestAttr.Value)
ret[msg.Index] = append(ret[msg.Index], vlanInfo)
}
}
}
}
}
return ret, nil
}
// BridgeVlanAdd adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
return pkgHandle.BridgeVlanAdd(link, vid, pvid, untagged, self, master)
}
// BridgeVlanAdd adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(syscall.RTM_SETLINK, link, vid, pvid, untagged, self, master)
}
// BridgeVlanDel adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
return pkgHandle.BridgeVlanDel(link, vid, pvid, untagged, self, master)
}
// BridgeVlanDel adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(syscall.RTM_DELLINK, link, vid, pvid, untagged, self, master)
}
func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged, self, master bool) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(cmd, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
msg.Index = int32(base.Index)
req.AddData(msg)
br := nl.NewRtAttr(nl.IFLA_AF_SPEC, nil)
var flags uint16
if self {
flags |= nl.BRIDGE_FLAGS_SELF
}
if master {
flags |= nl.BRIDGE_FLAGS_MASTER
}
if flags > 0 {
nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags))
}
vlanInfo := &nl.BridgeVlanInfo{Vid: vid}
if pvid {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID
}
if untagged {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED
}
nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
req.AddData(br)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
if err != nil {
return err
}
return nil
}
package netlink
import (
"fmt"
)
type Class interface {
Attrs() *ClassAttrs
Type() string
}
// ClassAttrs represents a netlink class. A filter is associated with a link,
// has a handle and a parent. The root filter of a device should have a
// parent == HANDLE_ROOT.
type ClassAttrs struct {
LinkIndex int
Handle uint32
Parent uint32
Leaf uint32
}
func (q ClassAttrs) String() string {
return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Leaf: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Leaf)
}
type HtbClassAttrs struct {
// TODO handle all attributes
Rate uint64
Ceil uint64
Buffer uint32
Cbuffer uint32
Quantum uint32
Level uint32
Prio uint32
}
func (q HtbClassAttrs) String() string {
return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer)
}
// HtbClass represents an Htb class
type HtbClass struct {
ClassAttrs
Rate uint64
Ceil uint64
Buffer uint32
Cbuffer uint32
Quantum uint32
Level uint32
Prio uint32
}
func (q HtbClass) String() string {
return fmt.Sprintf("{Rate: %d, Ceil: %d, Buffer: %d, Cbuffer: %d}", q.Rate, q.Ceil, q.Buffer, q.Cbuffer)
}
func (q *HtbClass) Attrs() *ClassAttrs {
return &q.ClassAttrs
}
func (q *HtbClass) Type() string {
return "htb"
}
// GenericClass classes represent types that are not currently understood
// by this netlink library.
type GenericClass struct {
ClassAttrs
ClassType string
}
func (class *GenericClass) Attrs() *ClassAttrs {
return &class.ClassAttrs
}
func (class *GenericClass) Type() string {
return class.ClassType
}
package netlink
import (
"errors"
"syscall"
"github.com/vishvananda/netlink/nl"
)
// NOTE: function is in here because it uses other linux functions
func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
mtu := 1600
rate := cattrs.Rate / 8
ceil := cattrs.Ceil / 8
buffer := cattrs.Buffer
cbuffer := cattrs.Cbuffer
if ceil == 0 {
ceil = rate
}
if buffer == 0 {
buffer = uint32(float64(rate)/Hz() + float64(mtu))
}
buffer = uint32(Xmittime(rate, buffer))
if cbuffer == 0 {
cbuffer = uint32(float64(ceil)/Hz() + float64(mtu))
}
cbuffer = uint32(Xmittime(ceil, cbuffer))
return &HtbClass{
ClassAttrs: attrs,
Rate: rate,
Ceil: ceil,
Buffer: buffer,
Cbuffer: cbuffer,
Quantum: 10,
Level: 0,
Prio: 0,
}
}
// ClassDel will delete a class from the system.
// Equivalent to: `tc class del $class`
func ClassDel(class Class) error {
return pkgHandle.ClassDel(class)
}
// ClassDel will delete a class from the system.
// Equivalent to: `tc class del $class`
func (h *Handle) ClassDel(class Class) error {
return h.classModify(syscall.RTM_DELTCLASS, 0, class)
}
// ClassChange will change a class in place
// Equivalent to: `tc class change $class`
// The parent and handle MUST NOT be changed.
func ClassChange(class Class) error {
return 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.
// 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 ClassReplace(class Class) error {
return pkgHandle.ClassReplace(class)
}
// ClassReplace will replace a class to the system.
// quivalent to: `tc class replace $class`
// The handle MAY be changed.
// If a class already exist with this parent/handle pair, the class is changed.
// If a class does not already exist with this parent/handle, a new class is created.
func (h *Handle) ClassReplace(class Class) error {
return h.classModify(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE, class)
}
// ClassAdd will add a class to the system.
// Equivalent to: `tc class add $class`
func ClassAdd(class Class) error {
return pkgHandle.ClassAdd(class)
}
// ClassAdd will add a class to the system.
// Equivalent to: `tc class add $class`
func (h *Handle) ClassAdd(class Class) error {
return h.classModify(
syscall.RTM_NEWTCLASS,
syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
class,
)
}
func (h *Handle) classModify(cmd, flags int, class Class) error {
req := h.newNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
base := class.Attrs()
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Ifindex: int32(base.LinkIndex),
Handle: base.Handle,
Parent: base.Parent,
}
req.AddData(msg)
if cmd != syscall.RTM_DELTCLASS {
if err := classPayload(req, class); err != nil {
return err
}
}
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
func classPayload(req *nl.NetlinkRequest, class Class) error {
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if htb, ok := class.(*HtbClass); ok {
opt := nl.TcHtbCopt{}
opt.Buffer = htb.Buffer
opt.Cbuffer = htb.Cbuffer
opt.Quantum = htb.Quantum
opt.Level = htb.Level
opt.Prio = htb.Prio
// TODO: Handle Debug properly. For now default to 0
/* Calculate {R,C}Tab and set Rate and Ceil */
cellLog := -1
ccellLog := -1
linklayer := nl.LINKLAYER_ETHERNET
mtu := 1600
var rtab [256]uint32
var ctab [256]uint32
tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
if CalcRtable(&tcrate, rtab, cellLog, uint32(mtu), linklayer) < 0 {
return errors.New("HTB: failed to calculate rate table")
}
opt.Rate = tcrate
tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
if CalcRtable(&tcceil, ctab, ccellLog, uint32(mtu), linklayer) < 0 {
return errors.New("HTB: failed to calculate ceil rate table")
}
opt.Ceil = tcceil
nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize())
nl.NewRtAttrChild(options, nl.TCA_HTB_RTAB, SerializeRtab(rtab))
nl.NewRtAttrChild(options, nl.TCA_HTB_CTAB, SerializeRtab(ctab))
}
req.AddData(options)
return nil
}
// 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 ClassList(link Link, parent uint32) ([]Class, error) {
return pkgHandle.ClassList(link, parent)
}
// ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`.
// Generally returns nothing if link and parent are not specified.
func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
req := h.newNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP)
msg := &nl.TcMsg{
Family: nl.FAMILY_ALL,
Parent: parent,
}
if link != nil {
base := link.Attrs()
h.ensureIndex(base)
msg.Ifindex = int32(base.Index)
}
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTCLASS)
if err != nil {
return nil, err
}
var res []Class
for _, m := range msgs {
msg := nl.DeserializeTcMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
base := ClassAttrs{
LinkIndex: int(msg.Ifindex),
Handle: msg.Handle,
Parent: msg.Parent,
}
var class Class
classType := ""
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.TCA_KIND:
classType = string(attr.Value[:len(attr.Value)-1])
switch classType {
case "htb":
class = &HtbClass{}
default:
class = &GenericClass{ClassType: classType}
}
case nl.TCA_OPTIONS:
switch classType {
case "htb":
data, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
_, err = parseHtbClassData(class, data)
if err != nil {
return nil, err
}
}
}
}
*class.Attrs() = base
res = append(res, class)
}
return res, nil
}
func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
htb := class.(*HtbClass)
detailed := false
for _, datum := range data {
switch datum.Attr.Type {
case nl.TCA_HTB_PARMS:
opt := nl.DeserializeTcHtbCopt(datum.Value)
htb.Rate = uint64(opt.Rate.Rate)
htb.Ceil = uint64(opt.Ceil.Rate)
htb.Buffer = opt.Buffer
htb.Cbuffer = opt.Cbuffer
htb.Quantum = opt.Quantum
htb.Level = opt.Level
htb.Prio = opt.Prio
}
}
return detailed, nil
}
// +build !linux
package netlink
// ConntrackTableType Conntrack table for the netlink operation
type ConntrackTableType uint8
// InetFamily Family type
type InetFamily uint8
// ConntrackFlow placeholder
type ConntrackFlow struct{}
// ConntrackFilter placeholder
type ConntrackFilter struct{}
// ConntrackTableList returns the flow list of a table of a specific family
// conntrack -L [table] [options] List conntrack or expectation table
func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
return nil, ErrNotImplemented
}
// ConntrackTableFlush flushes all the flows of a specified table
// conntrack -F [table] Flush table
// The flush operation applies to all the family types
func ConntrackTableFlush(table ConntrackTableType) error {
return ErrNotImplemented
}
// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
// conntrack -D [table] parameters Delete conntrack or expectation
func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) {
return 0, ErrNotImplemented
}
// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
// conntrack -L [table] [options] List conntrack or expectation table
func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
return nil, ErrNotImplemented
}
// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
// conntrack -F [table] Flush table
// The flush operation applies to all the family types
func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {
return ErrNotImplemented
}
// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed
// conntrack -D [table] parameters Delete conntrack or expectation
func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) {
return 0, ErrNotImplemented
}
...@@ -2,6 +2,8 @@ package netlink ...@@ -2,6 +2,8 @@ package netlink
import ( import (
"fmt" "fmt"
"github.com/vishvananda/netlink/nl"
) )
type Filter interface { type Filter interface {
...@@ -9,7 +11,7 @@ type Filter interface { ...@@ -9,7 +11,7 @@ type Filter interface {
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 {
...@@ -24,11 +26,205 @@ func (q FilterAttrs) String() string { ...@@ -24,11 +26,205 @@ 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,
}
}
// Constants used in TcU32Sel.Flags.
const (
TC_U32_TERMINAL = nl.TC_U32_TERMINAL
TC_U32_OFFSET = nl.TC_U32_OFFSET
TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
TC_U32_EAT = nl.TC_U32_EAT
)
// Sel of the U32 filters that contains multiple TcU32Key. This is the copy
// and the frontend representation of nl.TcU32Sel. It is serialized into canonical
// nl.TcU32Sel with the appropriate endianness.
type TcU32Sel struct {
Flags uint8
Offshift uint8
Nkeys uint8
Pad uint8
Offmask uint16
Off uint16
Offoff int16
Hoff int16
Hmask uint32
Keys []TcU32Key
}
// TcU32Key contained of Sel in the U32 filters. This is the copy and the frontend
// representation of nl.TcU32Key. It is serialized into chanonical nl.TcU32Sel
// with the appropriate endianness.
type TcU32Key struct {
Mask uint32
Val uint32
Off int32
OffMask int32
}
// 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
Sel *TcU32Sel
Actions []Action
} }
func (filter *U32) Attrs() *FilterAttrs { func (filter *U32) Attrs() *FilterAttrs {
...@@ -39,6 +235,38 @@ func (filter *U32) Type() string { ...@@ -39,6 +235,38 @@ func (filter *U32) Type() string {
return "u32" return "u32"
} }
type FilterFwAttrs struct {
ClassId uint32
InDev string
Mask uint32
Index uint32
Buffer uint32
Mtu uint32
Mpu uint16
Rate uint32
AvRate uint32
PeakRate uint32
Action TcPolAct
Overhead uint16
LinkLayer int
}
type BpfFilter struct {
FilterAttrs
ClassId uint32
Fd int
Name string
DirectAction bool
}
func (filter *BpfFilter) Type() string {
return "bpf"
}
func (filter *BpfFilter) Attrs() *FilterAttrs {
return &filter.FilterAttrs
}
// 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 {
......
package netlink
import (
"fmt"
"syscall"
"github.com/vishvananda/netlink/nl"
)
type GenlOp struct {
ID uint32
Flags uint32
}
type GenlMulticastGroup struct {
ID uint32
Name string
}
type GenlFamily struct {
ID uint16
HdrSize uint32
Name string
Version uint32
MaxAttr uint32
Ops []GenlOp
Groups []GenlMulticastGroup
}
func parseOps(b []byte) ([]GenlOp, error) {
attrs, err := nl.ParseRouteAttr(b)
if err != nil {
return nil, err
}
ops := make([]GenlOp, 0, len(attrs))
for _, a := range attrs {
nattrs, err := nl.ParseRouteAttr(a.Value)
if err != nil {
return nil, err
}
var op GenlOp
for _, na := range nattrs {
switch na.Attr.Type {
case nl.GENL_CTRL_ATTR_OP_ID:
op.ID = native.Uint32(na.Value)
case nl.GENL_CTRL_ATTR_OP_FLAGS:
op.Flags = native.Uint32(na.Value)
}
}
ops = append(ops, op)
}
return ops, nil
}
func parseMulticastGroups(b []byte) ([]GenlMulticastGroup, error) {
attrs, err := nl.ParseRouteAttr(b)
if err != nil {
return nil, err
}
groups := make([]GenlMulticastGroup, 0, len(attrs))
for _, a := range attrs {
nattrs, err := nl.ParseRouteAttr(a.Value)
if err != nil {
return nil, err
}
var g GenlMulticastGroup
for _, na := range nattrs {
switch na.Attr.Type {
case nl.GENL_CTRL_ATTR_MCAST_GRP_NAME:
g.Name = nl.BytesToString(na.Value)
case nl.GENL_CTRL_ATTR_MCAST_GRP_ID:
g.ID = native.Uint32(na.Value)
}
}
groups = append(groups, g)
}
return groups, nil
}
func (f *GenlFamily) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
for _, a := range attrs {
switch a.Attr.Type {
case nl.GENL_CTRL_ATTR_FAMILY_NAME:
f.Name = nl.BytesToString(a.Value)
case nl.GENL_CTRL_ATTR_FAMILY_ID:
f.ID = native.Uint16(a.Value)
case nl.GENL_CTRL_ATTR_VERSION:
f.Version = native.Uint32(a.Value)
case nl.GENL_CTRL_ATTR_HDRSIZE:
f.HdrSize = native.Uint32(a.Value)
case nl.GENL_CTRL_ATTR_MAXATTR:
f.MaxAttr = native.Uint32(a.Value)
case nl.GENL_CTRL_ATTR_OPS:
ops, err := parseOps(a.Value)
if err != nil {
return err
}
f.Ops = ops
case nl.GENL_CTRL_ATTR_MCAST_GROUPS:
groups, err := parseMulticastGroups(a.Value)
if err != nil {
return err
}
f.Groups = groups
}
}
return nil
}
func parseFamilies(msgs [][]byte) ([]*GenlFamily, error) {
families := make([]*GenlFamily, 0, len(msgs))
for _, m := range msgs {
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
if err != nil {
return nil, err
}
family := &GenlFamily{}
if err := family.parseAttributes(attrs); err != nil {
return nil, err
}
families = append(families, family)
}
return families, nil
}
func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
msg := &nl.Genlmsg{
Command: nl.GENL_CTRL_CMD_GETFAMILY,
Version: nl.GENL_CTRL_VERSION,
}
req := h.newNetlinkRequest(nl.GENL_ID_CTRL, syscall.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
return parseFamilies(msgs)
}
func GenlFamilyList() ([]*GenlFamily, error) {
return pkgHandle.GenlFamilyList()
}
func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
msg := &nl.Genlmsg{
Command: nl.GENL_CTRL_CMD_GETFAMILY,
Version: nl.GENL_CTRL_VERSION,
}
req := h.newNetlinkRequest(nl.GENL_ID_CTRL, 0)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_CTRL_ATTR_FAMILY_NAME, nl.ZeroTerminated(name)))
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
families, err := parseFamilies(msgs)
if len(families) != 1 {
return nil, fmt.Errorf("invalid response for GENL_CTRL_CMD_GETFAMILY")
}
return families[0], nil
}
func GenlFamilyGet(name string) (*GenlFamily, error) {
return pkgHandle.GenlFamilyGet(name)
}
// +build !linux
package netlink
type GenlOp struct{}
type GenlMulticastGroup struct{}
type GenlFamily struct{}
func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
return nil, ErrNotImplemented
}
func GenlFamilyList() ([]*GenlFamily, error) {
return nil, ErrNotImplemented
}
func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
return nil, ErrNotImplemented
}
func GenlFamilyGet(name string) (*GenlFamily, error) {
return nil, ErrNotImplemented
}
package netlink
import (
"fmt"
"net"
"strings"
"syscall"
"github.com/vishvananda/netlink/nl"
)
type PDP struct {
Version uint32
TID uint64
PeerAddress net.IP
MSAddress net.IP
Flow uint16
NetNSFD uint32
ITEI uint32
OTEI uint32
}
func (pdp *PDP) String() string {
elems := []string{}
elems = append(elems, fmt.Sprintf("Version: %d", pdp.Version))
if pdp.Version == 0 {
elems = append(elems, fmt.Sprintf("TID: %d", pdp.TID))
} else if pdp.Version == 1 {
elems = append(elems, fmt.Sprintf("TEI: %d/%d", pdp.ITEI, pdp.OTEI))
}
elems = append(elems, fmt.Sprintf("MS-Address: %s", pdp.MSAddress))
elems = append(elems, fmt.Sprintf("Peer-Address: %s", pdp.PeerAddress))
return fmt.Sprintf("{%s}", strings.Join(elems, " "))
}
func (p *PDP) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
for _, a := range attrs {
switch a.Attr.Type {
case nl.GENL_GTP_ATTR_VERSION:
p.Version = native.Uint32(a.Value)
case nl.GENL_GTP_ATTR_TID:
p.TID = native.Uint64(a.Value)
case nl.GENL_GTP_ATTR_PEER_ADDRESS:
p.PeerAddress = net.IP(a.Value)
case nl.GENL_GTP_ATTR_MS_ADDRESS:
p.MSAddress = net.IP(a.Value)
case nl.GENL_GTP_ATTR_FLOW:
p.Flow = native.Uint16(a.Value)
case nl.GENL_GTP_ATTR_NET_NS_FD:
p.NetNSFD = native.Uint32(a.Value)
case nl.GENL_GTP_ATTR_I_TEI:
p.ITEI = native.Uint32(a.Value)
case nl.GENL_GTP_ATTR_O_TEI:
p.OTEI = native.Uint32(a.Value)
}
}
return nil
}
func parsePDP(msgs [][]byte) ([]*PDP, error) {
pdps := make([]*PDP, 0, len(msgs))
for _, m := range msgs {
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
if err != nil {
return nil, err
}
pdp := &PDP{}
if err := pdp.parseAttributes(attrs); err != nil {
return nil, err
}
pdps = append(pdps, pdp)
}
return pdps, nil
}
func (h *Handle) GTPPDPList() ([]*PDP, error) {
f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
if err != nil {
return nil, err
}
msg := &nl.Genlmsg{
Command: nl.GENL_GTP_CMD_GETPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
return parsePDP(msgs)
}
func GTPPDPList() ([]*PDP, error) {
return pkgHandle.GTPPDPList()
}
func gtpPDPGet(req *nl.NetlinkRequest) (*PDP, error) {
msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
pdps, err := parsePDP(msgs)
if err != nil {
return nil, err
}
if len(pdps) != 1 {
return nil, fmt.Errorf("invalid reqponse for GENL_GTP_CMD_GETPDP")
}
return pdps[0], nil
}
func (h *Handle) GTPPDPByTID(link Link, tid int) (*PDP, error) {
f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
if err != nil {
return nil, err
}
msg := &nl.Genlmsg{
Command: nl.GENL_GTP_CMD_GETPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), 0)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(uint64(tid))))
return gtpPDPGet(req)
}
func GTPPDPByTID(link Link, tid int) (*PDP, error) {
return pkgHandle.GTPPDPByTID(link, tid)
}
func (h *Handle) GTPPDPByITEI(link Link, itei int) (*PDP, error) {
f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
if err != nil {
return nil, err
}
msg := &nl.Genlmsg{
Command: nl.GENL_GTP_CMD_GETPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), 0)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(1)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(uint32(itei))))
return gtpPDPGet(req)
}
func GTPPDPByITEI(link Link, itei int) (*PDP, error) {
return pkgHandle.GTPPDPByITEI(link, itei)
}
func (h *Handle) GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
if err != nil {
return nil, err
}
msg := &nl.Genlmsg{
Command: nl.GENL_GTP_CMD_GETPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), 0)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(addr.To4())))
return gtpPDPGet(req)
}
func GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
return pkgHandle.GTPPDPByMSAddress(link, addr)
}
func (h *Handle) GTPPDPAdd(link Link, pdp *PDP) error {
f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
if err != nil {
return err
}
msg := &nl.Genlmsg{
Command: nl.GENL_GTP_CMD_NEWPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_PEER_ADDRESS, []byte(pdp.PeerAddress.To4())))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(pdp.MSAddress.To4())))
switch pdp.Version {
case 0:
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_FLOW, nl.Uint16Attr(pdp.Flow)))
case 1:
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_O_TEI, nl.Uint32Attr(pdp.OTEI)))
default:
return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
}
_, err = req.Execute(syscall.NETLINK_GENERIC, 0)
return err
}
func GTPPDPAdd(link Link, pdp *PDP) error {
return pkgHandle.GTPPDPAdd(link, pdp)
}
func (h *Handle) GTPPDPDel(link Link, pdp *PDP) error {
f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
if err != nil {
return err
}
msg := &nl.Genlmsg{
Command: nl.GENL_GTP_CMD_DELPDP,
Version: nl.GENL_GTP_VERSION,
}
req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
req.AddData(msg)
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
switch pdp.Version {
case 0:
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
case 1:
req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
default:
return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
}
_, err = req.Execute(syscall.NETLINK_GENERIC, 0)
return err
}
func GTPPDPDel(link Link, pdp *PDP) error {
return pkgHandle.GTPPDPDel(link, pdp)
}
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,
}
}
// +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
}
package netlink
// ideally golang.org/x/sys/unix would define IfReq but it only has
// IFNAMSIZ, hence this minimalistic implementation
const (
SizeOfIfReq = 40
IFNAMSIZ = 16
)
type ifReq struct {
Name [IFNAMSIZ]byte
Flags uint16
pad [SizeOfIfReq - IFNAMSIZ - 2]byte
}
...@@ -67,30 +67,62 @@ func (msg *Ndmsg) Len() int { ...@@ -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 or replace 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 ....`
func (h *Handle) NeighAdd(neigh *Neigh) error {
return h.neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL)
}
// NeighSet will add or replace an IP to MAC mapping to the ARP table
// Equivalent to: `ip neigh replace....` // Equivalent to: `ip neigh replace....`
func NeighSet(neigh *Neigh) error { func NeighSet(neigh *Neigh) error {
return neighAdd(neigh, syscall.NLM_F_CREATE) 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 { ...@@ -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,9 +164,36 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { ...@@ -130,9 +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),
Flags: uint8(flags),
} }
req.AddData(&msg) req.AddData(&msg)
......
...@@ -9,16 +9,13 @@ ...@@ -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.
...@@ -33,7 +30,10 @@ func ParseIPNet(s string) (*net.IPNet, error) { ...@@ -33,7 +30,10 @@ func ParseIPNet(s string) (*net.IPNet, error) {
return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil
} }
// NewIPNet generates an IPNet from an ip address using a netmask of 32. // NewIPNet generates an IPNet from an ip address using a netmask of 32 or 128.
func NewIPNet(ip net.IP) *net.IPNet { func NewIPNet(ip net.IP) *net.IPNet {
if ip.To4() != nil {
return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)} return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
}
return &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)}
} }
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
)
...@@ -2,43 +2,117 @@ ...@@ -2,43 +2,117 @@
package netlink package netlink
import ( import "net"
"errors"
)
var ( func LinkSetUp(link Link) error {
ErrNotImplemented = errors.New("not implemented") return ErrNotImplemented
) }
func LinkSetDown(link Link) error {
return ErrNotImplemented
}
func LinkSetMTU(link Link, mtu int) error {
return ErrNotImplemented
}
func LinkSetMaster(link Link, master *Bridge) error {
return ErrNotImplemented
}
func LinkSetNsPid(link Link, nspid int) error {
return ErrNotImplemented
}
func LinkSetNsFd(link Link, fd int) error {
return ErrNotImplemented
}
func LinkSetName(link Link, name string) error {
return ErrNotImplemented
}
func LinkSetAlias(link Link, name string) error {
return ErrNotImplemented
}
func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
return ErrNotImplemented
}
func LinkSetUp(link *Link) error { func LinkSetVfHardwareAddr(link Link, vf int, hwaddr net.HardwareAddr) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetDown(link *Link) error { func LinkSetVfVlan(link Link, vf, vlan int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetMTU(link *Link, mtu int) error { func LinkSetVfTxRate(link Link, vf, rate int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetMaster(link *Link, master *Link) error { func LinkSetNoMaster(link Link) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetNsPid(link *Link, nspid int) error { func LinkSetMasterByIndex(link Link, masterIndex int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkSetNsFd(link *Link, fd int) error { func LinkSetXdpFd(link Link, fd int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkAdd(link *Link) error { func LinkSetARPOff(link Link) error {
return ErrNotImplemented return ErrNotImplemented
} }
func LinkDel(link *Link) error { func LinkSetARPOn(link Link) 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 +144,15 @@ func LinkList() ([]Link, error) { ...@@ -70,15 +144,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 +164,7 @@ func RouteDel(route *Route) error { ...@@ -90,7 +164,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 +212,10 @@ func NeighList(linkIndex, family int) ([]Neigh, error) { ...@@ -138,6 +212,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
} }
...@@ -2,20 +2,34 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") ...@@ -2,20 +2,34 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = select({ srcs = [
"nl_unspecified.go",
"syscall.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [ "@io_bazel_rules_go//go/platform:linux_amd64": [
"addr_linux.go", "addr_linux.go",
"bridge_linux.go",
"conntrack_linux.go",
"genetlink_linux.go",
"link_linux.go", "link_linux.go",
"mpls_linux.go",
"nl_linux.go", "nl_linux.go",
"route_linux.go", "route_linux.go",
"tc_linux.go", "tc_linux.go",
"xfrm_linux.go", "xfrm_linux.go",
"xfrm_monitor_linux.go",
"xfrm_policy_linux.go", "xfrm_policy_linux.go",
"xfrm_state_linux.go", "xfrm_state_linux.go",
], ],
"//conditions:default": [], "//conditions:default": [],
}), }),
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/github.com/vishvananda/netns:go_default_library",
],
"//conditions:default": [],
}),
) )
filegroup( filegroup(
......
...@@ -45,3 +45,32 @@ func (msg *IfAddrmsg) Serialize() []byte { ...@@ -45,3 +45,32 @@ func (msg *IfAddrmsg) Serialize() []byte {
func (msg *IfAddrmsg) Len() int { func (msg *IfAddrmsg) Len() int {
return syscall.SizeofIfAddrmsg return syscall.SizeofIfAddrmsg
} }
// struct ifa_cacheinfo {
// __u32 ifa_prefered;
// __u32 ifa_valid;
// __u32 cstamp; /* created timestamp, hundredths of seconds */
// __u32 tstamp; /* updated timestamp, hundredths of seconds */
// };
const IFA_CACHEINFO = 6
const SizeofIfaCacheInfo = 0x10
type IfaCacheInfo struct {
IfaPrefered uint32
IfaValid uint32
Cstamp uint32
Tstamp uint32
}
func (msg *IfaCacheInfo) Len() int {
return SizeofIfaCacheInfo
}
func DeserializeIfaCacheInfo(b []byte) *IfaCacheInfo {
return (*IfaCacheInfo)(unsafe.Pointer(&b[0:SizeofIfaCacheInfo][0]))
}
func (msg *IfaCacheInfo) Serialize() []byte {
return (*(*[SizeofIfaCacheInfo]byte)(unsafe.Pointer(msg)))[:]
}
package nl
import (
"fmt"
"unsafe"
)
const (
SizeofBridgeVlanInfo = 0x04
)
/* Bridge Flags */
const (
BRIDGE_FLAGS_MASTER = iota /* Bridge command to/from master */
BRIDGE_FLAGS_SELF /* Bridge command to/from lowerdev */
)
/* Bridge management nested attributes
* [IFLA_AF_SPEC] = {
* [IFLA_BRIDGE_FLAGS]
* [IFLA_BRIDGE_MODE]
* [IFLA_BRIDGE_VLAN_INFO]
* }
*/
const (
IFLA_BRIDGE_FLAGS = iota
IFLA_BRIDGE_MODE
IFLA_BRIDGE_VLAN_INFO
)
const (
BRIDGE_VLAN_INFO_MASTER = 1 << iota
BRIDGE_VLAN_INFO_PVID
BRIDGE_VLAN_INFO_UNTAGGED
BRIDGE_VLAN_INFO_RANGE_BEGIN
BRIDGE_VLAN_INFO_RANGE_END
)
// struct bridge_vlan_info {
// __u16 flags;
// __u16 vid;
// };
type BridgeVlanInfo struct {
Flags uint16
Vid uint16
}
func (b *BridgeVlanInfo) Serialize() []byte {
return (*(*[SizeofBridgeVlanInfo]byte)(unsafe.Pointer(b)))[:]
}
func DeserializeBridgeVlanInfo(b []byte) *BridgeVlanInfo {
return (*BridgeVlanInfo)(unsafe.Pointer(&b[0:SizeofBridgeVlanInfo][0]))
}
func (b *BridgeVlanInfo) PortVID() bool {
return b.Flags&BRIDGE_VLAN_INFO_PVID > 0
}
func (b *BridgeVlanInfo) EngressUntag() bool {
return b.Flags&BRIDGE_VLAN_INFO_UNTAGGED > 0
}
func (b *BridgeVlanInfo) String() string {
return fmt.Sprintf("%+v", *b)
}
/* New extended info filters for IFLA_EXT_MASK */
const (
RTEXT_FILTER_VF = 1 << iota
RTEXT_FILTER_BRVLAN
RTEXT_FILTER_BRVLAN_COMPRESSED
)
package nl
import "unsafe"
// Track the message sizes for the correct serialization/deserialization
const (
SizeofNfgenmsg = 4
SizeofNfattr = 4
SizeofNfConntrack = 376
SizeofNfctTupleHead = 52
)
var L4ProtoMap = map[uint8]string{
6: "tcp",
17: "udp",
}
// All the following constants are coming from:
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink_conntrack.h
// enum cntl_msg_types {
// IPCTNL_MSG_CT_NEW,
// IPCTNL_MSG_CT_GET,
// IPCTNL_MSG_CT_DELETE,
// IPCTNL_MSG_CT_GET_CTRZERO,
// IPCTNL_MSG_CT_GET_STATS_CPU,
// IPCTNL_MSG_CT_GET_STATS,
// IPCTNL_MSG_CT_GET_DYING,
// IPCTNL_MSG_CT_GET_UNCONFIRMED,
//
// IPCTNL_MSG_MAX
// };
const (
IPCTNL_MSG_CT_GET = 1
IPCTNL_MSG_CT_DELETE = 2
)
// #define NFNETLINK_V0 0
const (
NFNETLINK_V0 = 0
)
// #define NLA_F_NESTED (1 << 15)
const (
NLA_F_NESTED = (1 << 15)
)
// enum ctattr_type {
// CTA_UNSPEC,
// CTA_TUPLE_ORIG,
// CTA_TUPLE_REPLY,
// CTA_STATUS,
// CTA_PROTOINFO,
// CTA_HELP,
// CTA_NAT_SRC,
// #define CTA_NAT CTA_NAT_SRC /* backwards compatibility */
// CTA_TIMEOUT,
// CTA_MARK,
// CTA_COUNTERS_ORIG,
// CTA_COUNTERS_REPLY,
// CTA_USE,
// CTA_ID,
// CTA_NAT_DST,
// CTA_TUPLE_MASTER,
// CTA_SEQ_ADJ_ORIG,
// CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG,
// CTA_SEQ_ADJ_REPLY,
// CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY,
// CTA_SECMARK, /* obsolete */
// CTA_ZONE,
// CTA_SECCTX,
// CTA_TIMESTAMP,
// CTA_MARK_MASK,
// CTA_LABELS,
// CTA_LABELS_MASK,
// __CTA_MAX
// };
const (
CTA_TUPLE_ORIG = 1
CTA_TUPLE_REPLY = 2
CTA_STATUS = 3
CTA_TIMEOUT = 7
CTA_MARK = 8
CTA_PROTOINFO = 4
)
// enum ctattr_tuple {
// CTA_TUPLE_UNSPEC,
// CTA_TUPLE_IP,
// CTA_TUPLE_PROTO,
// CTA_TUPLE_ZONE,
// __CTA_TUPLE_MAX
// };
// #define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
const (
CTA_TUPLE_IP = 1
CTA_TUPLE_PROTO = 2
)
// enum ctattr_ip {
// CTA_IP_UNSPEC,
// CTA_IP_V4_SRC,
// CTA_IP_V4_DST,
// CTA_IP_V6_SRC,
// CTA_IP_V6_DST,
// __CTA_IP_MAX
// };
// #define CTA_IP_MAX (__CTA_IP_MAX - 1)
const (
CTA_IP_V4_SRC = 1
CTA_IP_V4_DST = 2
CTA_IP_V6_SRC = 3
CTA_IP_V6_DST = 4
)
// enum ctattr_l4proto {
// CTA_PROTO_UNSPEC,
// CTA_PROTO_NUM,
// CTA_PROTO_SRC_PORT,
// CTA_PROTO_DST_PORT,
// CTA_PROTO_ICMP_ID,
// CTA_PROTO_ICMP_TYPE,
// CTA_PROTO_ICMP_CODE,
// CTA_PROTO_ICMPV6_ID,
// CTA_PROTO_ICMPV6_TYPE,
// CTA_PROTO_ICMPV6_CODE,
// __CTA_PROTO_MAX
// };
// #define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
const (
CTA_PROTO_NUM = 1
CTA_PROTO_SRC_PORT = 2
CTA_PROTO_DST_PORT = 3
)
// enum ctattr_protoinfo {
// CTA_PROTOINFO_UNSPEC,
// CTA_PROTOINFO_TCP,
// CTA_PROTOINFO_DCCP,
// CTA_PROTOINFO_SCTP,
// __CTA_PROTOINFO_MAX
// };
// #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
const (
CTA_PROTOINFO_TCP = 1
)
// enum ctattr_protoinfo_tcp {
// CTA_PROTOINFO_TCP_UNSPEC,
// CTA_PROTOINFO_TCP_STATE,
// CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
// CTA_PROTOINFO_TCP_WSCALE_REPLY,
// CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
// CTA_PROTOINFO_TCP_FLAGS_REPLY,
// __CTA_PROTOINFO_TCP_MAX
// };
// #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
const (
CTA_PROTOINFO_TCP_STATE = 1
CTA_PROTOINFO_TCP_WSCALE_ORIGINAL = 2
CTA_PROTOINFO_TCP_WSCALE_REPLY = 3
CTA_PROTOINFO_TCP_FLAGS_ORIGINAL = 4
CTA_PROTOINFO_TCP_FLAGS_REPLY = 5
)
// /* General form of address family dependent message.
// */
// struct nfgenmsg {
// __u8 nfgen_family; /* AF_xxx */
// __u8 version; /* nfnetlink version */
// __be16 res_id; /* resource id */
// };
type Nfgenmsg struct {
NfgenFamily uint8
Version uint8
ResId uint16 // big endian
}
func (msg *Nfgenmsg) Len() int {
return SizeofNfgenmsg
}
func DeserializeNfgenmsg(b []byte) *Nfgenmsg {
return (*Nfgenmsg)(unsafe.Pointer(&b[0:SizeofNfgenmsg][0]))
}
func (msg *Nfgenmsg) Serialize() []byte {
return (*(*[SizeofNfgenmsg]byte)(unsafe.Pointer(msg)))[:]
}
package nl
import (
"unsafe"
)
const SizeofGenlmsg = 4
const (
GENL_ID_CTRL = 0x10
GENL_CTRL_VERSION = 2
GENL_CTRL_NAME = "nlctrl"
)
const (
GENL_CTRL_CMD_GETFAMILY = 3
)
const (
GENL_CTRL_ATTR_UNSPEC = iota
GENL_CTRL_ATTR_FAMILY_ID
GENL_CTRL_ATTR_FAMILY_NAME
GENL_CTRL_ATTR_VERSION
GENL_CTRL_ATTR_HDRSIZE
GENL_CTRL_ATTR_MAXATTR
GENL_CTRL_ATTR_OPS
GENL_CTRL_ATTR_MCAST_GROUPS
)
const (
GENL_CTRL_ATTR_OP_UNSPEC = iota
GENL_CTRL_ATTR_OP_ID
GENL_CTRL_ATTR_OP_FLAGS
)
const (
GENL_ADMIN_PERM = 1 << iota
GENL_CMD_CAP_DO
GENL_CMD_CAP_DUMP
GENL_CMD_CAP_HASPOL
)
const (
GENL_CTRL_ATTR_MCAST_GRP_UNSPEC = iota
GENL_CTRL_ATTR_MCAST_GRP_NAME
GENL_CTRL_ATTR_MCAST_GRP_ID
)
const (
GENL_GTP_VERSION = 0
GENL_GTP_NAME = "gtp"
)
const (
GENL_GTP_CMD_NEWPDP = iota
GENL_GTP_CMD_DELPDP
GENL_GTP_CMD_GETPDP
)
const (
GENL_GTP_ATTR_UNSPEC = iota
GENL_GTP_ATTR_LINK
GENL_GTP_ATTR_VERSION
GENL_GTP_ATTR_TID
GENL_GTP_ATTR_PEER_ADDRESS
GENL_GTP_ATTR_MS_ADDRESS
GENL_GTP_ATTR_FLOW
GENL_GTP_ATTR_NET_NS_FD
GENL_GTP_ATTR_I_TEI
GENL_GTP_ATTR_O_TEI
GENL_GTP_ATTR_PAD
)
type Genlmsg struct {
Command uint8
Version uint8
}
func (msg *Genlmsg) Len() int {
return SizeofGenlmsg
}
func DeserializeGenlmsg(b []byte) *Genlmsg {
return (*Genlmsg)(unsafe.Pointer(&b[0:SizeofGenlmsg][0]))
}
func (msg *Genlmsg) Serialize() []byte {
return (*(*[SizeofGenlmsg]byte)(unsafe.Pointer(msg)))[:]
}
package nl package nl
import (
"syscall"
"unsafe"
)
const ( const (
DEFAULT_CHANGE = 0xFFFFFFFF DEFAULT_CHANGE = 0xFFFFFFFF
// doesn't exist in syscall
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 (
...@@ -74,7 +102,10 @@ const ( ...@@ -74,7 +102,10 @@ const (
IFLA_BRPORT_FAST_LEAVE IFLA_BRPORT_FAST_LEAVE
IFLA_BRPORT_LEARNING IFLA_BRPORT_LEARNING
IFLA_BRPORT_UNICAST_FLOOD IFLA_BRPORT_UNICAST_FLOOD
IFLA_BRPORT_MAX = IFLA_BRPORT_UNICAST_FLOOD IFLA_BRPORT_PROXYARP
IFLA_BRPORT_LEARNING_SYNC
IFLA_BRPORT_PROXYARP_WIFI
IFLA_BRPORT_MAX = IFLA_BRPORT_PROXYARP_WIFI
) )
const ( const (
...@@ -84,11 +115,6 @@ const ( ...@@ -84,11 +115,6 @@ const (
) )
const ( const (
// not defined in syscall
IFLA_NET_NS_FD = 28
)
const (
IFLA_MACVLAN_UNSPEC = iota IFLA_MACVLAN_UNSPEC = iota
IFLA_MACVLAN_MODE IFLA_MACVLAN_MODE
IFLA_MACVLAN_FLAGS IFLA_MACVLAN_FLAGS
...@@ -102,3 +128,398 @@ const ( ...@@ -102,3 +128,398 @@ const (
MACVLAN_MODE_PASSTHRU = 8 MACVLAN_MODE_PASSTHRU = 8
MACVLAN_MODE_SOURCE = 16 MACVLAN_MODE_SOURCE = 16
) )
const (
IFLA_BOND_UNSPEC = iota
IFLA_BOND_MODE
IFLA_BOND_ACTIVE_SLAVE
IFLA_BOND_MIIMON
IFLA_BOND_UPDELAY
IFLA_BOND_DOWNDELAY
IFLA_BOND_USE_CARRIER
IFLA_BOND_ARP_INTERVAL
IFLA_BOND_ARP_IP_TARGET
IFLA_BOND_ARP_VALIDATE
IFLA_BOND_ARP_ALL_TARGETS
IFLA_BOND_PRIMARY
IFLA_BOND_PRIMARY_RESELECT
IFLA_BOND_FAIL_OVER_MAC
IFLA_BOND_XMIT_HASH_POLICY
IFLA_BOND_RESEND_IGMP
IFLA_BOND_NUM_PEER_NOTIF
IFLA_BOND_ALL_SLAVES_ACTIVE
IFLA_BOND_MIN_LINKS
IFLA_BOND_LP_INTERVAL
IFLA_BOND_PACKETS_PER_SLAVE
IFLA_BOND_AD_LACP_RATE
IFLA_BOND_AD_SELECT
IFLA_BOND_AD_INFO
IFLA_BOND_AD_ACTOR_SYS_PRIO
IFLA_BOND_AD_USER_PORT_KEY
IFLA_BOND_AD_ACTOR_SYSTEM
IFLA_BOND_TLB_DYNAMIC_LB
)
const (
IFLA_BOND_AD_INFO_UNSPEC = iota
IFLA_BOND_AD_INFO_AGGREGATOR
IFLA_BOND_AD_INFO_NUM_PORTS
IFLA_BOND_AD_INFO_ACTOR_KEY
IFLA_BOND_AD_INFO_PARTNER_KEY
IFLA_BOND_AD_INFO_PARTNER_MAC
)
const (
IFLA_BOND_SLAVE_UNSPEC = iota
IFLA_BOND_SLAVE_STATE
IFLA_BOND_SLAVE_MII_STATUS
IFLA_BOND_SLAVE_LINK_FAILURE_COUNT
IFLA_BOND_SLAVE_PERM_HWADDR
IFLA_BOND_SLAVE_QUEUE_ID
IFLA_BOND_SLAVE_AD_AGGREGATOR_ID
)
const (
IFLA_GRE_UNSPEC = iota
IFLA_GRE_LINK
IFLA_GRE_IFLAGS
IFLA_GRE_OFLAGS
IFLA_GRE_IKEY
IFLA_GRE_OKEY
IFLA_GRE_LOCAL
IFLA_GRE_REMOTE
IFLA_GRE_TTL
IFLA_GRE_TOS
IFLA_GRE_PMTUDISC
IFLA_GRE_ENCAP_LIMIT
IFLA_GRE_FLOWINFO
IFLA_GRE_FLAGS
IFLA_GRE_ENCAP_TYPE
IFLA_GRE_ENCAP_FLAGS
IFLA_GRE_ENCAP_SPORT
IFLA_GRE_ENCAP_DPORT
IFLA_GRE_COLLECT_METADATA
IFLA_GRE_MAX = IFLA_GRE_COLLECT_METADATA
)
const (
GRE_CSUM = 0x8000
GRE_ROUTING = 0x4000
GRE_KEY = 0x2000
GRE_SEQ = 0x1000
GRE_STRICT = 0x0800
GRE_REC = 0x0700
GRE_FLAGS = 0x00F8
GRE_VERSION = 0x0007
)
const (
IFLA_VF_INFO_UNSPEC = iota
IFLA_VF_INFO
IFLA_VF_INFO_MAX = IFLA_VF_INFO
)
const (
IFLA_VF_UNSPEC = iota
IFLA_VF_MAC /* Hardware queue specific attributes */
IFLA_VF_VLAN
IFLA_VF_TX_RATE /* Max TX Bandwidth Allocation */
IFLA_VF_SPOOFCHK /* Spoof Checking on/off switch */
IFLA_VF_LINK_STATE /* link state enable/disable/auto switch */
IFLA_VF_RATE /* Min and Max TX Bandwidth Allocation */
IFLA_VF_RSS_QUERY_EN /* RSS Redirection Table and Hash Key query
* on/off switch
*/
IFLA_VF_STATS /* network device statistics */
IFLA_VF_MAX = IFLA_VF_STATS
)
const (
IFLA_VF_LINK_STATE_AUTO = iota /* link state of the uplink */
IFLA_VF_LINK_STATE_ENABLE /* link always up */
IFLA_VF_LINK_STATE_DISABLE /* link always down */
IFLA_VF_LINK_STATE_MAX = IFLA_VF_LINK_STATE_DISABLE
)
const (
IFLA_VF_STATS_RX_PACKETS = iota
IFLA_VF_STATS_TX_PACKETS
IFLA_VF_STATS_RX_BYTES
IFLA_VF_STATS_TX_BYTES
IFLA_VF_STATS_BROADCAST
IFLA_VF_STATS_MULTICAST
IFLA_VF_STATS_MAX = IFLA_VF_STATS_MULTICAST
)
const (
SizeofVfMac = 0x24
SizeofVfVlan = 0x0c
SizeofVfTxRate = 0x08
SizeofVfRate = 0x0c
SizeofVfSpoofchk = 0x08
SizeofVfLinkState = 0x08
SizeofVfRssQueryEn = 0x08
)
// struct ifla_vf_mac {
// __u32 vf;
// __u8 mac[32]; /* MAX_ADDR_LEN */
// };
type VfMac struct {
Vf uint32
Mac [32]byte
}
func (msg *VfMac) Len() int {
return SizeofVfMac
}
func DeserializeVfMac(b []byte) *VfMac {
return (*VfMac)(unsafe.Pointer(&b[0:SizeofVfMac][0]))
}
func (msg *VfMac) Serialize() []byte {
return (*(*[SizeofVfMac]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_vlan {
// __u32 vf;
// __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
// __u32 qos;
// };
type VfVlan struct {
Vf uint32
Vlan uint32
Qos uint32
}
func (msg *VfVlan) Len() int {
return SizeofVfVlan
}
func DeserializeVfVlan(b []byte) *VfVlan {
return (*VfVlan)(unsafe.Pointer(&b[0:SizeofVfVlan][0]))
}
func (msg *VfVlan) Serialize() []byte {
return (*(*[SizeofVfVlan]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_tx_rate {
// __u32 vf;
// __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
// };
type VfTxRate struct {
Vf uint32
Rate uint32
}
func (msg *VfTxRate) Len() int {
return SizeofVfTxRate
}
func DeserializeVfTxRate(b []byte) *VfTxRate {
return (*VfTxRate)(unsafe.Pointer(&b[0:SizeofVfTxRate][0]))
}
func (msg *VfTxRate) Serialize() []byte {
return (*(*[SizeofVfTxRate]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_rate {
// __u32 vf;
// __u32 min_tx_rate; /* Min Bandwidth in Mbps */
// __u32 max_tx_rate; /* Max Bandwidth in Mbps */
// };
type VfRate struct {
Vf uint32
MinTxRate uint32
MaxTxRate uint32
}
func (msg *VfRate) Len() int {
return SizeofVfRate
}
func DeserializeVfRate(b []byte) *VfRate {
return (*VfRate)(unsafe.Pointer(&b[0:SizeofVfRate][0]))
}
func (msg *VfRate) Serialize() []byte {
return (*(*[SizeofVfRate]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_spoofchk {
// __u32 vf;
// __u32 setting;
// };
type VfSpoofchk struct {
Vf uint32
Setting uint32
}
func (msg *VfSpoofchk) Len() int {
return SizeofVfSpoofchk
}
func DeserializeVfSpoofchk(b []byte) *VfSpoofchk {
return (*VfSpoofchk)(unsafe.Pointer(&b[0:SizeofVfSpoofchk][0]))
}
func (msg *VfSpoofchk) Serialize() []byte {
return (*(*[SizeofVfSpoofchk]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_link_state {
// __u32 vf;
// __u32 link_state;
// };
type VfLinkState struct {
Vf uint32
LinkState uint32
}
func (msg *VfLinkState) Len() int {
return SizeofVfLinkState
}
func DeserializeVfLinkState(b []byte) *VfLinkState {
return (*VfLinkState)(unsafe.Pointer(&b[0:SizeofVfLinkState][0]))
}
func (msg *VfLinkState) Serialize() []byte {
return (*(*[SizeofVfLinkState]byte)(unsafe.Pointer(msg)))[:]
}
// struct ifla_vf_rss_query_en {
// __u32 vf;
// __u32 setting;
// };
type VfRssQueryEn struct {
Vf uint32
Setting uint32
}
func (msg *VfRssQueryEn) Len() int {
return SizeofVfRssQueryEn
}
func DeserializeVfRssQueryEn(b []byte) *VfRssQueryEn {
return (*VfRssQueryEn)(unsafe.Pointer(&b[0:SizeofVfRssQueryEn][0]))
}
func (msg *VfRssQueryEn) Serialize() []byte {
return (*(*[SizeofVfRssQueryEn]byte)(unsafe.Pointer(msg)))[:]
}
const (
IFLA_XDP_UNSPEC = iota
IFLA_XDP_FD /* fd of xdp program to attach, or -1 to remove */
IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */
IFLA_XDP_FLAGS /* xdp prog related flags */
IFLA_XDP_PROG_ID /* xdp prog id */
IFLA_XDP_MAX = IFLA_XDP_PROG_ID
)
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
)
const (
IFLA_BR_UNSPEC = iota
IFLA_BR_FORWARD_DELAY
IFLA_BR_HELLO_TIME
IFLA_BR_MAX_AGE
IFLA_BR_AGEING_TIME
IFLA_BR_STP_STATE
IFLA_BR_PRIORITY
IFLA_BR_VLAN_FILTERING
IFLA_BR_VLAN_PROTOCOL
IFLA_BR_GROUP_FWD_MASK
IFLA_BR_ROOT_ID
IFLA_BR_BRIDGE_ID
IFLA_BR_ROOT_PORT
IFLA_BR_ROOT_PATH_COST
IFLA_BR_TOPOLOGY_CHANGE
IFLA_BR_TOPOLOGY_CHANGE_DETECTED
IFLA_BR_HELLO_TIMER
IFLA_BR_TCN_TIMER
IFLA_BR_TOPOLOGY_CHANGE_TIMER
IFLA_BR_GC_TIMER
IFLA_BR_GROUP_ADDR
IFLA_BR_FDB_FLUSH
IFLA_BR_MCAST_ROUTER
IFLA_BR_MCAST_SNOOPING
IFLA_BR_MCAST_QUERY_USE_IFADDR
IFLA_BR_MCAST_QUERIER
IFLA_BR_MCAST_HASH_ELASTICITY
IFLA_BR_MCAST_HASH_MAX
IFLA_BR_MCAST_LAST_MEMBER_CNT
IFLA_BR_MCAST_STARTUP_QUERY_CNT
IFLA_BR_MCAST_LAST_MEMBER_INTVL
IFLA_BR_MCAST_MEMBERSHIP_INTVL
IFLA_BR_MCAST_QUERIER_INTVL
IFLA_BR_MCAST_QUERY_INTVL
IFLA_BR_MCAST_QUERY_RESPONSE_INTVL
IFLA_BR_MCAST_STARTUP_QUERY_INTVL
IFLA_BR_NF_CALL_IPTABLES
IFLA_BR_NF_CALL_IP6TABLES
IFLA_BR_NF_CALL_ARPTABLES
IFLA_BR_VLAN_DEFAULT_PVID
IFLA_BR_PAD
IFLA_BR_VLAN_STATS_ENABLED
IFLA_BR_MCAST_STATS_ENABLED
IFLA_BR_MCAST_IGMP_VERSION
IFLA_BR_MCAST_MLD_VERSION
IFLA_BR_MAX = IFLA_BR_MCAST_MLD_VERSION
)
const (
IFLA_GTP_UNSPEC = iota
IFLA_GTP_FD0
IFLA_GTP_FD1
IFLA_GTP_PDP_HASHSIZE
IFLA_GTP_ROLE
)
const (
GTP_ROLE_GGSN = iota
GTP_ROLE_SGSN
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment