Browse code

libnet/pm/nat: move back fw / proxy steps into the bridge driver

Since commit 4e246efcd, individual portmappers are responsible for
setting up firewall rules and proxying according to their needs.

This change moves the responsibility back to the bridge driver, removing
unnecessary code duplication across portmappers. For now, only the nat
portmapper takes advantage of this.

This partially reverts commit 4e246efcd.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>

Albin Kerouanton authored on 2025/08/09 00:36:14
Showing 6 changed files
... ...
@@ -933,7 +933,8 @@ func networkPlatformOptions(conf *config.Config) []nwconfig.Option {
933 933
 				"DisableFilterForwardDrop": conf.BridgeConfig.DisableFilterForwardDrop,
934 934
 				"EnableIPTables":           conf.BridgeConfig.EnableIPTables,
935 935
 				"EnableIP6Tables":          conf.BridgeConfig.EnableIP6Tables,
936
-				"Hairpin":                  !conf.EnableUserlandProxy || conf.UserlandProxyPath == "",
936
+				"EnableProxy":              conf.EnableUserlandProxy && conf.UserlandProxyPath != "",
937
+				"ProxyPath":                conf.UserlandProxyPath,
937 938
 				"AllowDirectRouting":       conf.BridgeConfig.AllowDirectRouting,
938 939
 				"AcceptFwMark":             conf.BridgeConfig.BridgeAcceptFwMark,
939 940
 			},
... ...
@@ -69,10 +69,11 @@ type configuration struct {
69 69
 	DisableFilterForwardDrop bool
70 70
 	EnableIPTables           bool
71 71
 	EnableIP6Tables          bool
72
-	// Hairpin indicates whether packets sent from a container to a host port
73
-	// published by another container on the same bridge network should be
74
-	// hairpinned.
75
-	Hairpin            bool
72
+	// EnableProxy indicates whether the userland proxy should be used for NAT
73
+	// port-mappings that can't be fulfilled with firewall rules alone. This
74
+	// must not be true if ProxyPath is empty.
75
+	EnableProxy        bool
76
+	ProxyPath          string
76 77
 	AllowDirectRouting bool
77 78
 	AcceptFwMark       string
78 79
 }
... ...
@@ -472,15 +473,6 @@ func (n *bridgeNetwork) gwMode(v firewaller.IPVersion) gwMode {
472 472
 	return n.config.GwModeIPv6
473 473
 }
474 474
 
475
-func (n *bridgeNetwork) hairpin() bool {
476
-	n.Lock()
477
-	defer n.Unlock()
478
-	if n.driver == nil {
479
-		return false
480
-	}
481
-	return n.driver.config.Hairpin
482
-}
483
-
484 475
 func (n *bridgeNetwork) portMappers() *drvregistry.PortMappers {
485 476
 	n.Lock()
486 477
 	defer n.Unlock()
... ...
@@ -525,7 +517,7 @@ func (d *driver) configure(option map[string]any) error {
525 525
 	d.firewaller, err = newFirewaller(context.Background(), firewaller.Config{
526 526
 		IPv4:               config.EnableIPTables,
527 527
 		IPv6:               config.EnableIP6Tables,
528
-		Hairpin:            config.Hairpin,
528
+		Hairpin:            !config.EnableProxy,
529 529
 		AllowDirectRouting: config.AllowDirectRouting,
530 530
 		WSL2Mirrored:       isRunningUnderWSL2MirroredMode(context.Background()),
531 531
 	})
... ...
@@ -853,8 +845,8 @@ func (d *driver) createNetwork(ctx context.Context, config *networkConfiguration
853 853
 	}
854 854
 
855 855
 	// Module br_netfilter needs to be loaded with net.bridge.bridge-nf-call-ip[6]tables
856
-	// enabled to implement icc=false, or DNAT when hairpin mode is enabled.
857
-	enableBrNfCallIptables := !config.EnableICC || d.config.Hairpin
856
+	// enabled to implement icc=false, or DNAT when the userland-proxy is disabled.
857
+	enableBrNfCallIptables := !config.EnableICC || !d.config.EnableProxy
858 858
 
859 859
 	// Conditionally queue setup steps depending on configuration values.
860 860
 	for _, step := range []struct {
... ...
@@ -906,7 +898,7 @@ func (d *driver) createNetwork(ctx context.Context, config *networkConfiguration
906 906
 		},
907 907
 
908 908
 		// Setup Loopback Addresses Routing
909
-		{d.config.Hairpin, "setupLoopbackAddressesRouting", setupLoopbackAddressesRouting},
909
+		{!d.config.EnableProxy, "setupLoopbackAddressesRouting", setupLoopbackAddressesRouting},
910 910
 
911 911
 		// Setup DefaultGatewayIPv4
912 912
 		{config.DefaultGatewayIPv4 != nil, "setupGatewayIPv4", setupGatewayIPv4},
... ...
@@ -1185,7 +1177,7 @@ func (d *driver) CreateEndpoint(ctx context.Context, nid, eid string, ifInfo dri
1185 1185
 		return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err)
1186 1186
 	}
1187 1187
 
1188
-	if dconfig.Hairpin {
1188
+	if !dconfig.EnableProxy {
1189 1189
 		err = setHairpinMode(d.nlh, host, true)
1190 1190
 		if err != nil {
1191 1191
 			return err
... ...
@@ -5,15 +5,22 @@ import (
5 5
 	"errors"
6 6
 	"fmt"
7 7
 	"net"
8
+	"net/netip"
9
+	"os"
8 10
 	"slices"
9 11
 
10 12
 	"github.com/containerd/log"
11
-	"github.com/moby/moby/v2/daemon/internal/sliceutil"
13
+	"github.com/moby/moby/v2/daemon/libnetwork/drvregistry"
12 14
 	"github.com/moby/moby/v2/daemon/libnetwork/netutils"
15
+	"github.com/moby/moby/v2/daemon/libnetwork/portallocator"
16
+	"github.com/moby/moby/v2/daemon/libnetwork/portmapper"
13 17
 	"github.com/moby/moby/v2/daemon/libnetwork/portmapperapi"
14 18
 	"github.com/moby/moby/v2/daemon/libnetwork/types"
15 19
 )
16 20
 
21
+// Allow unit tests to supply a dummy StartProxy.
22
+var startProxy = portmapper.StartProxy
23
+
17 24
 // addPortMappings takes cfg, the configuration for port mappings, selects host
18 25
 // ports when ranges are given, binds host ports to check they're available and
19 26
 // reserve them, starts docker-proxy if required, and sets up iptables
... ...
@@ -72,19 +79,11 @@ func (n *bridgeNetwork) addPortMappings(
72 72
 			continue
73 73
 		}
74 74
 
75
-		pm, err := pms.Get(c.Mapper)
76
-		if err != nil {
77
-			return nil, err
78
-		}
79
-
80
-		newB, err := pm.MapPorts(ctx, toBind, n.firewallerNetwork)
75
+		newB, err := n.mapPorts(ctx, pms, toBind)
81 76
 		if err != nil {
82 77
 			return nil, err
83 78
 		}
84
-		bindings = append(bindings, sliceutil.Map(newB, func(b portmapperapi.PortBinding) portmapperapi.PortBinding {
85
-			b.Mapper = c.Mapper
86
-			return b
87
-		})...)
79
+		bindings = append(bindings, newB...)
88 80
 
89 81
 		// Reset toBind now the ports are bound.
90 82
 		toBind = toBind[:0]
... ...
@@ -93,6 +92,96 @@ func (n *bridgeNetwork) addPortMappings(
93 93
 	return bindings, nil
94 94
 }
95 95
 
96
+// mapPorts calls the port mapper used to map the ports in reqs, applies the firewall rules requested by that portmapper,
97
+// and starts userland proxies if needed. It returns an error if it fails on any of these steps, and rolls back any
98
+// changes it made. Caller must ensure that reqs is non-empty and all requests have the same Mapper set.
99
+func (n *bridgeNetwork) mapPorts(ctx context.Context, pms *drvregistry.PortMappers, reqs []portmapperapi.PortBindingReq) (_ []portmapperapi.PortBinding, retErr error) {
100
+	mapper := reqs[0].Mapper
101
+	pm, err := pms.Get(mapper)
102
+	if err != nil {
103
+		return nil, err
104
+	}
105
+
106
+	bindings, err := pm.MapPorts(ctx, reqs, n.firewallerNetwork)
107
+	if err != nil {
108
+		return nil, err
109
+	}
110
+	defer func() {
111
+		if retErr != nil {
112
+			if err := pm.UnmapPorts(ctx, bindings, n.firewallerNetwork); err != nil {
113
+				log.G(ctx).WithFields(log.Fields{
114
+					"bindings": bindings,
115
+					"error":    err,
116
+					"origErr":  retErr,
117
+				}).Warn("Failed to unmap port bindings after error")
118
+			}
119
+			return
120
+		}
121
+	}()
122
+
123
+	for i := range bindings {
124
+		// Make sure that Mapper is correctly set such that UnmapPorts call the right portmapper.
125
+		bindings[i].Mapper = mapper
126
+	}
127
+
128
+	fwPorts := collectFirewallPorts(bindings)
129
+	if err := n.firewallerNetwork.AddPorts(ctx, fwPorts); err != nil {
130
+		return nil, err
131
+	}
132
+	defer func() {
133
+		if retErr != nil {
134
+			if err := n.firewallerNetwork.DelPorts(ctx, fwPorts); err != nil {
135
+				log.G(ctx).WithFields(log.Fields{
136
+					"bindings": bindings,
137
+					"error":    err,
138
+					"origErr":  retErr,
139
+				}).Warn("Failed to remove firewall rules after error")
140
+			}
141
+		}
142
+	}()
143
+
144
+	// Start userland proxy processes.
145
+	defer func() {
146
+		if retErr != nil {
147
+			for _, pb := range bindings {
148
+				if pb.StopProxy == nil {
149
+					continue
150
+				}
151
+				if err := pb.StopProxy(); err != nil {
152
+					log.G(ctx).WithFields(log.Fields{
153
+						"binding": pb.PortBinding,
154
+						"error":   err,
155
+					}).Warnf("failed to stop userland proxy for port mapping")
156
+				}
157
+			}
158
+		}
159
+	}()
160
+	if n.driver.config.EnableProxy {
161
+		for i, pb := range bindings {
162
+			if pb.BoundSocket == nil || pb.RootlesskitUnsupported || pb.StopProxy != nil {
163
+				continue
164
+			}
165
+			if err := portallocator.DetachSocketFilter(bindings[i].BoundSocket); err != nil {
166
+				return nil, fmt.Errorf("failed to detach socket filter for port mapping %s: %w", bindings[i].PortBinding, err)
167
+			}
168
+			var err error
169
+			bindings[i].StopProxy, err = startProxy(pb.ChildPortBinding(), n.driver.config.ProxyPath, pb.BoundSocket)
170
+			if err != nil {
171
+				return nil, fmt.Errorf("failed to start userland proxy for port mapping %s: %w", pb.PortBinding, err)
172
+			}
173
+			if err := bindings[i].BoundSocket.Close(); err != nil {
174
+				log.G(ctx).WithFields(log.Fields{
175
+					"error":   err,
176
+					"mapping": pb.PortBinding,
177
+				}).Warnf("failed to close proxy socket")
178
+			}
179
+			bindings[i].BoundSocket = nil
180
+		}
181
+	}
182
+
183
+	return bindings, nil
184
+}
185
+
96 186
 // sortAndNormPBs transforms cfg into a list of portBindingReq, with all fields
97 187
 // normalized:
98 188
 //
... ...
@@ -123,7 +212,6 @@ func (n *bridgeNetwork) sortAndNormPBs(
123 123
 		containerIPv6 = ep.addrv6.IP
124 124
 	}
125 125
 
126
-	hairpin := n.hairpin()
127 126
 	disableNAT4, disableNAT6 := n.getNATDisabled()
128 127
 
129 128
 	add4 := !ep.portBindingState.ipv4 && pbmReq.ipv4 || (disableNAT4 && !ep.portBindingState.routed && pbmReq.routed)
... ...
@@ -146,7 +234,7 @@ func (n *bridgeNetwork) sortAndNormPBs(
146 146
 		// This change was added to keep backward compatibility
147 147
 		containerIP := containerIPv6
148 148
 		if containerIPv6 == nil && pbmReq.ipv4 && add6 {
149
-			if hairpin {
149
+			if !n.driver.config.EnableProxy {
150 150
 				// There's no way to map from host-IPv6 to container-IPv4 with the userland proxy
151 151
 				// disabled.
152 152
 				// If that is required, don't treat it as an error because, as networks are
... ...
@@ -189,21 +277,6 @@ func needSamePort(a, b portmapperapi.PortBindingReq) bool {
189 189
 		a.HostPortEnd == b.HostPortEnd
190 190
 }
191 191
 
192
-// mergeChildHostIPs take a slice of PortBinding and returns a slice of
193
-// types.PortBinding, where the HostIP in each of the results has the
194
-// value of ChildHostIP from the input (if present).
195
-func mergeChildHostIPs(pbs []portmapperapi.PortBinding) []types.PortBinding {
196
-	res := make([]types.PortBinding, 0, len(pbs))
197
-	for _, b := range pbs {
198
-		pb := b.PortBinding
199
-		if b.ChildHostIP != nil {
200
-			pb.HostIP = b.ChildHostIP
201
-		}
202
-		res = append(res, pb)
203
-	}
204
-	return res
205
-}
206
-
207 192
 // configurePortBindingIPv4 returns a new port binding with the HostIP field
208 193
 // populated and true, if a binding is required. Else, false and an empty
209 194
 // binding.
... ...
@@ -343,6 +416,15 @@ func (n *bridgeNetwork) unmapPBs(ctx context.Context, bindings []portmapperapi.P
343 343
 		if err := pm.UnmapPorts(ctx, []portmapperapi.PortBinding{b}, n.firewallerNetwork); err != nil {
344 344
 			errs = append(errs, fmt.Errorf("unmapping port binding %s: %w", b.PortBinding, err))
345 345
 		}
346
+		if b.StopProxy != nil {
347
+			if err := b.StopProxy(); err != nil && !errors.Is(err, os.ErrProcessDone) {
348
+				errs = append(errs, fmt.Errorf("unmapping port binding %s: failed to stop userland proxy: %w", b.PortBinding, err))
349
+			}
350
+		}
351
+	}
352
+
353
+	if err := n.firewallerNetwork.DelPorts(ctx, collectFirewallPorts(bindings)); err != nil {
354
+		return err
346 355
 	}
347 356
 
348 357
 	return errors.Join(errs...)
... ...
@@ -365,7 +447,55 @@ func (n *bridgeNetwork) reapplyPerPortIptables() {
365 365
 		}
366 366
 	}
367 367
 
368
-	if err := n.firewallerNetwork.AddPorts(context.Background(), mergeChildHostIPs(allPBs)); err != nil {
368
+	if err := n.firewallerNetwork.AddPorts(context.Background(), collectFirewallPorts(allPBs)); err != nil {
369 369
 		log.G(context.TODO()).Warnf("Failed to reconfigure NAT: %s", err)
370 370
 	}
371 371
 }
372
+
373
+// collectFirewallPorts collects all the types.PortBinding needed to
374
+// reconfigure the host firewall for a given list of port bindings. If one of
375
+// the pbs is NATed, but has an invalid NAT field (i.e. multicast address, or a
376
+// port 0), an error is returned.
377
+func collectFirewallPorts(pbs []portmapperapi.PortBinding) []types.PortBinding {
378
+	var fwPBs []types.PortBinding
379
+	for _, pb := range pbs {
380
+		if pb.NAT.IsValid() {
381
+			if pb.NAT.Addr().IsMulticast() || pb.NAT.Port() == 0 {
382
+				log.G(context.Background()).WithFields(log.Fields{"pb": pb}).Error("invalid NAT address")
383
+				continue
384
+			}
385
+			fwPBs = append(fwPBs, toNATBinding(pb))
386
+		} else if pb.Forwarding {
387
+			fwPBs = append(fwPBs, toFwdBinding(pb))
388
+		}
389
+	}
390
+	return fwPBs
391
+}
392
+
393
+// toNATBinding converts a portmapperapi.PortBinding to a types.PortBinding
394
+// that can be passed to firewaller.Network for setting up a NAT rule.
395
+func toNATBinding(pb portmapperapi.PortBinding) types.PortBinding {
396
+	return types.PortBinding{
397
+		IP:          pb.IP,
398
+		Port:        pb.Port,
399
+		Proto:       pb.Proto,
400
+		HostIP:      pb.NAT.Addr().AsSlice(),
401
+		HostPort:    pb.NAT.Port(),
402
+		HostPortEnd: pb.NAT.Port(),
403
+	}
404
+}
405
+
406
+// toFwdBinding converts a portmapperapi.PortBinding to a types.PortBinding
407
+// that can be passed to firewaller.Network for setting up forwarding.
408
+func toFwdBinding(pb portmapperapi.PortBinding) types.PortBinding {
409
+	unspecAddr := netip.IPv4Unspecified()
410
+	if pb.IP.To4() == nil {
411
+		unspecAddr = netip.IPv6Unspecified()
412
+	}
413
+	return types.PortBinding{
414
+		IP:     pb.IP,
415
+		Port:   pb.Port,
416
+		Proto:  pb.Proto,
417
+		HostIP: unspecAddr.AsSlice(),
418
+	}
419
+}
... ...
@@ -563,7 +563,6 @@ func TestAddPortMappings(t *testing.T) {
563 563
 			cfg: []portmapperapi.PortBindingReq{
564 564
 				{PortBinding: types.PortBinding{Proto: types.TCP, Port: 22, HostIP: net.IPv6loopback}},
565 565
 			},
566
-			hairpin: true,
567 566
 			expLogs: []string{"Cannot map from IPv6 to an IPv4-only container because the userland proxy is disabled"},
568 567
 		},
569 568
 		{
... ...
@@ -573,7 +572,6 @@ func TestAddPortMappings(t *testing.T) {
573 573
 			cfg: []portmapperapi.PortBindingReq{
574 574
 				{PortBinding: types.PortBinding{Proto: types.TCP, Port: 22}},
575 575
 			},
576
-			hairpin: true,
577 576
 			expLogs: []string{"Cannot map from default host binding address to an IPv4-only container because the userland proxy is disabled"},
578 577
 		},
579 578
 		{
... ...
@@ -718,7 +716,7 @@ func TestAddPortMappings(t *testing.T) {
718 718
 
719 719
 			// Mock the startProxy function used by the code under test.
720 720
 			proxies := map[proxyCall]bool{} // proxy -> is not stopped
721
-			startProxy := func(pb types.PortBinding, listenSock *os.File) (stop func() error, retErr error) {
721
+			startProxy = func(pb types.PortBinding, _ string, listenSock *os.File) (stop func() error, retErr error) {
722 722
 				if tc.busyPortIPv4 > 0 && tc.busyPortIPv4 == int(pb.HostPort) && pb.HostIP.To4() != nil {
723 723
 					return nil, errors.New("busy port")
724 724
 				}
... ...
@@ -768,9 +766,7 @@ func TestAddPortMappings(t *testing.T) {
768 768
 
769 769
 			pms := &drvregistry.PortMappers{}
770 770
 			err := nat.Register(pms, nat.Config{
771
-				RlkClient:   pdc,
772
-				EnableProxy: tc.enableProxy,
773
-				StartProxy:  startProxy,
771
+				RlkClient: pdc,
774 772
 			})
775 773
 			assert.NilError(t, err)
776 774
 			err = routed.Register(pms)
... ...
@@ -791,7 +787,7 @@ func TestAddPortMappings(t *testing.T) {
791 791
 				netlabel.GenericData: &configuration{
792 792
 					EnableIPTables:  true,
793 793
 					EnableIP6Tables: true,
794
-					Hairpin:         tc.hairpin,
794
+					EnableProxy:     tc.enableProxy,
795 795
 				},
796 796
 			}
797 797
 			err = n.driver.configure(genericOption)
... ...
@@ -892,8 +888,8 @@ func TestAddPortMappings(t *testing.T) {
892 892
 			}
893 893
 
894 894
 			// Check a docker-proxy was started and stopped for each expected port binding.
895
+			expProxies := map[proxyCall]bool{}
895 896
 			if tc.enableProxy {
896
-				expProxies := map[proxyCall]bool{}
897 897
 				for _, expPB := range tc.expPBs {
898 898
 					hip := expChildIP(expPB.HostIP)
899 899
 					is4 := hip.To4() != nil
... ...
@@ -905,8 +901,8 @@ func TestAddPortMappings(t *testing.T) {
905 905
 						expPB.IP, int(expPB.Port))
906 906
 					expProxies[p] = tc.expReleaseErr != ""
907 907
 				}
908
-				assert.Check(t, is.DeepEqual(expProxies, proxies))
909 908
 			}
909
+			assert.Check(t, is.DeepEqual(expProxies, proxies))
910 910
 
911 911
 			// Check the port driver has seen the expected port mappings and no others,
912 912
 			// and that they have all been closed.
... ...
@@ -3,7 +3,6 @@ package libnetwork
3 3
 import (
4 4
 	"context"
5 5
 	"fmt"
6
-	"os"
7 6
 
8 7
 	"github.com/moby/moby/v2/daemon/libnetwork/config"
9 8
 	"github.com/moby/moby/v2/daemon/libnetwork/datastore"
... ...
@@ -16,10 +15,8 @@ import (
16 16
 	"github.com/moby/moby/v2/daemon/libnetwork/drivers/overlay"
17 17
 	"github.com/moby/moby/v2/daemon/libnetwork/drvregistry"
18 18
 	"github.com/moby/moby/v2/daemon/libnetwork/internal/rlkclient"
19
-	"github.com/moby/moby/v2/daemon/libnetwork/portmapper"
20 19
 	"github.com/moby/moby/v2/daemon/libnetwork/portmappers/nat"
21 20
 	"github.com/moby/moby/v2/daemon/libnetwork/portmappers/routed"
22
-	"github.com/moby/moby/v2/daemon/libnetwork/types"
23 21
 )
24 22
 
25 23
 func registerNetworkDrivers(r driverapi.Registerer, store *datastore.Store, pms *drvregistry.PortMappers, driverConfig func(string) map[string]any) error {
... ...
@@ -60,13 +57,7 @@ func registerPortMappers(ctx context.Context, r *drvregistry.PortMappers, cfg *c
60 60
 		}
61 61
 	}
62 62
 
63
-	if err := nat.Register(r, nat.Config{
64
-		RlkClient: pdc,
65
-		StartProxy: func(pb types.PortBinding, file *os.File) (func() error, error) {
66
-			return portmapper.StartProxy(pb, cfg.UserlandProxyPath, file)
67
-		},
68
-		EnableProxy: cfg.EnableUserlandProxy && cfg.UserlandProxyPath != "",
69
-	}); err != nil {
63
+	if err := nat.Register(r, nat.Config{RlkClient: pdc}); err != nil {
70 64
 		return fmt.Errorf("registering nat portmapper: %w", err)
71 65
 	}
72 66
 
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	"fmt"
7 7
 	"net"
8 8
 	"net/netip"
9
-	"os"
10 9
 	"strconv"
11 10
 
12 11
 	"github.com/containerd/log"
... ...
@@ -23,8 +22,6 @@ type PortDriverClient interface {
23 23
 	AddPort(ctx context.Context, proto string, hostIP, childIP netip.Addr, hostPort int) (func() error, error)
24 24
 }
25 25
 
26
-type proxyStarter func(types.PortBinding, *os.File) (func() error, error)
27
-
28 26
 // Register the "nat" port-mapper with libnetwork.
29 27
 func Register(r portmapperapi.Registerer, cfg Config) error {
30 28
 	return r.Register(driverName, NewPortMapper(cfg))
... ...
@@ -32,24 +29,18 @@ func Register(r portmapperapi.Registerer, cfg Config) error {
32 32
 
33 33
 type PortMapper struct {
34 34
 	// pdc is used to interact with rootlesskit port driver.
35
-	pdc         PortDriverClient
36
-	startProxy  proxyStarter
37
-	enableProxy bool
35
+	pdc PortDriverClient
38 36
 }
39 37
 
40 38
 type Config struct {
41 39
 	// RlkClient is called by MapPorts to determine the ChildHostIP and ask
42 40
 	// rootlesskit to map ports in its netns.
43
-	RlkClient   PortDriverClient
44
-	StartProxy  proxyStarter
45
-	EnableProxy bool
41
+	RlkClient PortDriverClient
46 42
 }
47 43
 
48 44
 func NewPortMapper(cfg Config) PortMapper {
49 45
 	return PortMapper{
50
-		pdc:         cfg.RlkClient,
51
-		startProxy:  cfg.StartProxy,
52
-		enableProxy: cfg.EnableProxy,
46
+		pdc: cfg.RlkClient,
53 47
 	}
54 48
 }
55 49
 
... ...
@@ -102,42 +93,16 @@ func (pm PortMapper) MapPorts(ctx context.Context, cfg []portmapperapi.PortBindi
102 102
 		}
103 103
 		pb.PortBinding.HostPort = uint16(allocatedPort)
104 104
 		pb.PortBinding.HostPortEnd = pb.HostPort
105
+
106
+		childHIP, _ := netip.AddrFromSlice(cfg[i].ChildHostIP)
107
+		pb.NAT = netip.AddrPortFrom(childHIP, pb.PortBinding.HostPort)
108
+
105 109
 		bindings = append(bindings, pb)
106 110
 	}
107 111
 
108 112
 	if err := configPortDriver(ctx, bindings, pm.pdc); err != nil {
109 113
 		return nil, err
110 114
 	}
111
-	if err := fwn.AddPorts(ctx, mergeChildHostIPs(bindings)); err != nil {
112
-		return nil, err
113
-	}
114
-
115
-	// Start userland proxy processes.
116
-	if pm.enableProxy {
117
-		for i := range bindings {
118
-			if bindings[i].BoundSocket == nil || bindings[i].RootlesskitUnsupported || bindings[i].StopProxy != nil {
119
-				continue
120
-			}
121
-			if err := portallocator.DetachSocketFilter(bindings[i].BoundSocket); err != nil {
122
-				return nil, fmt.Errorf("failed to detach socket filter for port mapping %s: %w", bindings[i].PortBinding, err)
123
-			}
124
-			var err error
125
-			bindings[i].StopProxy, err = pm.startProxy(
126
-				bindings[i].ChildPortBinding(), bindings[i].BoundSocket,
127
-			)
128
-			if err != nil {
129
-				return nil, fmt.Errorf("failed to start userland proxy for port mapping %s: %w",
130
-					bindings[i].PortBinding, err)
131
-			}
132
-			if err := bindings[i].BoundSocket.Close(); err != nil {
133
-				log.G(ctx).WithFields(log.Fields{
134
-					"error":   err,
135
-					"mapping": bindings[i].PortBinding,
136
-				}).Warnf("failed to close proxy socket")
137
-			}
138
-			bindings[i].BoundSocket = nil
139
-		}
140
-	}
141 115
 
142 116
 	return bindings, nil
143 117
 }
... ...
@@ -155,14 +120,6 @@ func (pm PortMapper) UnmapPorts(ctx context.Context, pbs []portmapperapi.PortBin
155 155
 				errs = append(errs, err)
156 156
 			}
157 157
 		}
158
-		if pb.StopProxy != nil {
159
-			if err := pb.StopProxy(); err != nil && !errors.Is(err, os.ErrProcessDone) {
160
-				errs = append(errs, fmt.Errorf("failed to stop userland proxy: %w", err))
161
-			}
162
-		}
163
-	}
164
-	if err := fwn.DelPorts(ctx, mergeChildHostIPs(pbs)); err != nil {
165
-		errs = append(errs, err)
166 158
 	}
167 159
 	for _, pb := range pbs {
168 160
 		portallocator.Get().ReleasePort(pb.ChildHostIP, pb.Proto.String(), int(pb.HostPort))
... ...
@@ -180,21 +137,6 @@ func setChildHostIP(pdc PortDriverClient, req portmapperapi.PortBindingReq) port
180 180
 	return req
181 181
 }
182 182
 
183
-// mergeChildHostIPs take a slice of PortBinding and returns a slice of
184
-// types.PortBinding, where the HostIP in each of the results has the
185
-// value of ChildHostIP from the input (if present).
186
-func mergeChildHostIPs(pbs []portmapperapi.PortBinding) []types.PortBinding {
187
-	res := make([]types.PortBinding, 0, len(pbs))
188
-	for _, b := range pbs {
189
-		pb := b.PortBinding
190
-		if b.ChildHostIP != nil {
191
-			pb.HostIP = b.ChildHostIP
192
-		}
193
-		res = append(res, pb)
194
-	}
195
-	return res
196
-}
197
-
198 183
 // configPortDriver passes the port binding's details to rootlesskit, and updates the
199 184
 // port binding with callbacks to remove the rootlesskit config (or marks the binding as
200 185
 // unsupported by rootlesskit).