@@ -38,61 +38,66 @@ func endpointNodeID(scope string, ip net.IP, port uint16) string {
3838
3939Some examples of connections with NAT:
4040
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+
4143Pod to pod via Kubernetes service
4244 picked up by ebpf as 10.32.0.16:47600->10.105.173.176:5432 and 10.32.0.6:5432 (??)
4345 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
4446 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)
4648
4749Incoming from outside the cluster to a NodePort:
4850 picked up by ebpf as 10.32.0.1:13488->10.32.0.7:80
4951 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
5052 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)
5256
5357Outgoing from a pod:
5458 picked up by ebpf as 10.32.0.7:36078->18.221.99.178:443
5559 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
5660 We want: 10.32.0.7:36078->18.221.99.178:443
57- - leave it alone.
61+ - leave it alone. (D)
5862
5963Docker container exposing port to similar on different host
6064host1:
6165 picked up by ebpf as ip-172-31-5-80;172.17.0.2:43042->172.31.2.17:8080
6266 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)
6570host2:
6671 picked up by ebpf as 172.31.5.80:43042->ip-172-31-2-17;172.17.0.2:80
6772 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 )
7176
7277All 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)
7784*/
7885
7986// applyNAT modifies Nodes in the endpoint topology of a report, based on
8087// the NAT table.
8188func (n natMapper ) applyNAT (rpt report.Report , scope string ) {
8289 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 )
8590
8691 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 )
8794 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 ))
9499 } 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)
96101 newNode := origSrcNode .WithID (replyDstID ).WithLatests (map [string ]string {
97102 CopyOf : origSrcID ,
98103 })
@@ -102,29 +107,23 @@ func (n natMapper) applyNAT(rpt report.Report, scope string) {
102107 }
103108
104109 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
113110 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 )
123126 }
124- newNode := replySrcNode .WithID (toID ).WithLatests (map [string ]string {
125- CopyOf : replySrcID ,
126- })
127- rpt .Endpoint .AddNode (newNode )
128127 }
129128
130129 }
0 commit comments