@@ -30,45 +30,102 @@ func makeNATMapper(fw flowWalker) natMapper {
30
30
return natMapper {fw }
31
31
}
32
32
33
- func toMapping (f conntrack.Conn ) * endpointMapping {
34
- var mapping endpointMapping
35
- if f .Orig .Src .Equal (f .Reply .Dst ) {
36
- mapping = endpointMapping {
37
- originalIP : f .Reply .Src ,
38
- originalPort : f .Reply .SrcPort ,
39
- rewrittenIP : f .Orig .Dst ,
40
- rewrittenPort : f .Orig .DstPort ,
41
- }
42
- } else {
43
- mapping = endpointMapping {
44
- originalIP : f .Orig .Src ,
45
- originalPort : f .Orig .SrcPort ,
46
- rewrittenIP : f .Reply .Dst ,
47
- rewrittenPort : f .Reply .DstPort ,
48
- }
49
- }
50
-
51
- return & mapping
33
+ func endpointNodeID (scope string , ip net.IP , port uint16 ) string {
34
+ return report .MakeEndpointNodeID (scope , "" , ip .String (), strconv .Itoa (int (port )))
52
35
}
53
36
54
- // applyNAT duplicates Nodes in the endpoint topology of a report, based on
37
+ /*
38
+
39
+ Some examples of connections with NAT:
40
+
41
+ Pod to pod via Kubernetes service
42
+ picked up by ebpf as 10.32.0.16:47600->10.105.173.176:5432 and 10.32.0.6:5432 (??)
43
+ 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
+ We want: 10.32.0.16:47600->10.32.0.6:5432
45
+ - replace the destination (== NAT orig dst) with the NAT reply source
46
+
47
+ Incoming from outside the cluster to a NodePort:
48
+ picked up by ebpf as 10.32.0.1:13488->10.32.0.7:80
49
+ 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
+ We want: 37.157.33.76:13488->10.32.0.7:80
51
+ - replace the source (== NAT reply dst) with the NAT original source
52
+
53
+ Outgoing from a pod:
54
+ picked up by ebpf as 10.32.0.7:36078->18.221.99.178:443
55
+ 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
+ We want: 10.32.0.7:36078->18.221.99.178:443
57
+ - leave it alone.
58
+
59
+ Docker container exposing port to similar on different host
60
+ host1:
61
+ picked up by ebpf as ip-172-31-5-80;172.17.0.2:43042->172.31.2.17:8080
62
+ 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)
65
+ host2:
66
+ picked up by ebpf as 172.31.5.80:43042->ip-172-31-2-17;172.17.0.2:80
67
+ 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)
71
+
72
+ 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
77
+ */
78
+
79
+ // applyNAT modifies Nodes in the endpoint topology of a report, based on
55
80
// the NAT table.
56
81
func (n natMapper ) applyNAT (rpt report.Report , scope string ) {
57
82
n .flowWalker .walkFlows (func (f conntrack.Conn , _ bool ) {
58
- mapping := toMapping (f )
83
+ replyDstID := endpointNodeID (scope , f .Reply .Dst , f .Reply .DstPort )
84
+ origSrcID := endpointNodeID (scope , f .Orig .Src , f .Orig .SrcPort )
59
85
60
- realEndpointPort := strconv .Itoa (int (mapping .originalPort ))
61
- copyEndpointPort := strconv .Itoa (int (mapping .rewrittenPort ))
62
- realEndpointID := report .MakeEndpointNodeID (scope , "" , mapping .originalIP .String (), realEndpointPort )
63
- copyEndpointID := report .MakeEndpointNodeID (scope , "" , mapping .rewrittenIP .String (), copyEndpointPort )
64
-
65
- node , ok := rpt .Endpoint .Nodes [realEndpointID ]
66
- if ! ok {
67
- return
86
+ if (f .Status & conntrack .IPS_SRC_NAT ) != 0 {
87
+ 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 )
94
+ } else if origSrcNode , ok := rpt .Endpoint .Nodes [origSrcID ]; ok {
95
+ // or add NAT reply destination as a copy of NAT original source
96
+ newNode := origSrcNode .WithID (replyDstID ).WithLatests (map [string ]string {
97
+ CopyOf : origSrcID ,
98
+ })
99
+ rpt .Endpoint .AddNode (newNode )
100
+ }
101
+ }
68
102
}
69
103
70
- rpt .Endpoint .AddNode (node .WithID (copyEndpointID ).WithLatests (map [string ]string {
71
- CopyOf : realEndpointID ,
72
- }))
104
+ 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
+ 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
+
118
+ // add nat original destination as a copy of nat reply source
119
+ replySrcNode , ok := rpt .Endpoint .Nodes [replySrcID ]
120
+ if ! ok {
121
+ replySrcNode = report .MakeNode (replySrcID )
122
+ }
123
+ newNode := replySrcNode .WithID (toID ).WithLatests (map [string ]string {
124
+ CopyOf : replySrcID ,
125
+ })
126
+ rpt .Endpoint .AddNode (newNode )
127
+ }
128
+
129
+ }
73
130
})
74
131
}
0 commit comments