Browse code

Create tests for pkg/iptables

Docker-DCO-1.1-Signed-off-by: Ian Bishop <ianbishop@pace7.com> (github: porjo)

Ian Bishop authored on 2014/11/15 10:36:38
Showing 6 changed files
... ...
@@ -5,8 +5,6 @@ import (
5 5
 	"io/ioutil"
6 6
 	"net"
7 7
 	"os"
8
-	"strconv"
9
-	"strings"
10 8
 	"sync"
11 9
 
12 10
 	log "github.com/Sirupsen/logrus"
... ...
@@ -513,10 +511,6 @@ func LinkContainers(job *engine.Job) engine.Status {
513 513
 		ports        = job.GetenvList("Ports")
514 514
 		chain        = iptables.Chain{}
515 515
 	)
516
-	split := func(p string) (string, string) {
517
-		parts := strings.Split(p, "/")
518
-		return parts[0], parts[1]
519
-	}
520 516
 
521 517
 	switch action {
522 518
 	case "-A":
... ...
@@ -541,13 +535,11 @@ func LinkContainers(job *engine.Job) engine.Status {
541 541
 	chain.Name = "DOCKER"
542 542
 	chain.Bridge = bridgeIface
543 543
 	for _, p := range ports {
544
-		portStr, proto := split(p)
545
-		port, err := strconv.Atoi(portStr)
546
-		if !ignoreErrors && err != nil {
547
-			return job.Errorf("port '%s' is invalid", portStr)
548
-		}
549
-		if err := chain.Link(nfAction, ip1, ip2, port, proto); !ignoreErrors && err != nil {
544
+		port := nat.Port(p)
545
+		if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil {
546
+			fmt.Print(err)
550 547
 			return job.Error(err)
548
+
551 549
 		}
552 550
 	}
553 551
 	return engine.StatusOK
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/daemon/networkdriver/portmapper"
9 9
 	"github.com/docker/docker/engine"
10
+	"github.com/docker/docker/pkg/iptables"
10 11
 )
11 12
 
12 13
 func init() {
... ...
@@ -118,3 +119,43 @@ func TestMacAddrGeneration(t *testing.T) {
118 118
 		t.Fatal("Non-unique MAC address")
119 119
 	}
120 120
 }
121
+
122
+func TestLinkContainers(t *testing.T) {
123
+	eng := engine.New()
124
+	eng.Logging = false
125
+
126
+	// Init driver
127
+	job := eng.Job("initdriver")
128
+	if res := InitDriver(job); res != engine.StatusOK {
129
+		t.Fatal("Failed to initialize network driver")
130
+	}
131
+
132
+	// Allocate interface
133
+	job = eng.Job("allocate_interface", "container_id")
134
+	if res := Allocate(job); res != engine.StatusOK {
135
+		t.Fatal("Failed to allocate network interface")
136
+	}
137
+
138
+	job.Args[0] = "-I"
139
+
140
+	job.Setenv("ChildIP", "172.17.0.2")
141
+	job.Setenv("ParentIP", "172.17.0.1")
142
+	job.SetenvBool("IgnoreErrors", false)
143
+	job.SetenvList("Ports", []string{"1234"})
144
+
145
+	bridgeIface = "lo"
146
+	_, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter)
147
+	if err != nil {
148
+		t.Fatal(err)
149
+	}
150
+
151
+	if res := LinkContainers(job); res != engine.StatusOK {
152
+		t.Fatalf("LinkContainers failed")
153
+	}
154
+
155
+	// flush rules
156
+	if _, err = iptables.Raw([]string{"-F", "DOCKER"}...); err != nil {
157
+		t.Fatal(err)
158
+	}
159
+
160
+}
... ...
@@ -83,8 +83,8 @@ func TestLinksIpTablesRulesWhenLinkAndUnlink(t *testing.T) {
83 83
 	childIP := findContainerIP(t, "child")
84 84
 	parentIP := findContainerIP(t, "parent")
85 85
 
86
-	sourceRule := []string{"FORWARD", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
87
-	destinationRule := []string{"FORWARD", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
86
+	sourceRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
87
+	destinationRule := []string{"DOCKER", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
88 88
 	if !iptables.Exists(sourceRule...) || !iptables.Exists(destinationRule...) {
89 89
 		t.Fatal("Iptables rules not found")
90 90
 	}
... ...
@@ -2070,7 +2070,7 @@ func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
2070 2070
 	if err != nil {
2071 2071
 		t.Fatal(err)
2072 2072
 	}
2073
-	iptCmd := exec.Command("iptables", "-D", "FORWARD", "-d", fmt.Sprintf("%s/32", ip),
2073
+	iptCmd := exec.Command("iptables", "-D", "DOCKER", "-d", fmt.Sprintf("%s/32", ip),
2074 2074
 		"!", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-m", "tcp", "--dport", "23", "-j", "ACCEPT")
2075 2075
 	out, _, err = runCommandWithOutput(iptCmd)
2076 2076
 	if err != nil {
... ...
@@ -114,7 +114,7 @@ func RemoveExistingChain(name string, table Table) error {
114 114
 }
115 115
 
116 116
 // Add forwarding rule to 'filter' table and corresponding nat rule to 'nat' table
117
-func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
117
+func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
118 118
 	daddr := ip.String()
119 119
 	if ip.IsUnspecified() {
120 120
 		// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
... ...
@@ -128,7 +128,7 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str
128 128
 		"--dport", strconv.Itoa(port),
129 129
 		"!", "-i", c.Bridge,
130 130
 		"-j", "DNAT",
131
-		"--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port))); err != nil {
131
+		"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
132 132
 		return err
133 133
 	} else if len(output) != 0 {
134 134
 		return &ChainError{Chain: "FORWARD", Output: output}
... ...
@@ -138,14 +138,25 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str
138 138
 		"!", "-i", c.Bridge,
139 139
 		"-o", c.Bridge,
140 140
 		"-p", proto,
141
-		"-d", dest_addr,
142
-		"--dport", strconv.Itoa(dest_port),
141
+		"-d", destAddr,
142
+		"--dport", strconv.Itoa(destPort),
143 143
 		"-j", "ACCEPT"); err != nil {
144 144
 		return err
145 145
 	} else if len(output) != 0 {
146 146
 		return &ChainError{Chain: "FORWARD", Output: output}
147 147
 	}
148 148
 
149
+	if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING",
150
+		"-p", proto,
151
+		"-s", destAddr,
152
+		"-d", destAddr,
153
+		"--dport", strconv.Itoa(destPort),
154
+		"-j", "MASQUERADE"); err != nil {
155
+		return err
156
+	} else if len(output) != 0 {
157
+		return &ChainError{Chain: "FORWARD", Output: output}
158
+	}
159
+
149 160
 	return nil
150 161
 }
151 162
 
... ...
@@ -156,8 +167,8 @@ func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) err
156 156
 		"-i", c.Bridge, "-o", c.Bridge,
157 157
 		"-p", proto,
158 158
 		"-s", ip1.String(),
159
-		"--dport", strconv.Itoa(port),
160 159
 		"-d", ip2.String(),
160
+		"--dport", strconv.Itoa(port),
161 161
 		"-j", "ACCEPT"); err != nil {
162 162
 		return err
163 163
 	} else if len(output) != 0 {
... ...
@@ -167,8 +178,8 @@ func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) err
167 167
 		"-i", c.Bridge, "-o", c.Bridge,
168 168
 		"-p", proto,
169 169
 		"-s", ip2.String(),
170
-		"--dport", strconv.Itoa(port),
171 170
 		"-d", ip1.String(),
171
+		"--sport", strconv.Itoa(port),
172 172
 		"-j", "ACCEPT"); err != nil {
173 173
 		return err
174 174
 	} else if len(output) != 0 {
... ...
@@ -206,18 +217,17 @@ func (c *Chain) Output(action Action, args ...string) error {
206 206
 }
207 207
 
208 208
 func (c *Chain) Remove() error {
209
+	// Ignore errors - This could mean the chains were never set up
209 210
 	if c.Table == Nat {
210
-		// Ignore errors - This could mean the chains were never set up
211 211
 		c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
212 212
 		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
213 213
 		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
214 214
 
215 215
 		c.Prerouting(Delete)
216 216
 		c.Output(Delete)
217
-
218
-		Raw("-t", string(Nat), "-F", c.Name)
219
-		Raw("-t", string(Nat), "-X", c.Name)
220 217
 	}
218
+	Raw("-t", string(c.Table), "-F", c.Name)
219
+	Raw("-t", string(c.Table), "-X", c.Name)
221 220
 	return nil
222 221
 }
223 222
 
224 223
new file mode 100644
... ...
@@ -0,0 +1,204 @@
0
+package iptables
1
+
2
+import (
3
+	"net"
4
+	"os/exec"
5
+	"strconv"
6
+	"strings"
7
+	"testing"
8
+)
9
+
10
+const chainName = "DOCKERTEST"
11
+
12
+var natChain *Chain
13
+var filterChain *Chain
14
+
15
+func TestNewChain(t *testing.T) {
16
+	var err error
17
+
18
+	natChain, err = NewChain(chainName, "lo", Nat)
19
+	if err != nil {
20
+		t.Fatal(err)
21
+	}
22
+
23
+	filterChain, err = NewChain(chainName, "lo", Filter)
24
+	if err != nil {
25
+		t.Fatal(err)
26
+	}
27
+}
28
+
29
+func TestForward(t *testing.T) {
30
+	ip := net.ParseIP("192.168.1.1")
31
+	port := 1234
32
+	dstAddr := "172.17.0.1"
33
+	dstPort := 4321
34
+	proto := "tcp"
35
+
36
+	err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort)
37
+	if err != nil {
38
+		t.Fatal(err)
39
+	}
40
+
41
+	dnatRule := []string{natChain.Name,
42
+		"-t", string(natChain.Table),
43
+		"!", "-i", filterChain.Bridge,
44
+		"-d", ip.String(),
45
+		"-p", proto,
46
+		"--dport", strconv.Itoa(port),
47
+		"-j", "DNAT",
48
+		"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
49
+	}
50
+
51
+	if !Exists(dnatRule...) {
52
+		t.Fatalf("DNAT rule does not exist")
53
+	}
54
+
55
+	filterRule := []string{filterChain.Name,
56
+		"-t", string(filterChain.Table),
57
+		"!", "-i", filterChain.Bridge,
58
+		"-o", filterChain.Bridge,
59
+		"-d", dstAddr,
60
+		"-p", proto,
61
+		"--dport", strconv.Itoa(dstPort),
62
+		"-j", "ACCEPT",
63
+	}
64
+
65
+	if !Exists(filterRule...) {
66
+		t.Fatalf("filter rule does not exist")
67
+	}
68
+
69
+	masqRule := []string{"POSTROUTING",
70
+		"-t", string(natChain.Table),
71
+		"-d", dstAddr,
72
+		"-s", dstAddr,
73
+		"-p", proto,
74
+		"--dport", strconv.Itoa(dstPort),
75
+		"-j", "MASQUERADE",
76
+	}
77
+
78
+	if !Exists(masqRule...) {
79
+		t.Fatalf("MASQUERADE rule does not exist")
80
+	}
81
+}
82
+
83
+func TestLink(t *testing.T) {
84
+	var err error
85
+
86
+	ip1 := net.ParseIP("192.168.1.1")
87
+	ip2 := net.ParseIP("192.168.1.2")
88
+	port := 1234
89
+	proto := "tcp"
90
+
91
+	err = filterChain.Link(Append, ip1, ip2, port, proto)
92
+	if err != nil {
93
+		t.Fatal(err)
94
+	}
95
+
96
+	rule1 := []string{filterChain.Name,
97
+		"-t", string(filterChain.Table),
98
+		"-i", filterChain.Bridge,
99
+		"-o", filterChain.Bridge,
100
+		"-p", proto,
101
+		"-s", ip1.String(),
102
+		"-d", ip2.String(),
103
+		"--dport", strconv.Itoa(port),
104
+		"-j", "ACCEPT"}
105
+
106
+	if !Exists(rule1...) {
107
+		t.Fatalf("rule1 does not exist")
108
+	}
109
+
110
+	rule2 := []string{filterChain.Name,
111
+		"-t", string(filterChain.Table),
112
+		"-i", filterChain.Bridge,
113
+		"-o", filterChain.Bridge,
114
+		"-p", proto,
115
+		"-s", ip2.String(),
116
+		"-d", ip1.String(),
117
+		"--sport", strconv.Itoa(port),
118
+		"-j", "ACCEPT"}
119
+
120
+	if !Exists(rule2...) {
121
+		t.Fatalf("rule2 does not exist")
122
+	}
123
+}
124
+
125
+func TestPrerouting(t *testing.T) {
126
+	args := []string{
127
+		"-i", "lo",
128
+		"-d", "192.168.1.1"}
129
+
130
+	err := natChain.Prerouting(Insert, args...)
131
+	if err != nil {
132
+		t.Fatal(err)
133
+	}
134
+
135
+	rule := []string{"PREROUTING",
136
+		"-t", string(Nat),
137
+		"-j", natChain.Name}
138
+
139
+	rule = append(rule, args...)
140
+
141
+	if !Exists(rule...) {
142
+		t.Fatalf("rule does not exist")
143
+	}
144
+
145
+	delRule := append([]string{"-D"}, rule...)
146
+	if _, err = Raw(delRule...); err != nil {
147
+		t.Fatal(err)
148
+	}
149
+}
150
+
151
+func TestOutput(t *testing.T) {
152
+	args := []string{
153
+		"-o", "lo",
154
+		"-d", "192.168.1.1"}
155
+
156
+	err := natChain.Output(Insert, args...)
157
+	if err != nil {
158
+		t.Fatal(err)
159
+	}
160
+
161
+	rule := []string{"OUTPUT",
162
+		"-t", string(natChain.Table),
163
+		"-j", natChain.Name}
164
+
165
+	rule = append(rule, args...)
166
+
167
+	if !Exists(rule...) {
168
+		t.Fatalf("rule does not exist")
169
+	}
170
+
171
+	delRule := append([]string{"-D"}, rule...)
172
+	if _, err = Raw(delRule...); err != nil {
173
+		t.Fatal(err)
174
+	}
175
+}
176
+
177
+func TestCleanup(t *testing.T) {
178
+	var err error
179
+	var rules []byte
180
+
181
+	// Cleanup filter/FORWARD first otherwise output of iptables-save is dirty
182
+	link := []string{"-t", string(filterChain.Table),
183
+		string(Delete), "FORWARD",
184
+		"-o", filterChain.Bridge,
185
+		"-j", filterChain.Name}
186
+	if _, err = Raw(link...); err != nil {
187
+		t.Fatal(err)
188
+	}
189
+	filterChain.Remove()
190
+
191
+	err = RemoveExistingChain(chainName, Nat)
192
+	if err != nil {
193
+		t.Fatal(err)
194
+	}
195
+
196
+	rules, err = exec.Command("iptables-save").Output()
197
+	if err != nil {
198
+		t.Fatal(err)
199
+	}
200
+	if strings.Contains(string(rules), chainName) {
201
+		t.Fatalf("Removing chain failed. %s found in iptables-save", chainName)
202
+	}
203
+}