forked from projectcalico/felix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conntrack.go
104 lines (93 loc) · 3.35 KB
/
conntrack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
//
// 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 conntrack
import (
"net"
"os/exec"
"strings"
log "github.com/sirupsen/logrus"
)
// For TCP/UDP, each conntrack entry holds two copies of the tuple
// (src addr, dst addr, src port, dst port). One copy for the original direction and one copy for
// the reply direction. This is how the kernel handles NAT: by looking up the tuple for a packet
// by its original tuple and mapping onto the corresponding reply direction tuple (or vice versa).
// The reply tuple is calculated when the original outgoing packet is processed (and possibly
// NATted).
//
// When we delete conntrack entries by IP address, we need to specify which element of the tuple
// to look in. This slice holds the flags corresponding to the fields we care about. Since we're
// deleting entries for local workload endpoints, either the endpoint originated the traffic, or it
// received the traffic and replied to it. In the originating case, the "original source" will be
// set to the endpoint's IP; in the other case, the "reply source". Hence, it's sufficient to only
// look in those two fields.
var deleteDirections = []string{
"--orig-src",
"--reply-src",
}
const numRetries = 3
type Conntrack struct {
newCmd newCmd
}
func New() *Conntrack {
return NewWithCmdShim(func(name string, arg ...string) CmdIface {
return exec.Command(name, arg...)
})
}
// NewWithCmdShim is a test constructor that allows for shimming exec.Command.
func NewWithCmdShim(newCmd newCmd) *Conntrack {
return &Conntrack{
newCmd: newCmd,
}
}
type newCmd func(name string, arg ...string) CmdIface
type CmdIface interface {
CombinedOutput() ([]byte, error)
}
func (c Conntrack) RemoveConntrackFlows(ipVersion uint8, ipAddr net.IP) {
var family string
switch ipVersion {
case 4:
family = "ipv4"
case 6:
family = "ipv6"
default:
log.WithField("version", ipVersion).Panic("Unknown IP version")
}
log.WithField("ip", ipAddr).Info("Removing conntrack flows")
for _, direction := range deleteDirections {
logCxt := log.WithFields(log.Fields{"ip": ipAddr, "direction": direction})
// Retry a few times because the conntrack command seems to fail at random.
for retry := 0; retry <= numRetries; retry += 1 {
cmd := c.newCmd("conntrack",
"--family", family,
"--delete", direction,
ipAddr.String())
output, err := cmd.CombinedOutput()
if err == nil {
logCxt.Debug("Successfully removed conntrack flows.")
break
}
if strings.Contains(string(output), "0 flow entries") {
// Success, there were no flows.
logCxt.Debug("IP wasn't in conntrack")
break
}
if retry == numRetries {
logCxt.WithError(err).Error("Failed to remove conntrack flows after retries.")
} else {
logCxt.WithError(err).Warn("Failed to remove conntrack flows, will retry...")
}
}
}
}