github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/network/iptables/iptables.go (about) 1 package iptables 2 3 import ( 4 "bytes" 5 "fmt" 6 "net" 7 "os/exec" 8 "strings" 9 10 "sync" 11 12 "github.com/cloudfoundry-incubator/garden" 13 "github.com/cloudfoundry/gunk/command_runner" 14 "github.com/pivotal-golang/lager" 15 ) 16 17 var protocols = map[garden.Protocol]string{ 18 garden.ProtocolAll: "all", 19 garden.ProtocolTCP: "tcp", 20 garden.ProtocolICMP: "icmp", 21 garden.ProtocolUDP: "udp", 22 } 23 24 // NewGlobalChain creates a chain without an associated log chain. 25 // The chain is not created by this package (currently it is created in net.sh). 26 // It is an error to attempt to call Setup on this chain. 27 func NewGlobalChain(name string, runner command_runner.CommandRunner, log lager.Logger) Chain { 28 return &chain{name: name, logChainName: "", runner: runner, logger: log} 29 } 30 31 // NewLoggingChain creates a chain with an associated log chain. 32 // This allows NetOut calls with the 'log' parameter to succesfully log. 33 func NewLoggingChain(name string, useKernelLogging bool, runner command_runner.CommandRunner, logger lager.Logger) Chain { 34 return &chain{name: name, logChainName: name + "-log", useKernelLogging: useKernelLogging, runner: runner, logger: logger} 35 } 36 37 //go:generate counterfeiter . Chain 38 type Chain interface { 39 // Create the actual iptable chains in the underlying system. 40 // logPrefix defines the log prefix used for logging this chain. 41 Setup(logPrefix string) error 42 43 // Destroy the actual iptable chains in the underlying system 44 TearDown() error 45 46 AppendRule(source string, destination string, jump Action) error 47 DeleteRule(source string, destination string, jump Action) error 48 49 AppendNatRule(source string, destination string, jump Action, to net.IP) error 50 DeleteNatRule(source string, destination string, jump Action, to net.IP) error 51 52 PrependFilterRule(rule garden.NetOutRule) error 53 } 54 55 type chain struct { 56 mu sync.Mutex 57 name string 58 logChainName string 59 useKernelLogging bool 60 runner command_runner.CommandRunner 61 logger lager.Logger 62 } 63 64 func (ch *chain) Setup(logPrefix string) error { 65 ch.mu.Lock() 66 defer ch.mu.Unlock() 67 68 if ch.logChainName == "" { 69 // we still use net.sh to set up global non-logging chains 70 panic("cannot set up chains without associated log chains") 71 } 72 73 ch.TearDown() 74 75 if err := ch.runner.Run(exec.Command("/sbin/iptables", "-w", "-N", ch.logChainName)); err != nil { 76 return fmt.Errorf("iptables: log chain setup: %v", err) 77 } 78 ch.logger.Debug("log-chain-created") 79 80 logParams := ch.buildLogParams(logPrefix) 81 appendFlags := []string{"-w", "-A", ch.logChainName, "-m", "conntrack", "--ctstate", "NEW,UNTRACKED,INVALID", "--protocol", "tcp"} 82 if err := ch.runner.Run(exec.Command("/sbin/iptables", append(appendFlags, logParams...)...)); err != nil { 83 return fmt.Errorf("iptables: log chain setup: %v", err) 84 } 85 ch.logger.Debug("log-chain-conntrack-set-up") 86 87 if err := ch.runner.Run(exec.Command("/sbin/iptables", "-w", "-A", ch.logChainName, "--jump", "RETURN")); err != nil { 88 return fmt.Errorf("iptables: log chain setup: %v", err) 89 } 90 ch.logger.Debug("log-chain-setup-finished") 91 92 return nil 93 } 94 95 func (ch *chain) buildLogParams(logPrefix string) []string { 96 if ch.useKernelLogging { 97 return []string{"--jump", "LOG", "--log-prefix", logPrefix} 98 } else { 99 return []string{"--jump", "NFLOG", "--nflog-prefix", logPrefix, "--nflog-group", "1"} 100 } 101 } 102 103 func (ch *chain) TearDown() error { 104 if ch.logChainName == "" { 105 // we still use net.sh to tear down global non-logging chains 106 panic("cannot tear down chains without associated log chains") 107 } 108 109 ch.runner.Run(exec.Command("/sbin/iptables", "-w", "-F", ch.logChainName)) 110 ch.runner.Run(exec.Command("/sbin/iptables", "-w", "-X", ch.logChainName)) 111 return nil 112 } 113 114 func (ch *chain) AppendRule(source string, destination string, jump Action) error { 115 return ch.Create(&rule{ 116 source: source, 117 destination: destination, 118 jump: jump, 119 }) 120 } 121 122 func (ch *chain) DeleteRule(source string, destination string, jump Action) error { 123 return ch.Destroy(&rule{ 124 source: source, 125 destination: destination, 126 jump: jump, 127 }) 128 } 129 130 func (ch *chain) AppendNatRule(source string, destination string, jump Action, to net.IP) error { 131 return ch.Create(&rule{ 132 typ: Nat, 133 source: source, 134 destination: destination, 135 jump: jump, 136 to: to, 137 }) 138 } 139 140 func (ch *chain) DeleteNatRule(source string, destination string, jump Action, to net.IP) error { 141 return ch.Destroy(&rule{ 142 typ: Nat, 143 source: source, 144 destination: destination, 145 jump: jump, 146 to: to, 147 }) 148 } 149 150 type singleRule struct { 151 Protocol garden.Protocol 152 Networks *garden.IPRange 153 Ports *garden.PortRange 154 ICMPs *garden.ICMPControl 155 Log bool 156 } 157 158 func (ch *chain) PrependFilterRule(r garden.NetOutRule) error { 159 160 if len(r.Ports) > 0 && !allowsPort(r.Protocol) { 161 return fmt.Errorf("Ports cannot be specified for Protocol %s", strings.ToUpper(protocols[r.Protocol])) 162 } 163 164 single := singleRule{ 165 Protocol: r.Protocol, 166 ICMPs: r.ICMPs, 167 Log: r.Log, 168 } 169 170 // It should still loop once even if there are no networks or ports. 171 for j := 0; j < len(r.Networks) || j == 0; j++ { 172 for i := 0; i < len(r.Ports) || i == 0; i++ { 173 174 // Preserve nils unless there are ports specified 175 if len(r.Ports) > 0 { 176 single.Ports = &r.Ports[i] 177 } 178 179 // Preserve nils unless there are networks specified 180 if len(r.Networks) > 0 { 181 single.Networks = &r.Networks[j] 182 } 183 184 if err := ch.prependSingleRule(single); err != nil { 185 return err 186 } 187 } 188 } 189 190 return nil 191 } 192 193 func allowsPort(p garden.Protocol) bool { 194 return p == garden.ProtocolTCP || p == garden.ProtocolUDP 195 } 196 197 func (ch *chain) prependSingleRule(r singleRule) error { 198 params := []string{"-w", "-I", ch.name, "1"} 199 200 protocolString, ok := protocols[r.Protocol] 201 202 if !ok { 203 return fmt.Errorf("invalid protocol: %d", r.Protocol) 204 } 205 206 params = append(params, "--protocol", protocolString) 207 208 network := r.Networks 209 if network != nil { 210 if network.Start != nil && network.End != nil { 211 params = append(params, "-m", "iprange", "--dst-range", network.Start.String()+"-"+network.End.String()) 212 } else if network.Start != nil { 213 params = append(params, "--destination", network.Start.String()) 214 } else if network.End != nil { 215 params = append(params, "--destination", network.End.String()) 216 } 217 } 218 219 ports := r.Ports 220 if ports != nil { 221 if ports.End != ports.Start { 222 params = append(params, "--destination-port", fmt.Sprintf("%d:%d", ports.Start, ports.End)) 223 } else { 224 params = append(params, "--destination-port", fmt.Sprintf("%d", ports.Start)) 225 } 226 } 227 228 if r.ICMPs != nil { 229 icmpType := fmt.Sprintf("%d", r.ICMPs.Type) 230 if r.ICMPs.Code != nil { 231 icmpType = fmt.Sprintf("%d/%d", r.ICMPs.Type, *r.ICMPs.Code) 232 } 233 234 params = append(params, "--icmp-type", icmpType) 235 } 236 237 if r.Log { 238 params = append(params, "--goto", ch.logChainName) 239 } else { 240 params = append(params, "--jump", "RETURN") 241 } 242 243 ch.logger.Debug("prepend-filter-rule", lager.Data{"parms": params}) 244 245 var stderr bytes.Buffer 246 cmd := exec.Command("/sbin/iptables", params...) 247 cmd.Stderr = &stderr 248 if err := ch.runner.Run(cmd); err != nil { 249 return fmt.Errorf("iptables: %v, %v", err, stderr.String()) 250 } 251 ch.logger.Debug("prependSingleRule-finished") 252 253 return nil 254 } 255 256 type rule struct { 257 typ Type 258 source string 259 destination string 260 to net.IP 261 jump Action 262 } 263 264 func (n *rule) create(chain string, runner command_runner.CommandRunner) error { 265 return runner.Run(exec.Command("/sbin/iptables", flags("-A", chain, n)...)) 266 } 267 268 func (n *rule) destroy(chain string, runner command_runner.CommandRunner) error { 269 return runner.Run(exec.Command("/sbin/iptables", flags("-D", chain, n)...)) 270 } 271 272 func flags(action, chain string, n *rule) []string { 273 rule := []string{"-w"} 274 275 if n.typ != "" { 276 rule = append(rule, "-t", string(n.typ)) 277 } 278 279 rule = append(rule, action, chain) 280 281 if n.source != "" { 282 rule = append(rule, "--source", n.source) 283 } 284 285 if n.destination != "" { 286 rule = append(rule, "--destination", n.destination) 287 } 288 289 rule = append(rule, "--jump", string(n.jump)) 290 291 if n.to != nil { 292 rule = append(rule, "--to", string(n.to.String())) 293 } 294 295 return rule 296 } 297 298 type Destroyable interface { 299 Destroy() error 300 } 301 302 type creater interface { 303 create(chain string, runner command_runner.CommandRunner) error 304 } 305 306 type destroyer interface { 307 destroy(chain string, runner command_runner.CommandRunner) error 308 } 309 310 func (c *chain) Create(rule creater) error { 311 return rule.create(c.name, c.runner) 312 } 313 314 func (c *chain) Destroy(rule destroyer) error { 315 return rule.destroy(c.name, c.runner) 316 } 317 318 type Action string 319 320 const ( 321 Return Action = "RETURN" 322 SourceNAT = "SNAT" 323 Reject = "REJECT" 324 Drop = "DROP" 325 ) 326 327 type Type string 328 329 const ( 330 Nat Type = "nat" 331 )