@@ -38,61 +38,66 @@ func endpointNodeID(scope string, ip net.IP, port uint16) string {
38
38
39
39
Some examples of connections with NAT:
40
40
41
+ Here 10.32.0.X are pod addresses; 172.31.X.X are node addresses; 10.10X.X.X are service virtual addresses.
42
+
41
43
Pod to pod via Kubernetes service
42
44
picked up by ebpf as 10.32.0.16:47600->10.105.173.176:5432 and 10.32.0.6:5432 (??)
43
45
NAT IPS_DST_NAT orig: 10.32.0.16:47600->10.105.173.176:5432, reply: 10.32.0.6:5432->10.32.0.16:47600
44
46
We want: 10.32.0.16:47600->10.32.0.6:5432
45
- - replace the destination (== NAT orig dst) with the NAT reply source
47
+ - replace the destination (== NAT orig dst) with the NAT reply source (A)
46
48
47
49
Incoming from outside the cluster to a NodePort:
48
50
picked up by ebpf as 10.32.0.1:13488->10.32.0.7:80
49
51
NAT: IPS_SRC_NAT IPS_DST_NAT orig: 37.157.33.76:13488->172.31.2.17:30081, reply: 10.32.0.7:80->10.32.0.1:13488
50
52
We want: 37.157.33.76:13488->10.32.0.7:80
51
- - replace the source (== NAT reply dst) with the NAT original source
53
+ - replace the source (== NAT reply dst) with the NAT original source (B)
54
+ To match another probe with the other side of this connection, also want 37.157.33.76:13488->172.31.2.17:30081
55
+ - add NAT original dst as a copy of nat reply source (C)
52
56
53
57
Outgoing from a pod:
54
58
picked up by ebpf as 10.32.0.7:36078->18.221.99.178:443
55
59
NAT: IPS_SRC_NAT orig: 10.32.0.7:36078->18.221.99.178:443, reply: 18.221.99.178:443->172.31.2.17:36078
56
60
We want: 10.32.0.7:36078->18.221.99.178:443
57
- - leave it alone.
61
+ - leave it alone. (D)
58
62
59
63
Docker container exposing port to similar on different host
60
64
host1:
61
65
picked up by ebpf as ip-172-31-5-80;172.17.0.2:43042->172.31.2.17:8080
62
66
NAT: IPS_SRC_NAT orig: 172.17.0.2:43042->172.31.2.17:8080, reply: 172.31.2.17:8080-> 172.31.5.80:43042
63
- applying standard rule: ip-172-31-5-80;172.17.0.2:43042->172.31.2.17:8080 (i.e. no change)
64
- we could add 172.31.5.80:43042 (nat reply destination) as a copy of ip-172-31-5-80;172.17.0.2:43042 (nat orig source)
67
+ We want: 172.31.5.80:43042->172.31.2.17:8080
68
+ - can't have a blanket rule to replace NAT original source with NAT reply destination, because that breaks case D.
69
+ we could add 172.31.5.80:43042 (nat reply destination) as a copy of ip-172-31-5-80;172.17.0.2:43042 (nat orig source) (E)
65
70
host2:
66
71
picked up by ebpf as 172.31.5.80:43042->ip-172-31-2-17;172.17.0.2:80
67
72
NAT: IPS_DST_NAT orig: 172.31.5.80:43042->172.31.2.17:8080, reply: 172.17.0.2:80->172.31.5.80:43042
68
- Ideally we might want: ip-172-31-5-80;172.17.0.2:43042->ip-172-31-2-17;172.17.0.2:80
69
- applying standard rule: 172.31.5.80:43042->ip- 172-31-2-17;172 .17.0.2:80 (i.e. no change)
70
- we could add 172.31.2.17:8080 (nat original destination) as a copy of ip-172-31-2-17;172.17.0.2:80 ( nat reply source)
73
+ Rule A doesn't match and rule B is a no-op because the addresses are the same.
74
+ To match another probe with the other side of this connection, also want 172.31.5.80:43042->172.31.2 .17:8080
75
+ - add NAT original dst as a copy of nat reply source (C )
71
76
72
77
All of the above can be satisfied by these rules:
73
- For SRC_NAT either add NAT orig source as a copy of NAT reply destination
74
- or add NAT reply destination as a copy of NAT original source
75
- For DST_NAT replace the destination in adjacencies with the NAT reply source
76
- and add nat original destination as a copy of nat reply source
78
+ For SRC_NAT
79
+ replace the source (== NAT reply dst) with the NAT original source (B)
80
+ or add NAT reply destination as a copy of NAT original source (E)
81
+ For DST_NAT
82
+ replace NAT original destination in adjacencies with the NAT reply source (A)
83
+ or add NAT original destination as a copy of NAT reply source (C)
77
84
*/
78
85
79
86
// applyNAT modifies Nodes in the endpoint topology of a report, based on
80
87
// the NAT table.
81
88
func (n natMapper ) applyNAT (rpt report.Report , scope string ) {
82
89
n .flowWalker .walkFlows (func (f conntrack.Conn , _ bool ) {
83
- replyDstID := endpointNodeID (scope , f .Reply .Dst , f .Reply .DstPort )
84
- origSrcID := endpointNodeID (scope , f .Orig .Src , f .Orig .SrcPort )
85
90
86
91
if (f .Status & conntrack .IPS_SRC_NAT ) != 0 {
92
+ origSrcID := endpointNodeID (scope , f .Orig .Src , f .Orig .SrcPort )
93
+ replyDstID := endpointNodeID (scope , f .Reply .Dst , f .Reply .DstPort )
87
94
if replyDstID != origSrcID {
88
- // either add NAT orig source as a copy of NAT reply destination
89
- if replyDstNode , ok := rpt .Endpoint .Nodes [replyDstID ]; ok {
90
- newNode := replyDstNode .WithID (origSrcID ).WithLatests (map [string ]string {
91
- CopyOf : replyDstID ,
92
- })
93
- rpt .Endpoint .AddNode (newNode )
95
+ if fromNode , ok := rpt .Endpoint .Nodes [replyDstID ]; ok {
96
+ // replace the source (== NAT reply dst) with the NAT original source (B)
97
+ delete (rpt .Endpoint .Nodes , replyDstID )
98
+ rpt .Endpoint .AddNode (fromNode .WithID (origSrcID ))
94
99
} else if origSrcNode , ok := rpt .Endpoint .Nodes [origSrcID ]; ok {
95
- // or add NAT reply destination as a copy of NAT original source
100
+ // add NAT reply destination as a copy of NAT original source (E)
96
101
newNode := origSrcNode .WithID (replyDstID ).WithLatests (map [string ]string {
97
102
CopyOf : origSrcID ,
98
103
})
@@ -102,29 +107,23 @@ func (n natMapper) applyNAT(rpt report.Report, scope string) {
102
107
}
103
108
104
109
if (f .Status & conntrack .IPS_DST_NAT ) != 0 {
105
- fromID := endpointNodeID (scope , f .Reply .Dst , f .Reply .DstPort )
106
- fromNode , ok := rpt .Endpoint .Nodes [fromID ]
107
- if ! ok {
108
- return
109
- }
110
- toID := endpointNodeID (scope , f .Orig .Dst , f .Orig .DstPort )
111
-
112
- // replace destination with reply source
113
110
replySrcID := endpointNodeID (scope , f .Reply .Src , f .Reply .SrcPort )
114
- if replySrcID != toID {
115
- fromNode .Adjacency = fromNode .Adjacency .Minus (toID )
116
- fromNode = fromNode .WithAdjacent (replySrcID )
117
- rpt .Endpoint .Nodes [fromID ] = fromNode
118
-
119
- // add nat original destination as a copy of nat reply source
120
- replySrcNode , ok := rpt .Endpoint .Nodes [replySrcID ]
121
- if ! ok {
122
- replySrcNode = report .MakeNode (replySrcID )
111
+ origDstID := endpointNodeID (scope , f .Orig .Dst , f .Orig .DstPort )
112
+ if replySrcID != origDstID {
113
+ fromID := endpointNodeID (scope , f .Reply .Dst , f .Reply .DstPort )
114
+ fromNode , ok := rpt .Endpoint .Nodes [fromID ]
115
+ if ok && fromNode .Adjacency .Contains (origDstID ) {
116
+ // replace NAT original destination in adjacencies with the NAT reply source (A)
117
+ fromNode .Adjacency = fromNode .Adjacency .Minus (origDstID )
118
+ fromNode = fromNode .WithAdjacent (replySrcID )
119
+ rpt .Endpoint .Nodes [fromID ] = fromNode
120
+ } else if replySrcNode , ok := rpt .Endpoint .Nodes [replySrcID ]; ok {
121
+ // add NAT original destination as a copy of NAT reply source (C)
122
+ newNode := replySrcNode .WithID (origDstID ).WithLatests (map [string ]string {
123
+ CopyOf : replySrcID ,
124
+ })
125
+ rpt .Endpoint .AddNode (newNode )
123
126
}
124
- newNode := replySrcNode .WithID (toID ).WithLatests (map [string ]string {
125
- CopyOf : replySrcID ,
126
- })
127
- rpt .Endpoint .AddNode (newNode )
128
127
}
129
128
130
129
}
0 commit comments