1// +build linux
2
3/*
4Copyright 2017 The Kubernetes Authors.
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17*/
18
19package iptables
20
21import (
22	"fmt"
23	"net"
24	"os"
25	"time"
26
27	"golang.org/x/sys/unix"
28	utilerrors "k8s.io/apimachinery/pkg/util/errors"
29	"k8s.io/apimachinery/pkg/util/wait"
30)
31
32type locker struct {
33	lock16 *os.File
34	lock14 *net.UnixListener
35}
36
37func (l *locker) Close() error {
38	errList := []error{}
39	if l.lock16 != nil {
40		if err := l.lock16.Close(); err != nil {
41			errList = append(errList, err)
42		}
43	}
44	if l.lock14 != nil {
45		if err := l.lock14.Close(); err != nil {
46			errList = append(errList, err)
47		}
48	}
49	return utilerrors.NewAggregate(errList)
50}
51
52func grabIptablesLocks(lockfilePath14x, lockfilePath16x string) (iptablesLocker, error) {
53	var err error
54	var success bool
55
56	l := &locker{}
57	defer func(l *locker) {
58		// Clean up immediately on failure
59		if !success {
60			l.Close()
61		}
62	}(l)
63
64	// Grab both 1.6.x and 1.4.x-style locks; we don't know what the
65	// iptables-restore version is if it doesn't support --wait, so we
66	// can't assume which lock method it'll use.
67
68	// Roughly duplicate iptables 1.6.x xtables_lock() function.
69	l.lock16, err = os.OpenFile(lockfilePath16x, os.O_CREATE, 0600)
70	if err != nil {
71		return nil, fmt.Errorf("failed to open iptables lock %s: %v", lockfilePath16x, err)
72	}
73
74	if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) {
75		if err := grabIptablesFileLock(l.lock16); err != nil {
76			return false, nil
77		}
78		return true, nil
79	}); err != nil {
80		return nil, fmt.Errorf("failed to acquire new iptables lock: %v", err)
81	}
82
83	// Roughly duplicate iptables 1.4.x xtables_lock() function.
84	if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) {
85		l.lock14, err = net.ListenUnix("unix", &net.UnixAddr{Name: lockfilePath14x, Net: "unix"})
86		if err != nil {
87			return false, nil
88		}
89		return true, nil
90	}); err != nil {
91		return nil, fmt.Errorf("failed to acquire old iptables lock: %v", err)
92	}
93
94	success = true
95	return l, nil
96}
97
98func grabIptablesFileLock(f *os.File) error {
99	return unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB)
100}
101