Browse code

Move per-container forward rules to DOCKER chain

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

Porjo authored on 2014/06/27 16:29:55
Showing 4 changed files
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"net"
7 7
 	"os"
8 8
 	"strconv"
9
+	"strings"
9 10
 	"sync"
10 11
 
11 12
 	log "github.com/Sirupsen/logrus"
... ...
@@ -501,35 +502,48 @@ func AllocatePort(job *engine.Job) engine.Status {
501 501
 func LinkContainers(job *engine.Job) engine.Status {
502 502
 	var (
503 503
 		action       = job.Args[0]
504
+		nfAction     iptables.Action
504 505
 		childIP      = job.Getenv("ChildIP")
505 506
 		parentIP     = job.Getenv("ParentIP")
506 507
 		ignoreErrors = job.GetenvBool("IgnoreErrors")
507 508
 		ports        = job.GetenvList("Ports")
509
+		chain        = iptables.Chain{}
508 510
 	)
509
-	for _, value := range ports {
510
-		port := nat.Port(value)
511
-		if output, err := iptables.Raw(action, "FORWARD",
512
-			"-i", bridgeIface, "-o", bridgeIface,
513
-			"-p", port.Proto(),
514
-			"-s", parentIP,
515
-			"--dport", strconv.Itoa(port.Int()),
516
-			"-d", childIP,
517
-			"-j", "ACCEPT"); !ignoreErrors && err != nil {
518
-			return job.Error(err)
519
-		} else if len(output) != 0 {
520
-			return job.Errorf("Error toggle iptables forward: %s", output)
521
-		}
511
+	split := func(p string) (string, string) {
512
+		parts := strings.Split(p, "/")
513
+		return parts[0], parts[1]
514
+	}
515
+
516
+	switch action {
517
+	case "-A":
518
+		nfAction = iptables.Append
519
+	case "-I":
520
+		nfAction = iptables.Insert
521
+	case "-D":
522
+		nfAction = iptables.Delete
523
+	default:
524
+		return job.Errorf("Invalid action '%s' specified", action)
525
+	}
522 526
 
523
-		if output, err := iptables.Raw(action, "FORWARD",
524
-			"-i", bridgeIface, "-o", bridgeIface,
525
-			"-p", port.Proto(),
526
-			"-s", childIP,
527
-			"--sport", strconv.Itoa(port.Int()),
528
-			"-d", parentIP,
529
-			"-j", "ACCEPT"); !ignoreErrors && err != nil {
527
+	ip1 := net.ParseIP(parentIP)
528
+	if ip1 == nil {
529
+		return job.Errorf("parent IP '%s' is invalid", parentIP)
530
+	}
531
+	ip2 := net.ParseIP(childIP)
532
+	if ip2 == nil {
533
+		return job.Errorf("child IP '%s' is invalid", childIP)
534
+	}
535
+
536
+	chain.Name = "DOCKER"
537
+	chain.Bridge = bridgeIface
538
+	for _, p := range ports {
539
+		portStr, proto := split(p)
540
+		port, err := strconv.Atoi(portStr)
541
+		if !ignoreErrors && err != nil {
542
+			return job.Errorf("port '%s' is invalid", portStr)
543
+		}
544
+		if err := chain.Link(nfAction, ip1, ip2, port, proto); !ignoreErrors && err != nil {
530 545
 			return job.Error(err)
531
-		} else if len(output) != 0 {
532
-			return job.Errorf("Error toggle iptables forward: %s", output)
533 546
 		}
534 547
 	}
535 548
 	return engine.StatusOK
... ...
@@ -93,7 +93,7 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er
93 93
 	}
94 94
 
95 95
 	containerIP, containerPort := getIPAndPort(m.container)
96
-	if err := forward(iptables.Add, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
96
+	if err := forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
97 97
 		return nil, err
98 98
 	}
99 99
 
... ...
@@ -138,7 +138,8 @@ func (l *Link) getDefaultPort() *nat.Port {
138 138
 }
139 139
 
140 140
 func (l *Link) Enable() error {
141
-	if err := l.toggle("-I", false); err != nil {
141
+	// -A == iptables append flag
142
+	if err := l.toggle("-A", false); err != nil {
142 143
 		return err
143 144
 	}
144 145
 	l.IsEnabled = true
... ...
@@ -148,6 +149,7 @@ func (l *Link) Enable() error {
148 148
 func (l *Link) Disable() {
149 149
 	// We do not care about errors here because the link may not
150 150
 	// exist in iptables
151
+	// -D == iptables delete flag
151 152
 	l.toggle("-D", true)
152 153
 
153 154
 	l.IsEnabled = false
... ...
@@ -15,8 +15,9 @@ import (
15 15
 type Action string
16 16
 
17 17
 const (
18
-	Add    Action = "-A"
18
+	Append Action = "-A"
19 19
 	Delete Action = "-D"
20
+	Insert Action = "-I"
20 21
 )
21 22
 
22 23
 var (
... ...
@@ -54,10 +55,10 @@ func NewChain(name, bridge string) (*Chain, error) {
54 54
 		Bridge: bridge,
55 55
 	}
56 56
 
57
-	if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
57
+	if err := chain.Prerouting(Append, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
58 58
 		return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
59 59
 	}
60
-	if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
60
+	if err := chain.Output(Append, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
61 61
 		return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
62 62
 	}
63 63
 	return chain, nil
... ...
@@ -78,7 +79,7 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str
78 78
 		// value" by both iptables and ip6tables.
79 79
 		daddr = "0/0"
80 80
 	}
81
-	if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name,
81
+	if output, err := Raw("-t", "nat", string(action), c.Name,
82 82
 		"-p", proto,
83 83
 		"-d", daddr,
84 84
 		"--dport", strconv.Itoa(port),
... ...
@@ -90,11 +91,13 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str
90 90
 		return &ChainError{Chain: "FORWARD", Output: output}
91 91
 	}
92 92
 
93
-	fAction := action
94
-	if fAction == Add {
95
-		fAction = "-I"
93
+	if action != Delete {
94
+		if err := c.createForwardChain(); err != nil {
95
+			return err
96
+		}
96 97
 	}
97
-	if output, err := Raw(string(fAction), "FORWARD",
98
+
99
+	if output, err := Raw(string(action), c.Name,
98 100
 		"!", "-i", c.Bridge,
99 101
 		"-o", c.Bridge,
100 102
 		"-p", proto,
... ...
@@ -109,6 +112,39 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str
109 109
 	return nil
110 110
 }
111 111
 
112
+func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error {
113
+	if action != Delete {
114
+		if err := c.createForwardChain(); err != nil {
115
+			return err
116
+		}
117
+	}
118
+	if output, err := Raw(string(action), c.Name,
119
+		"-i", c.Bridge, "-o", c.Bridge,
120
+		"-p", proto,
121
+		"-s", ip1.String(),
122
+		"--dport", strconv.Itoa(port),
123
+		"-d", ip2.String(),
124
+		"-j", "ACCEPT"); err != nil {
125
+		return err
126
+	} else if len(output) != 0 {
127
+		return fmt.Errorf("Error toggle iptables forward: %s", output)
128
+	}
129
+
130
+	if output, err := Raw(string(action), c.Name,
131
+		"-i", c.Bridge, "-o", c.Bridge,
132
+		"-p", proto,
133
+		"-s", ip2.String(),
134
+		"--dport", strconv.Itoa(port),
135
+		"-d", ip1.String(),
136
+		"-j", "ACCEPT"); err != nil {
137
+		return err
138
+	} else if len(output) != 0 {
139
+		return fmt.Errorf("Error toggle iptables forward: %s", output)
140
+	}
141
+
142
+	return nil
143
+}
144
+
112 145
 func (c *Chain) Prerouting(action Action, args ...string) error {
113 146
 	a := append(nat, fmt.Sprint(action), "PREROUTING")
114 147
 	if len(args) > 0 {
... ...
@@ -199,3 +235,28 @@ func Raw(args ...string) ([]byte, error) {
199 199
 
200 200
 	return output, err
201 201
 }
202
+
203
+func (c *Chain) createForwardChain() error {
204
+	// Add chain if doesn't exist
205
+	if _, err := Raw("-n", "-L", c.Name); err != nil {
206
+		output, err := Raw("-N", c.Name)
207
+		if err != nil {
208
+			return err
209
+		} else if len(output) != 0 {
210
+			return fmt.Errorf("Error iptables forward: %s", output)
211
+		}
212
+	}
213
+	// Add linking rule if it doesn't exist
214
+	if !Exists("FORWARD",
215
+		"-o", c.Bridge,
216
+		"-j", c.Name) {
217
+		if output2, err := Raw(string(Insert), "FORWARD",
218
+			"-o", c.Bridge,
219
+			"-j", c.Name); err != nil {
220
+			return err
221
+		} else if len(output2) != 0 {
222
+			return fmt.Errorf("Error iptables forward: %s", output2)
223
+		}
224
+	}
225
+	return nil
226
+}