From 56329911158d3d01a321f320bd13037a36794c4d Mon Sep 17 00:00:00 2001 From: Surya Seetharaman Date: Sat, 29 Jan 2022 12:00:37 +0100 Subject: [PATCH] Local Traffic Detector: Add two new modes This PR introduces two new modes for detecting local traffic in a cluster. 1) detectLocalByBridgeInterface: This takes a bridge name as argument and decides all traffic that match on their originating interface being that of this bridge, shall be considered as local pod traffic. 2) detectLocalByInterfaceNamePrefix: This takes an interface prefix name as argument and decides all traffic that match on their originating interface names having a prefix that matches this argument shall be considered as local pod traffic. Signed-off-by: Surya Seetharaman --- pkg/proxy/util/iptables/traffic.go | 59 +++++++++++ pkg/proxy/util/iptables/traffic_test.go | 132 ++++++++++++++++++++++++ 2 files changed, 191 insertions(+) diff --git a/pkg/proxy/util/iptables/traffic.go b/pkg/proxy/util/iptables/traffic.go index f96f8f44e09..4666c6c3de6 100644 --- a/pkg/proxy/util/iptables/traffic.go +++ b/pkg/proxy/util/iptables/traffic.go @@ -87,3 +87,62 @@ func (d *detectLocalByCIDR) IfLocal() []string { func (d *detectLocalByCIDR) IfNotLocal() []string { return d.ifNotLocal } + +type detectLocalByBridgeInterface struct { + ifLocal []string + ifNotLocal []string +} + +// NewDetectLocalByBridgeInterface implements the LocalTrafficDetector interface using a bridge interface name. +// This can be used when a bridge can be used to capture the notion of local traffic from pods. +func NewDetectLocalByBridgeInterface(interfaceName string) (LocalTrafficDetector, error) { + if len(interfaceName) == 0 { + return nil, fmt.Errorf("no bridge interface name set") + } + return &detectLocalByBridgeInterface{ + ifLocal: []string{"-i", interfaceName}, + ifNotLocal: []string{"!", "-i", interfaceName}, + }, nil +} + +func (d *detectLocalByBridgeInterface) IsImplemented() bool { + return true +} + +func (d *detectLocalByBridgeInterface) IfLocal() []string { + return d.ifLocal +} + +func (d *detectLocalByBridgeInterface) IfNotLocal() []string { + return d.ifNotLocal +} + +type detectLocalByInterfaceNamePrefix struct { + ifLocal []string + ifNotLocal []string +} + +// NewDetectLocalByInterfaceNamePrefix implements the LocalTrafficDetector interface using an interface name prefix. +// This can be used when a pod interface name prefix can be used to capture the notion of local traffic. Note +// that this will match on all interfaces that start with the given prefix. +func NewDetectLocalByInterfaceNamePrefix(interfacePrefix string) (LocalTrafficDetector, error) { + if len(interfacePrefix) == 0 { + return nil, fmt.Errorf("no interface prefix set") + } + return &detectLocalByInterfaceNamePrefix{ + ifLocal: []string{"-i", interfacePrefix + "+"}, + ifNotLocal: []string{"!", "-i", interfacePrefix + "+"}, + }, nil +} + +func (d *detectLocalByInterfaceNamePrefix) IsImplemented() bool { + return true +} + +func (d *detectLocalByInterfaceNamePrefix) IfLocal() []string { + return d.ifLocal +} + +func (d *detectLocalByInterfaceNamePrefix) IfNotLocal() []string { + return d.ifNotLocal +} diff --git a/pkg/proxy/util/iptables/traffic_test.go b/pkg/proxy/util/iptables/traffic_test.go index d54ec61f896..f74b850b2fa 100644 --- a/pkg/proxy/util/iptables/traffic_test.go +++ b/pkg/proxy/util/iptables/traffic_test.go @@ -144,3 +144,135 @@ func TestDetectLocalByCIDR(t *testing.T) { } } } + +func TestNewDetectLocalByBridgeInterface(t *testing.T) { + cases := []struct { + ifaceName string + errExpected bool + }{ + { + ifaceName: "avz", + errExpected: false, + }, + { + ifaceName: "", + errExpected: true, + }, + } + for i, c := range cases { + r, err := NewDetectLocalByBridgeInterface(c.ifaceName) + if c.errExpected { + if err == nil { + t.Errorf("Case[%d] expected error, but succeeded with: %q", i, r) + } + continue + } + if err != nil { + t.Errorf("Case[%d] failed with error: %v", i, err) + } + } +} + +func TestNewDetectLocalByInterfaceNamePrefix(t *testing.T) { + cases := []struct { + ifacePrefix string + errExpected bool + }{ + { + ifacePrefix: "veth", + errExpected: false, + }, + { + ifacePrefix: "cbr0", + errExpected: false, + }, + { + ifacePrefix: "", + errExpected: true, + }, + } + for i, c := range cases { + r, err := NewDetectLocalByInterfaceNamePrefix(c.ifacePrefix) + if c.errExpected { + if err == nil { + t.Errorf("Case[%d] expected error, but succeeded with: %q", i, r) + } + continue + } + if err != nil { + t.Errorf("Case[%d] failed with error: %v", i, err) + } + } +} + +func TestDetectLocalByBridgeInterface(t *testing.T) { + cases := []struct { + ifaceName string + expectedJumpIfOutput []string + expectedJumpIfNotOutput []string + }{ + { + ifaceName: "eth0", + expectedJumpIfOutput: []string{"-i", "eth0"}, + expectedJumpIfNotOutput: []string{"!", "-i", "eth0"}, + }, + } + for _, c := range cases { + localDetector, err := NewDetectLocalByBridgeInterface(c.ifaceName) + if err != nil { + t.Errorf("Error initializing localDetector: %v", err) + continue + } + if !localDetector.IsImplemented() { + t.Error("DetectLocalByBridgeInterface returns false for IsImplemented") + } + + ifLocal := localDetector.IfLocal() + ifNotLocal := localDetector.IfNotLocal() + + if !reflect.DeepEqual(ifLocal, c.expectedJumpIfOutput) { + t.Errorf("IfLocal, expected: '%v', but got: '%v'", c.expectedJumpIfOutput, ifLocal) + } + + if !reflect.DeepEqual(ifNotLocal, c.expectedJumpIfNotOutput) { + t.Errorf("IfNotLocal, expected: '%v', but got: '%v'", c.expectedJumpIfNotOutput, ifNotLocal) + } + } +} + +func TestDetectLocalByInterfaceNamePrefix(t *testing.T) { + cases := []struct { + ifacePrefix string + chain string + args []string + expectedJumpIfOutput []string + expectedJumpIfNotOutput []string + }{ + { + ifacePrefix: "eth0", + expectedJumpIfOutput: []string{"-i", "eth0+"}, + expectedJumpIfNotOutput: []string{"!", "-i", "eth0+"}, + }, + } + for _, c := range cases { + localDetector, err := NewDetectLocalByInterfaceNamePrefix(c.ifacePrefix) + if err != nil { + t.Errorf("Error initializing localDetector: %v", err) + continue + } + if !localDetector.IsImplemented() { + t.Error("DetectLocalByInterfaceNamePrefix returns false for IsImplemented") + } + + ifLocal := localDetector.IfLocal() + ifNotLocal := localDetector.IfNotLocal() + + if !reflect.DeepEqual(ifLocal, c.expectedJumpIfOutput) { + t.Errorf("IfLocal, expected: '%v', but got: '%v'", c.expectedJumpIfOutput, ifLocal) + } + + if !reflect.DeepEqual(ifNotLocal, c.expectedJumpIfNotOutput) { + t.Errorf("IfNotLocal, expected: '%v', but got: '%v'", c.expectedJumpIfNotOutput, ifNotLocal) + } + } +}