summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2018-09-09 13:16:34 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2018-09-10 18:53:27 +0000
commit9405e3704fae9c30b24ad8807174639005b1db6c (patch)
tree96d6c02662364c965aeecca8ced8b1deccc17f2d
parent2afadeec6696fefac468a49c8ba24b0bc275aa75 (diff)
downloadpodman-9405e3704fae9c30b24ad8807174639005b1db6c.tar.gz
podman-9405e3704fae9c30b24ad8807174639005b1db6c.tar.bz2
podman-9405e3704fae9c30b24ad8807174639005b1db6c.zip
Vendor CNI plugins firewall code
The upstream CNI project has a PR open for adding iptables and firewalld support, but this has been stalled for the better part of a year upstream. On advice of several maintainers, we are vendoring this code into libpod, to perform the relevant firewall configuration ourselves. Signed-off-by: Matthew Heon <matthew.heon@gmail.com> Closes: #1431 Approved by: baude
-rw-r--r--libpod/runtime.go39
-rw-r--r--pkg/firewall/common.go51
-rw-r--r--pkg/firewall/firewall_linux.go47
-rw-r--r--pkg/firewall/firewall_none.go41
-rw-r--r--pkg/firewall/firewall_unsupported.go27
-rw-r--r--pkg/firewall/firewalld.go119
-rw-r--r--pkg/firewall/iptables.go212
-rw-r--r--vendor/github.com/coreos/go-iptables/LICENSE191
-rw-r--r--vendor/github.com/coreos/go-iptables/NOTICE5
-rw-r--r--vendor/github.com/coreos/go-iptables/README.md10
-rw-r--r--vendor/github.com/coreos/go-iptables/iptables/iptables.go475
-rw-r--r--vendor/github.com/coreos/go-iptables/iptables/lock.go84
12 files changed, 1288 insertions, 13 deletions
diff --git a/libpod/runtime.go b/libpod/runtime.go
index c405eb773..8dc561cd8 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -13,6 +13,7 @@ import (
is "github.com/containers/image/storage"
"github.com/containers/image/types"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/firewall"
"github.com/containers/libpod/pkg/hooks"
sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless"
@@ -70,19 +71,20 @@ type RuntimeOption func(*Runtime) error
// Runtime is the core libpod runtime
type Runtime struct {
- config *RuntimeConfig
- state State
- store storage.Store
- storageService *storageService
- imageContext *types.SystemContext
- ociRuntime *OCIRuntime
- lockDir string
- netPlugin ocicni.CNIPlugin
- ociRuntimePath string
- conmonPath string
- valid bool
- lock sync.RWMutex
- imageRuntime *image.Runtime
+ config *RuntimeConfig
+ state State
+ store storage.Store
+ storageService *storageService
+ imageContext *types.SystemContext
+ ociRuntime *OCIRuntime
+ lockDir string
+ netPlugin ocicni.CNIPlugin
+ ociRuntimePath string
+ conmonPath string
+ valid bool
+ lock sync.RWMutex
+ imageRuntime *image.Runtime
+ firewallBackend firewall.FirewallBackend
}
// RuntimeConfig contains configuration options used to set up the runtime
@@ -507,6 +509,17 @@ func makeRuntime(runtime *Runtime) (err error) {
}
runtime.netPlugin = netPlugin
+ // Set up a firewall backend
+ backendType := ""
+ if os.Geteuid() != 0 {
+ backendType = "none"
+ }
+ fwBackend, err := firewall.GetBackend(backendType)
+ if err != nil {
+ return err
+ }
+ runtime.firewallBackend = fwBackend
+
// Set up the state
switch runtime.config.StateType {
case InMemoryStateStore:
diff --git a/pkg/firewall/common.go b/pkg/firewall/common.go
new file mode 100644
index 000000000..993c691cd
--- /dev/null
+++ b/pkg/firewall/common.go
@@ -0,0 +1,51 @@
+package firewall
+
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import (
+ "net"
+
+ "github.com/containernetworking/cni/pkg/types/current"
+)
+
+// FirewallNetConf represents the firewall configuration.
+type FirewallNetConf struct {
+ //types.NetConf
+
+ // IptablesAdminChainName is an optional name to use instead of the default
+ // admin rules override chain name that includes the interface name.
+ IptablesAdminChainName string
+
+ // FirewalldZone is an optional firewalld zone to place the interface into. If
+ // the firewalld backend is used but the zone is not given, it defaults
+ // to 'trusted'
+ FirewalldZone string
+
+ PrevResult *current.Result
+}
+
+// FirewallBackend is an interface to the system firewall, allowing addition and
+// removal of firewall rules.
+type FirewallBackend interface {
+ Add(*FirewallNetConf) error
+ Del(*FirewallNetConf) error
+}
+
+func ipString(ip net.IPNet) string {
+ if ip.IP.To4() == nil {
+ return ip.IP.String() + "/128"
+ }
+ return ip.IP.String() + "/32"
+}
diff --git a/pkg/firewall/firewall_linux.go b/pkg/firewall/firewall_linux.go
new file mode 100644
index 000000000..4ac45427b
--- /dev/null
+++ b/pkg/firewall/firewall_linux.go
@@ -0,0 +1,47 @@
+// +build linux
+
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package firewall
+
+import (
+ "fmt"
+)
+
+// GetBackend retrieves a firewall backend for adding or removing firewall rules
+// on the system.
+// Valid backend names are firewalld, iptables, and none.
+// If the empty string is given, a firewalld backend will be returned if
+// firewalld is running, and an iptables backend will be returned otherwise.
+func GetBackend(backend string) (FirewallBackend, error) {
+ switch backend {
+ case "firewalld":
+ return newFirewalldBackend()
+ case "iptables":
+ return newIptablesBackend()
+ case "none":
+ return newNoneBackend()
+ case "":
+ // Default to firewalld if it's running
+ if isFirewalldRunning() {
+ return newFirewalldBackend()
+ }
+
+ // Otherwise iptables
+ return newIptablesBackend()
+ default:
+ return nil, fmt.Errorf("unrecognized firewall backend %q", backend)
+ }
+}
diff --git a/pkg/firewall/firewall_none.go b/pkg/firewall/firewall_none.go
new file mode 100644
index 000000000..9f9594b4a
--- /dev/null
+++ b/pkg/firewall/firewall_none.go
@@ -0,0 +1,41 @@
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package firewall
+
+import (
+ "fmt"
+)
+
+// FirewallNone is a firewall backend for environments where manipulating the
+// system firewall is unsupported (for example, when running without root)
+type FirewallNone struct {}
+
+func newNoneBackend() (FirewallBackend, error) {
+ return &FirewallNone{}, nil
+}
+
+// Add adds a rule to the system firewall.
+// No action is taken and an error is unconditionally returned as this backend
+// does not support manipulating the firewall.
+func (f *FirewallNone) Add(conf *FirewallNetConf) error {
+ return fmt.Errorf("cannot modify system firewall rules")
+}
+
+// Del deletes a rule from the system firewall.
+// No action is taken and an error is unconditionally returned as this backend
+// does not support manipulating the firewall.
+func (f *FirewallNone) Del(conf *FirewallNetConf) error {
+ return fmt.Errorf("cannot modify system firewall rules")
+}
diff --git a/pkg/firewall/firewall_unsupported.go b/pkg/firewall/firewall_unsupported.go
new file mode 100644
index 000000000..24c07a8a9
--- /dev/null
+++ b/pkg/firewall/firewall_unsupported.go
@@ -0,0 +1,27 @@
+// +build !linux
+
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package firewall
+
+import (
+ "fmt"
+)
+
+// GetBackend retrieves a firewall backend for adding or removing firewall rules
+// on the system.
+func GetBackend(backend string) (FirewallBackend, error) {
+ return nil, fmt.Errorf("firewall backends are not presently supported on this OS")
+}
diff --git a/pkg/firewall/firewalld.go b/pkg/firewall/firewalld.go
new file mode 100644
index 000000000..32c2337a0
--- /dev/null
+++ b/pkg/firewall/firewalld.go
@@ -0,0 +1,119 @@
+// +build linux
+
+// Copyright 2018 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package firewall
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/godbus/dbus"
+)
+
+const (
+ dbusName = "org.freedesktop.DBus"
+ dbusPath = "/org/freedesktop/DBus"
+ dbusGetNameOwnerMethod = "GetNameOwner"
+
+ firewalldName = "org.fedoraproject.FirewallD1"
+ firewalldPath = "/org/fedoraproject/FirewallD1"
+ firewalldZoneInterface = "org.fedoraproject.FirewallD1.zone"
+ firewalldAddSourceMethod = "addSource"
+ firewalldRemoveSourceMethod = "removeSource"
+
+ errZoneAlreadySet = "ZONE_ALREADY_SET"
+)
+
+// Only used for testcases to override the D-Bus connection
+var testConn *dbus.Conn
+
+type fwdBackend struct {
+ conn *dbus.Conn
+}
+
+// fwdBackend implements the FirewallBackend interface
+var _ FirewallBackend = &fwdBackend{}
+
+func getConn() (*dbus.Conn, error) {
+ if testConn != nil {
+ return testConn, nil
+ }
+ return dbus.SystemBus()
+}
+
+// isFirewalldRunning checks whether firewalld is running.
+func isFirewalldRunning() bool {
+ conn, err := getConn()
+ if err != nil {
+ return false
+ }
+
+ dbusObj := conn.Object(dbusName, dbusPath)
+ var res string
+ if err := dbusObj.Call(dbusName+"."+dbusGetNameOwnerMethod, 0, firewalldName).Store(&res); err != nil {
+ return false
+ }
+
+ return true
+}
+
+func newFirewalldBackend() (FirewallBackend, error) {
+ conn, err := getConn()
+ if err != nil {
+ return nil, err
+ }
+
+ backend := &fwdBackend{
+ conn: conn,
+ }
+ return backend, nil
+}
+
+func getFirewalldZone(conf *FirewallNetConf) string {
+ if conf.FirewalldZone != "" {
+ return conf.FirewalldZone
+ }
+
+ return "trusted"
+}
+
+func (fb *fwdBackend) Add(conf *FirewallNetConf) error {
+ zone := getFirewalldZone(conf)
+
+ for _, ip := range conf.PrevResult.IPs {
+ ipStr := ipString(ip.Address)
+ // Add a firewalld rule which assigns the given source IP to the given zone
+ firewalldObj := fb.conn.Object(firewalldName, firewalldPath)
+ var res string
+ if err := firewalldObj.Call(firewalldZoneInterface+"."+firewalldAddSourceMethod, 0, zone, ipStr).Store(&res); err != nil {
+ if !strings.Contains(err.Error(), errZoneAlreadySet) {
+ return fmt.Errorf("failed to add the address %v to %v zone: %v", ipStr, zone, err)
+ }
+ }
+ }
+ return nil
+}
+
+func (fb *fwdBackend) Del(conf *FirewallNetConf) error {
+ for _, ip := range conf.PrevResult.IPs {
+ ipStr := ipString(ip.Address)
+ // Remove firewalld rules which assigned the given source IP to the given zone
+ firewalldObj := fb.conn.Object(firewalldName, firewalldPath)
+ var res string
+ firewalldObj.Call(firewalldZoneInterface+"."+firewalldRemoveSourceMethod, 0, getFirewalldZone(conf), ipStr).Store(&res)
+ }
+ return nil
+}
diff --git a/pkg/firewall/iptables.go b/pkg/firewall/iptables.go
new file mode 100644
index 000000000..9f065dbcf
--- /dev/null
+++ b/pkg/firewall/iptables.go
@@ -0,0 +1,212 @@
+// +build linux
+
+// Copyright 2016 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This is a "meta-plugin". It reads in its own netconf, it does not create
+// any network interface but just changes the network sysctl.
+
+package firewall
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/coreos/go-iptables/iptables"
+)
+
+func getPrivChainRules(ip string) [][]string {
+ var rules [][]string
+ rules = append(rules, []string{"-d", ip, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"})
+ rules = append(rules, []string{"-s", ip, "-j", "ACCEPT"})
+ return rules
+}
+
+func ensureChain(ipt *iptables.IPTables, table, chain string) error {
+ chains, err := ipt.ListChains(table)
+ if err != nil {
+ return fmt.Errorf("failed to list iptables chains: %v", err)
+ }
+ for _, ch := range chains {
+ if ch == chain {
+ return nil
+ }
+ }
+
+ return ipt.NewChain(table, chain)
+}
+
+func generateFilterRule(privChainName string) []string {
+ return []string{"-m", "comment", "--comment", "CNI firewall plugin rules", "-j", privChainName}
+}
+
+func generateAdminRule(adminChainName string) []string {
+ return []string{"-m", "comment", "--comment", "CNI firewall plugin admin overrides", "-j", adminChainName}
+}
+
+func cleanupRules(ipt *iptables.IPTables, privChainName string, rules [][]string) {
+ for _, rule := range rules {
+ ipt.Delete("filter", privChainName, rule...)
+ }
+}
+
+func ensureFirstChainRule(ipt *iptables.IPTables, chain string, rule []string) error {
+ exists, err := ipt.Exists("filter", chain, rule...)
+ if !exists && err == nil {
+ err = ipt.Insert("filter", chain, 1, rule...)
+ }
+ return err
+}
+
+func (ib *iptablesBackend) setupChains(ipt *iptables.IPTables) error {
+ privRule := generateFilterRule(ib.privChainName)
+ adminRule := generateFilterRule(ib.adminChainName)
+
+ // Ensure our private chains exist
+ if err := ensureChain(ipt, "filter", ib.privChainName); err != nil {
+ return err
+ }
+ if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil {
+ return err
+ }
+
+ // Ensure our filter rule exists in the forward chain
+ if err := ensureFirstChainRule(ipt, "FORWARD", privRule); err != nil {
+ return err
+ }
+
+ // Ensure our admin override chain rule exists in our private chain
+ if err := ensureFirstChainRule(ipt, ib.privChainName, adminRule); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func protoForIP(ip net.IPNet) iptables.Protocol {
+ if ip.IP.To4() != nil {
+ return iptables.ProtocolIPv4
+ }
+ return iptables.ProtocolIPv6
+}
+
+func (ib *iptablesBackend) addRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error {
+ rules := make([][]string, 0)
+ for _, ip := range conf.PrevResult.IPs {
+ if protoForIP(ip.Address) == proto {
+ rules = append(rules, getPrivChainRules(ipString(ip.Address))...)
+ }
+ }
+
+ if len(rules) > 0 {
+ if err := ib.setupChains(ipt); err != nil {
+ return err
+ }
+
+ // Clean up on any errors
+ var err error
+ defer func() {
+ if err != nil {
+ cleanupRules(ipt, ib.privChainName, rules)
+ }
+ }()
+
+ for _, rule := range rules {
+ err = ipt.AppendUnique("filter", ib.privChainName, rule...)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func (ib *iptablesBackend) delRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error {
+ rules := make([][]string, 0)
+ for _, ip := range conf.PrevResult.IPs {
+ if protoForIP(ip.Address) == proto {
+ rules = append(rules, getPrivChainRules(ipString(ip.Address))...)
+ }
+ }
+
+ if len(rules) > 0 {
+ cleanupRules(ipt, ib.privChainName, rules)
+ }
+
+ return nil
+}
+
+func findProtos(conf *FirewallNetConf) []iptables.Protocol {
+ protos := []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6}
+ if conf.PrevResult != nil {
+ // If PrevResult is given, scan all IP addresses to figure out
+ // which IP versions to use
+ protos = []iptables.Protocol{}
+ for _, addr := range conf.PrevResult.IPs {
+ if addr.Address.IP.To4() != nil {
+ protos = append(protos, iptables.ProtocolIPv4)
+ } else {
+ protos = append(protos, iptables.ProtocolIPv6)
+ }
+ }
+ }
+ return protos
+}
+
+type iptablesBackend struct {
+ protos map[iptables.Protocol]*iptables.IPTables
+ privChainName string
+ adminChainName string
+ ifName string
+}
+
+// iptablesBackend implements the FirewallBackend interface
+var _ FirewallBackend = &iptablesBackend{}
+
+func newIptablesBackend() (FirewallBackend, error) {
+ adminChainName := "CNI-ADMIN"
+
+ backend := &iptablesBackend{
+ privChainName: "CNI-FORWARD",
+ adminChainName: adminChainName,
+ protos: make(map[iptables.Protocol]*iptables.IPTables),
+ }
+
+ for _, proto := range []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6} {
+ ipt, err := iptables.NewWithProtocol(proto)
+ if err != nil {
+ return nil, fmt.Errorf("could not initialize iptables protocol %v: %v", proto, err)
+ }
+ backend.protos[proto] = ipt
+ }
+
+ return backend, nil
+}
+
+func (ib *iptablesBackend) Add(conf *FirewallNetConf) error {
+ for proto, ipt := range ib.protos {
+ if err := ib.addRules(conf, ipt, proto); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ib *iptablesBackend) Del(conf *FirewallNetConf) error {
+ for proto, ipt := range ib.protos {
+ ib.delRules(conf, ipt, proto)
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/go-iptables/LICENSE b/vendor/github.com/coreos/go-iptables/LICENSE
new file mode 100644
index 000000000..37ec93a14
--- /dev/null
+++ b/vendor/github.com/coreos/go-iptables/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/coreos/go-iptables/NOTICE b/vendor/github.com/coreos/go-iptables/NOTICE
new file mode 100644
index 000000000..23a0ada2f
--- /dev/null
+++ b/vendor/github.com/coreos/go-iptables/NOTICE
@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2018 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).
diff --git a/vendor/github.com/coreos/go-iptables/README.md b/vendor/github.com/coreos/go-iptables/README.md
new file mode 100644
index 000000000..974a983e0
--- /dev/null
+++ b/vendor/github.com/coreos/go-iptables/README.md
@@ -0,0 +1,10 @@
+# go-iptables
+
+[![GoDoc](https://godoc.org/github.com/coreos/go-iptables/iptables?status.svg)](https://godoc.org/github.com/coreos/go-iptables/iptables)
+[![Build Status](https://travis-ci.org/coreos/go-iptables.png?branch=master)](https://travis-ci.org/coreos/go-iptables)
+
+Go bindings for iptables utility.
+
+In-kernel netfilter does not have a good userspace API. The tables are manipulated via setsockopt that sets/replaces the entire table. Changes to existing table need to be resolved by userspace code which is difficult and error-prone. Netfilter developers heavily advocate using iptables utlity for programmatic manipulation.
+
+go-iptables wraps invocation of iptables utility with functions to append and delete rules; create, clear and delete chains.
diff --git a/vendor/github.com/coreos/go-iptables/iptables/iptables.go b/vendor/github.com/coreos/go-iptables/iptables/iptables.go
new file mode 100644
index 000000000..3b62fe205
--- /dev/null
+++ b/vendor/github.com/coreos/go-iptables/iptables/iptables.go
@@ -0,0 +1,475 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package iptables
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net"
+ "os/exec"
+ "regexp"
+ "strconv"
+ "strings"
+ "syscall"
+)
+
+// Adds the output of stderr to exec.ExitError
+type Error struct {
+ exec.ExitError
+ cmd exec.Cmd
+ msg string
+}
+
+func (e *Error) ExitStatus() int {
+ return e.Sys().(syscall.WaitStatus).ExitStatus()
+}
+
+func (e *Error) Error() string {
+ return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg)
+}
+
+// IsNotExist returns true if the error is due to the chain or rule not existing
+func (e *Error) IsNotExist() bool {
+ return e.ExitStatus() == 1 &&
+ (e.msg == "iptables: Bad rule (does a matching rule exist in that chain?).\n" ||
+ e.msg == "iptables: No chain/target/match by that name.\n")
+}
+
+// Protocol to differentiate between IPv4 and IPv6
+type Protocol byte
+
+const (
+ ProtocolIPv4 Protocol = iota
+ ProtocolIPv6
+)
+
+type IPTables struct {
+ path string
+ proto Protocol
+ hasCheck bool
+ hasWait bool
+ hasRandomFully bool
+ v1 int
+ v2 int
+ v3 int
+}
+
+// New creates a new IPTables.
+// For backwards compatibility, this always uses IPv4, i.e. "iptables".
+func New() (*IPTables, error) {
+ return NewWithProtocol(ProtocolIPv4)
+}
+
+// New creates a new IPTables for the given proto.
+// The proto will determine which command is used, either "iptables" or "ip6tables".
+func NewWithProtocol(proto Protocol) (*IPTables, error) {
+ path, err := exec.LookPath(getIptablesCommand(proto))
+ if err != nil {
+ return nil, err
+ }
+ vstring, err := getIptablesVersionString(path)
+ v1, v2, v3, err := extractIptablesVersion(vstring)
+
+ checkPresent, waitPresent, randomFullyPresent, err := getIptablesCommandSupport(v1, v2, v3)
+ if err != nil {
+ return nil, fmt.Errorf("error checking iptables version: %v", err)
+ }
+ ipt := IPTables{
+ path: path,
+ proto: proto,
+ hasCheck: checkPresent,
+ hasWait: waitPresent,
+ hasRandomFully: randomFullyPresent,
+ v1: v1,
+ v2: v2,
+ v3: v3,
+ }
+ return &ipt, nil
+}
+
+// Proto returns the protocol used by this IPTables.
+func (ipt *IPTables) Proto() Protocol {
+ return ipt.proto
+}
+
+// Exists checks if given rulespec in specified table/chain exists
+func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) {
+ if !ipt.hasCheck {
+ return ipt.existsForOldIptables(table, chain, rulespec)
+
+ }
+ cmd := append([]string{"-t", table, "-C", chain}, rulespec...)
+ err := ipt.run(cmd...)
+ eerr, eok := err.(*Error)
+ switch {
+ case err == nil:
+ return true, nil
+ case eok && eerr.ExitStatus() == 1:
+ return false, nil
+ default:
+ return false, err
+ }
+}
+
+// Insert inserts rulespec to specified table/chain (in specified pos)
+func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error {
+ cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...)
+ return ipt.run(cmd...)
+}
+
+// Append appends rulespec to specified table/chain
+func (ipt *IPTables) Append(table, chain string, rulespec ...string) error {
+ cmd := append([]string{"-t", table, "-A", chain}, rulespec...)
+ return ipt.run(cmd...)
+}
+
+// AppendUnique acts like Append except that it won't add a duplicate
+func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error {
+ exists, err := ipt.Exists(table, chain, rulespec...)
+ if err != nil {
+ return err
+ }
+
+ if !exists {
+ return ipt.Append(table, chain, rulespec...)
+ }
+
+ return nil
+}
+
+// Delete removes rulespec in specified table/chain
+func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
+ cmd := append([]string{"-t", table, "-D", chain}, rulespec...)
+ return ipt.run(cmd...)
+}
+
+// List rules in specified table/chain
+func (ipt *IPTables) List(table, chain string) ([]string, error) {
+ args := []string{"-t", table, "-S", chain}
+ return ipt.executeList(args)
+}
+
+// List rules (with counters) in specified table/chain
+func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) {
+ args := []string{"-t", table, "-v", "-S", chain}
+ return ipt.executeList(args)
+}
+
+// ListChains returns a slice containing the name of each chain in the specified table.
+func (ipt *IPTables) ListChains(table string) ([]string, error) {
+ args := []string{"-t", table, "-S"}
+
+ result, err := ipt.executeList(args)
+ if err != nil {
+ return nil, err
+ }
+
+ // Iterate over rules to find all default (-P) and user-specified (-N) chains.
+ // Chains definition always come before rules.
+ // Format is the following:
+ // -P OUTPUT ACCEPT
+ // -N Custom
+ var chains []string
+ for _, val := range result {
+ if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") {
+ chains = append(chains, strings.Fields(val)[1])
+ } else {
+ break
+ }
+ }
+ return chains, nil
+}
+
+// Stats lists rules including the byte and packet counts
+func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
+ args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"}
+ lines, err := ipt.executeList(args)
+ if err != nil {
+ return nil, err
+ }
+
+ appendSubnet := func(addr string) string {
+ if strings.IndexByte(addr, byte('/')) < 0 {
+ if strings.IndexByte(addr, '.') < 0 {
+ return addr + "/128"
+ }
+ return addr + "/32"
+ }
+ return addr
+ }
+
+ ipv6 := ipt.proto == ProtocolIPv6
+
+ rows := [][]string{}
+ for i, line := range lines {
+ // Skip over chain name and field header
+ if i < 2 {
+ continue
+ }
+
+ // Fields:
+ // 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options
+ line = strings.TrimSpace(line)
+ fields := strings.Fields(line)
+
+ // The ip6tables verbose output cannot be naively split due to the default "opt"
+ // field containing 2 single spaces.
+ if ipv6 {
+ // Check if field 6 is "opt" or "source" address
+ dest := fields[6]
+ ip, _, _ := net.ParseCIDR(dest)
+ if ip == nil {
+ ip = net.ParseIP(dest)
+ }
+
+ // If we detected a CIDR or IP, the "opt" field is empty.. insert it.
+ if ip != nil {
+ f := []string{}
+ f = append(f, fields[:4]...)
+ f = append(f, " ") // Empty "opt" field for ip6tables
+ f = append(f, fields[4:]...)
+ fields = f
+ }
+ }
+
+ // Adjust "source" and "destination" to include netmask, to match regular
+ // List output
+ fields[7] = appendSubnet(fields[7])
+ fields[8] = appendSubnet(fields[8])
+
+ // Combine "options" fields 9... into a single space-delimited field.
+ options := fields[9:]
+ fields = fields[:9]
+ fields = append(fields, strings.Join(options, " "))
+ rows = append(rows, fields)
+ }
+ return rows, nil
+}
+
+func (ipt *IPTables) executeList(args []string) ([]string, error) {
+ var stdout bytes.Buffer
+ if err := ipt.runWithOutput(args, &stdout); err != nil {
+ return nil, err
+ }
+
+ rules := strings.Split(stdout.String(), "\n")
+ if len(rules) > 0 && rules[len(rules)-1] == "" {
+ rules = rules[:len(rules)-1]
+ }
+
+ return rules, nil
+}
+
+// NewChain creates a new chain in the specified table.
+// If the chain already exists, it will result in an error.
+func (ipt *IPTables) NewChain(table, chain string) error {
+ return ipt.run("-t", table, "-N", chain)
+}
+
+// ClearChain flushed (deletes all rules) in the specified table/chain.
+// If the chain does not exist, a new one will be created
+func (ipt *IPTables) ClearChain(table, chain string) error {
+ err := ipt.NewChain(table, chain)
+
+ eerr, eok := err.(*Error)
+ switch {
+ case err == nil:
+ return nil
+ case eok && eerr.ExitStatus() == 1:
+ // chain already exists. Flush (clear) it.
+ return ipt.run("-t", table, "-F", chain)
+ default:
+ return err
+ }
+}
+
+// RenameChain renames the old chain to the new one.
+func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error {
+ return ipt.run("-t", table, "-E", oldChain, newChain)
+}
+
+// DeleteChain deletes the chain in the specified table.
+// The chain must be empty
+func (ipt *IPTables) DeleteChain(table, chain string) error {
+ return ipt.run("-t", table, "-X", chain)
+}
+
+// ChangePolicy changes policy on chain to target
+func (ipt *IPTables) ChangePolicy(table, chain, target string) error {
+ return ipt.run("-t", table, "-P", chain, target)
+}
+
+// Check if the underlying iptables command supports the --random-fully flag
+func (ipt *IPTables) HasRandomFully() bool {
+ return ipt.hasRandomFully
+}
+
+// Return version components of the underlying iptables command
+func (ipt *IPTables) GetIptablesVersion() (int, int, int) {
+ return ipt.v1, ipt.v2, ipt.v3
+}
+
+// run runs an iptables command with the given arguments, ignoring
+// any stdout output
+func (ipt *IPTables) run(args ...string) error {
+ return ipt.runWithOutput(args, nil)
+}
+
+// runWithOutput runs an iptables command with the given arguments,
+// writing any stdout output to the given writer
+func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
+ args = append([]string{ipt.path}, args...)
+ if ipt.hasWait {
+ args = append(args, "--wait")
+ } else {
+ fmu, err := newXtablesFileLock()
+ if err != nil {
+ return err
+ }
+ ul, err := fmu.tryLock()
+ if err != nil {
+ return err
+ }
+ defer ul.Unlock()
+ }
+
+ var stderr bytes.Buffer
+ cmd := exec.Cmd{
+ Path: ipt.path,
+ Args: args,
+ Stdout: stdout,
+ Stderr: &stderr,
+ }
+
+ if err := cmd.Run(); err != nil {
+ switch e := err.(type) {
+ case *exec.ExitError:
+ return &Error{*e, cmd, stderr.String()}
+ default:
+ return err
+ }
+ }
+
+ return nil
+}
+
+// getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables".
+func getIptablesCommand(proto Protocol) string {
+ if proto == ProtocolIPv6 {
+ return "ip6tables"
+ } else {
+ return "iptables"
+ }
+}
+
+// Checks if iptables has the "-C" and "--wait" flag
+func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool, error) {
+
+ return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3), nil
+}
+
+// getIptablesVersion returns the first three components of the iptables version.
+// e.g. "iptables v1.3.66" would return (1, 3, 66, nil)
+func extractIptablesVersion(str string) (int, int, int, error) {
+ versionMatcher := regexp.MustCompile("v([0-9]+)\\.([0-9]+)\\.([0-9]+)")
+ result := versionMatcher.FindStringSubmatch(str)
+ if result == nil {
+ return 0, 0, 0, fmt.Errorf("no iptables version found in string: %s", str)
+ }
+
+ v1, err := strconv.Atoi(result[1])
+ if err != nil {
+ return 0, 0, 0, err
+ }
+
+ v2, err := strconv.Atoi(result[2])
+ if err != nil {
+ return 0, 0, 0, err
+ }
+
+ v3, err := strconv.Atoi(result[3])
+ if err != nil {
+ return 0, 0, 0, err
+ }
+
+ return v1, v2, v3, nil
+}
+
+// Runs "iptables --version" to get the version string
+func getIptablesVersionString(path string) (string, error) {
+ cmd := exec.Command(path, "--version")
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ err := cmd.Run()
+ if err != nil {
+ return "", err
+ }
+ return out.String(), nil
+}
+
+// Checks if an iptables version is after 1.4.11, when --check was added
+func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool {
+ if v1 > 1 {
+ return true
+ }
+ if v1 == 1 && v2 > 4 {
+ return true
+ }
+ if v1 == 1 && v2 == 4 && v3 >= 11 {
+ return true
+ }
+ return false
+}
+
+// Checks if an iptables version is after 1.4.20, when --wait was added
+func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool {
+ if v1 > 1 {
+ return true
+ }
+ if v1 == 1 && v2 > 4 {
+ return true
+ }
+ if v1 == 1 && v2 == 4 && v3 >= 20 {
+ return true
+ }
+ return false
+}
+
+// Checks if an iptables version is after 1.6.2, when --random-fully was added
+func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool {
+ if v1 > 1 {
+ return true
+ }
+ if v1 == 1 && v2 > 6 {
+ return true
+ }
+ if v1 == 1 && v2 == 6 && v3 >= 2 {
+ return true
+ }
+ return false
+}
+
+// Checks if a rule specification exists for a table
+func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) {
+ rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ")
+ args := []string{"-t", table, "-S"}
+ var stdout bytes.Buffer
+ err := ipt.runWithOutput(args, &stdout)
+ if err != nil {
+ return false, err
+ }
+ return strings.Contains(stdout.String(), rs), nil
+}
diff --git a/vendor/github.com/coreos/go-iptables/iptables/lock.go b/vendor/github.com/coreos/go-iptables/iptables/lock.go
new file mode 100644
index 000000000..a88e92b4e
--- /dev/null
+++ b/vendor/github.com/coreos/go-iptables/iptables/lock.go
@@ -0,0 +1,84 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package iptables
+
+import (
+ "os"
+ "sync"
+ "syscall"
+)
+
+const (
+ // In earlier versions of iptables, the xtables lock was implemented
+ // via a Unix socket, but now flock is used via this lockfile:
+ // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707
+ // Note the LSB-conforming "/run" directory does not exist on old
+ // distributions, so assume "/var" is symlinked
+ xtablesLockFilePath = "/var/run/xtables.lock"
+
+ defaultFilePerm = 0600
+)
+
+type Unlocker interface {
+ Unlock() error
+}
+
+type nopUnlocker struct{}
+
+func (_ nopUnlocker) Unlock() error { return nil }
+
+type fileLock struct {
+ // mu is used to protect against concurrent invocations from within this process
+ mu sync.Mutex
+ fd int
+}
+
+// tryLock takes an exclusive lock on the xtables lock file without blocking.
+// This is best-effort only: if the exclusive lock would block (i.e. because
+// another process already holds it), no error is returned. Otherwise, any
+// error encountered during the locking operation is returned.
+// The returned Unlocker should be used to release the lock when the caller is
+// done invoking iptables commands.
+func (l *fileLock) tryLock() (Unlocker, error) {
+ l.mu.Lock()
+ err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
+ switch err {
+ case syscall.EWOULDBLOCK:
+ l.mu.Unlock()
+ return nopUnlocker{}, nil
+ case nil:
+ return l, nil
+ default:
+ l.mu.Unlock()
+ return nil, err
+ }
+}
+
+// Unlock closes the underlying file, which implicitly unlocks it as well. It
+// also unlocks the associated mutex.
+func (l *fileLock) Unlock() error {
+ defer l.mu.Unlock()
+ return syscall.Close(l.fd)
+}
+
+// newXtablesFileLock opens a new lock on the xtables lockfile without
+// acquiring the lock
+func newXtablesFileLock() (*fileLock, error) {
+ fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm)
+ if err != nil {
+ return nil, err
+ }
+ return &fileLock{fd: fd}, nil
+}