1package overlay
2
3import (
4	"fmt"
5	"sync"
6
7	"github.com/docker/libnetwork/iptables"
8	"github.com/sirupsen/logrus"
9)
10
11const globalChain = "DOCKER-OVERLAY"
12
13var filterOnce sync.Once
14
15var filterChan = make(chan struct{}, 1)
16
17func filterWait() func() {
18	filterChan <- struct{}{}
19	return func() { <-filterChan }
20}
21
22func chainExists(cname string) bool {
23	if _, err := iptables.Raw("-L", cname); err != nil {
24		return false
25	}
26
27	return true
28}
29
30func setupGlobalChain() {
31	// Because of an ungraceful shutdown, chain could already be present
32	if !chainExists(globalChain) {
33		if err := iptables.RawCombinedOutput("-N", globalChain); err != nil {
34			logrus.Errorf("could not create global overlay chain: %v", err)
35			return
36		}
37	}
38
39	if !iptables.Exists(iptables.Filter, globalChain, "-j", "RETURN") {
40		if err := iptables.RawCombinedOutput("-A", globalChain, "-j", "RETURN"); err != nil {
41			logrus.Errorf("could not install default return chain in the overlay global chain: %v", err)
42		}
43	}
44}
45
46func setNetworkChain(cname string, remove bool) error {
47	// Initialize the onetime global overlay chain
48	filterOnce.Do(setupGlobalChain)
49
50	exists := chainExists(cname)
51
52	opt := "-N"
53	// In case of remove, make sure to flush the rules in the chain
54	if remove && exists {
55		if err := iptables.RawCombinedOutput("-F", cname); err != nil {
56			return fmt.Errorf("failed to flush overlay network chain %s rules: %v", cname, err)
57		}
58		opt = "-X"
59	}
60
61	if (!remove && !exists) || (remove && exists) {
62		if err := iptables.RawCombinedOutput(opt, cname); err != nil {
63			return fmt.Errorf("failed network chain operation %q for chain %s: %v", opt, cname, err)
64		}
65	}
66
67	if !remove {
68		if !iptables.Exists(iptables.Filter, cname, "-j", "DROP") {
69			if err := iptables.RawCombinedOutput("-A", cname, "-j", "DROP"); err != nil {
70				return fmt.Errorf("failed adding default drop rule to overlay network chain %s: %v", cname, err)
71			}
72		}
73	}
74
75	return nil
76}
77
78func addNetworkChain(cname string) error {
79	defer filterWait()()
80
81	return setNetworkChain(cname, false)
82}
83
84func removeNetworkChain(cname string) error {
85	defer filterWait()()
86
87	return setNetworkChain(cname, true)
88}
89
90func setFilters(cname, brName string, remove bool) error {
91	opt := "-I"
92	if remove {
93		opt = "-D"
94	}
95
96	// Every time we set filters for a new subnet make sure to move the global overlay hook to the top of the both the OUTPUT and forward chains
97	if !remove {
98		for _, chain := range []string{"OUTPUT", "FORWARD"} {
99			exists := iptables.Exists(iptables.Filter, chain, "-j", globalChain)
100			if exists {
101				if err := iptables.RawCombinedOutput("-D", chain, "-j", globalChain); err != nil {
102					return fmt.Errorf("failed to delete overlay hook in chain %s while moving the hook: %v", chain, err)
103				}
104			}
105
106			if err := iptables.RawCombinedOutput("-I", chain, "-j", globalChain); err != nil {
107				return fmt.Errorf("failed to insert overlay hook in chain %s: %v", chain, err)
108			}
109		}
110	}
111
112	// Insert/Delete the rule to jump to per-bridge chain
113	exists := iptables.Exists(iptables.Filter, globalChain, "-o", brName, "-j", cname)
114	if (!remove && !exists) || (remove && exists) {
115		if err := iptables.RawCombinedOutput(opt, globalChain, "-o", brName, "-j", cname); err != nil {
116			return fmt.Errorf("failed to add per-bridge filter rule for bridge %s, network chain %s: %v", brName, cname, err)
117		}
118	}
119
120	exists = iptables.Exists(iptables.Filter, cname, "-i", brName, "-j", "ACCEPT")
121	if (!remove && exists) || (remove && !exists) {
122		return nil
123	}
124
125	if err := iptables.RawCombinedOutput(opt, cname, "-i", brName, "-j", "ACCEPT"); err != nil {
126		return fmt.Errorf("failed to add overlay filter rile for network chain %s, bridge %s: %v", cname, brName, err)
127	}
128
129	return nil
130}
131
132func addFilters(cname, brName string) error {
133	defer filterWait()()
134
135	return setFilters(cname, brName, false)
136}
137
138func removeFilters(cname, brName string) error {
139	defer filterWait()()
140
141	return setFilters(cname, brName, true)
142}
143