From 9a053a4b59fe3f114bd6946829a058cf6b57eec4 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Mon, 3 Nov 2014 08:04:42 -0800 Subject: [PATCH] Initial ipv6 / iptables work --- cmd/proxy/proxy.go | 6 ++++- pkg/proxy/proxier.go | 14 +++++++--- pkg/proxy/proxier_test.go | 4 +++ pkg/util/iptables/iptables.go | 32 ++++++++++++++++++---- pkg/util/iptables/iptables_test.go | 43 +++++++++++++++++++++--------- 5 files changed, 77 insertions(+), 22 deletions(-) diff --git a/cmd/proxy/proxy.go b/cmd/proxy/proxy.go index b9c3fdb1d9c..2d0e6c9734a 100644 --- a/cmd/proxy/proxy.go +++ b/cmd/proxy/proxy.go @@ -100,8 +100,12 @@ func main() { } } + protocol := iptables.ProtocolIpv4 + if net.IP(bindAddress).To4() == nil { + protocol = iptables.ProtocolIpv6 + } loadBalancer := proxy.NewLoadBalancerRR() - proxier := proxy.NewProxier(loadBalancer, net.IP(bindAddress), iptables.New(exec.New())) + proxier := proxy.NewProxier(loadBalancer, net.IP(bindAddress), iptables.New(exec.New(), protocol)) // Wire proxier to handle changes to services serviceConfig.RegisterHandler(proxier) // And wire loadBalancer to handle changes to endpoints to services diff --git a/pkg/proxy/proxier.go b/pkg/proxy/proxier.go index 3e6479085f7..91de74c1d70 100644 --- a/pkg/proxy/proxier.go +++ b/pkg/proxy/proxier.go @@ -529,8 +529,11 @@ func iptablesFlush(ipt iptables.Interface) error { } // Used below. -var zeroIP = net.ParseIP("0.0.0.0") -var localhostIP = net.ParseIP("127.0.0.1") +var zeroIPv4 = net.ParseIP("0.0.0.0") +var localhostIPv4 = net.ParseIP("127.0.0.1") + +var zeroIPv6 = net.ParseIP("::0") +var localhostIPv6 = net.ParseIP("::1") // Build a slice of iptables args for a portal rule. func iptablesPortalArgs(destIP net.IP, destPort int, proxyIP net.IP, proxyPort int, service string) []string { @@ -558,10 +561,13 @@ func iptablesPortalArgs(destIP net.IP, destPort int, proxyIP net.IP, proxyPort i // Unfortunately, I don't know of any way to listen on some (N > 1) // interfaces but not ALL interfaces, short of doing it manually, and // this is simpler than that. - if proxyIP.Equal(zeroIP) || proxyIP.Equal(localhostIP) { + if proxyIP.Equal(zeroIPv4) || proxyIP.Equal(zeroIPv6) || + proxyIP.Equal(localhostIPv4) || proxyIP.Equal(localhostIPv6) { + // TODO: Can we REDIRECT with IPv6? args = append(args, "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", proxyPort)) } else { - args = append(args, "-j", "DNAT", "--to-destination", fmt.Sprintf("%s:%d", proxyIP.String(), proxyPort)) + // TODO: Can we DNAT with IPv6? + args = append(args, "-j", "DNAT", "--to-destination", net.JoinHostPort(proxyIP.String(), strconv.Itoa(proxyPort))) } return args } diff --git a/pkg/proxy/proxier_test.go b/pkg/proxy/proxier_test.go index 196a4d5e71e..410c7bb1f6a 100644 --- a/pkg/proxy/proxier_test.go +++ b/pkg/proxy/proxier_test.go @@ -93,6 +93,10 @@ func (fake *fakeIptables) DeleteRule(table iptables.Table, chain iptables.Chain, return nil } +func (fake *fakeIptables) IsIpv6() bool { + return false +} + var tcpServerPort string var udpServerPort string diff --git a/pkg/util/iptables/iptables.go b/pkg/util/iptables/iptables.go index 5253c2d200b..d4dab06df32 100644 --- a/pkg/util/iptables/iptables.go +++ b/pkg/util/iptables/iptables.go @@ -34,8 +34,17 @@ type Interface interface { EnsureRule(table Table, chain Chain, args ...string) (bool, error) // DeleteRule checks if the specified rule is present and, if so, deletes it. DeleteRule(table Table, chain Chain, args ...string) error + // IsIpv6 returns true if this is managing ipv6 tables + IsIpv6() bool } +type Protocol bool + +const ( + ProtocolIpv4 Protocol = false + ProtocolIpv6 Protocol = true +) + type Table string const ( @@ -51,13 +60,14 @@ const ( // runner implements Interface in terms of exec("iptables"). type runner struct { - mu sync.Mutex - exec utilexec.Interface + mu sync.Mutex + exec utilexec.Interface + protocol Protocol } // New returns a new Interface which will exec iptables. -func New(exec utilexec.Interface) Interface { - return &runner{exec: exec} +func New(exec utilexec.Interface, protocol Protocol) Interface { + return &runner{exec: exec, protocol: protocol} } // EnsureChain is part of Interface. @@ -135,8 +145,20 @@ func (runner *runner) DeleteRule(table Table, chain Chain, args ...string) error return nil } +func (runner *runner) IsIpv6() bool { + return runner.protocol == ProtocolIpv6 +} + +func (runner *runner) iptablesCommand() string { + if runner.IsIpv6() { + return "ip6tables" + } else { + return "iptables" + } +} + func (runner *runner) run(op operation, args []string) ([]byte, error) { - const iptablesCmd = "iptables" + iptablesCmd := runner.iptablesCommand() fullArgs := append([]string{string(op)}, args...) glog.V(1).Infof("running iptables %s %v", string(op), args) diff --git a/pkg/util/iptables/iptables_test.go b/pkg/util/iptables/iptables_test.go index f027ab351b0..34bdc99f521 100644 --- a/pkg/util/iptables/iptables_test.go +++ b/pkg/util/iptables/iptables_test.go @@ -23,7 +23,17 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" ) -func TestEnsureChain(t *testing.T) { +func getIptablesCommand(protocol Protocol) string { + if protocol == ProtocolIpv4 { + return "iptables" + } + if protocol == ProtocolIpv6 { + return "ip6tables" + } + panic("Unknown protocol") +} + +func testEnsureChain(t *testing.T, protocol Protocol) { fcmd := exec.FakeCmd{ CombinedOutputScript: []exec.FakeCombinedOutputAction{ // Success. @@ -41,7 +51,7 @@ func TestEnsureChain(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, protocol) // Success. exists, err := runner.EnsureChain(TableNAT, Chain("FOOBAR")) if err != nil { @@ -53,7 +63,8 @@ func TestEnsureChain(t *testing.T) { if fcmd.CombinedOutputCalls != 1 { t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls) } - if !util.NewStringSet(fcmd.CombinedOutputLog[0]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") { + cmd := getIptablesCommand(protocol) + if !util.NewStringSet(fcmd.CombinedOutputLog[0]...).HasAll(cmd, "-t", "nat", "-N", "FOOBAR") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) } // Exists. @@ -71,6 +82,14 @@ func TestEnsureChain(t *testing.T) { } } +func TestEnsureChainIpv4(t *testing.T) { + testEnsureChain(t, ProtocolIpv4) +} + +func TestEnsureChainIpv6(t *testing.T) { + testEnsureChain(t, ProtocolIpv6) +} + func TestFlushChain(t *testing.T) { fcmd := exec.FakeCmd{ CombinedOutputScript: []exec.FakeCombinedOutputAction{ @@ -86,7 +105,7 @@ func TestFlushChain(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) // Success. err := runner.FlushChain(TableNAT, Chain("FOOBAR")) if err != nil { @@ -118,7 +137,7 @@ func TestEnsureRuleAlreadyExists(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) exists, err := runner.EnsureRule(TableNAT, ChainOutput, "abc", "123") if err != nil { t.Errorf("expected success, got %+v", err) @@ -150,7 +169,7 @@ func TestEnsureRuleNew(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) exists, err := runner.EnsureRule(TableNAT, ChainOutput, "abc", "123") if err != nil { t.Errorf("expected success, got %+v", err) @@ -179,7 +198,7 @@ func TestEnsureRuleErrorChecking(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) _, err := runner.EnsureRule(TableNAT, ChainOutput, "abc", "123") if err == nil { t.Errorf("expected failure") @@ -205,7 +224,7 @@ func TestEnsureRuleErrorCreating(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) _, err := runner.EnsureRule(TableNAT, ChainOutput, "abc", "123") if err == nil { t.Errorf("expected failure") @@ -228,7 +247,7 @@ func TestDeleteRuleAlreadyExists(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") if err != nil { t.Errorf("expected success, got %+v", err) @@ -257,7 +276,7 @@ func TestDeleteRuleNew(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") if err != nil { t.Errorf("expected success, got %+v", err) @@ -283,7 +302,7 @@ func TestDeleteRuleErrorChecking(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") if err == nil { t.Errorf("expected failure") @@ -309,7 +328,7 @@ func TestDeleteRuleErrorCreating(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } - runner := New(&fexec) + runner := New(&fexec, ProtocolIpv4) err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") if err == nil { t.Errorf("expected failure")