@@ -23,12 +23,28 @@ import (
2323
2424type portBinding struct {
2525 types.PortBinding
26+ // childHostIP is the host IP address, as seen from the daemon. This
27+ // is normally the same as PortBinding.HostIP but, in rootless mode, it
28+ // will be an address in the rootless network namespace. RootlessKit
29+ // binds the port on the real (parent) host address and maps it to the
30+ // same port number on the address dockerd sees in the child namespace.
31+ // So, for example, docker-proxy and DNAT rules need to use the child
32+ // namespace's host address. (PortBinding.HostIP isn't replaced by the
33+ // child address, because it's stored as user-config and the child
34+ // address may change if RootlessKit is configured differently.)
35+ childHostIP net.IP
36+ // portDriverRemove is a function that will inform the RootlessKit
37+ // port driver about removal of a port binding, or nil.
38+ portDriverRemove func () error
39+ // stopProxy is a function to stop the userland proxy for this binding,
40+ // if a proxy has been started - else nil.
2641 stopProxy func () error
2742}
2843
2944type portBindingReq struct {
3045 types.PortBinding
31- disableNAT bool
46+ childHostIP net.IP
47+ disableNAT bool
3248}
3349
3450// addPortMappings takes cfg, the configuration for port mappings, selects host
@@ -79,6 +95,7 @@ func (n *bridgeNetwork) addPortMappings(
7995 sortAndNormPBs (sortedCfg )
8096
8197 proxyPath := n .userlandProxyPath ()
98+ pdc := n .getPortDriverClient ()
8299
83100 // toBind accumulates port bindings that should be allocated the same host port
84101 // (if required by NAT config). If the host address is unspecified, and defHostIP
@@ -91,7 +108,7 @@ func (n *bridgeNetwork) addPortMappings(
91108 // bindings to collect, they're applied and toBind is reset.
92109 var toBind []portBindingReq
93110 for i , c := range sortedCfg {
94- if bindingIPv4 , ok := configurePortBindingIPv4 (disableNAT4 , c , containerIPv4 , defHostIP ); ok {
111+ if bindingIPv4 , ok := configurePortBindingIPv4 (pdc , disableNAT4 , c , containerIPv4 , defHostIP ); ok {
95112 toBind = append (toBind , bindingIPv4 )
96113 }
97114
@@ -107,7 +124,7 @@ func (n *bridgeNetwork) addPortMappings(
107124 if proxyPath != "" && (containerIPv6 == nil ) {
108125 containerIP = containerIPv4
109126 }
110- if bindingIPv6 , ok := configurePortBindingIPv6 (disableNAT6 , c , containerIP , defHostIP ); ok {
127+ if bindingIPv6 , ok := configurePortBindingIPv6 (pdc , disableNAT6 , c , containerIP , defHostIP ); ok {
111128 toBind = append (toBind , bindingIPv6 )
112129 }
113130
@@ -129,8 +146,24 @@ func (n *bridgeNetwork) addPortMappings(
129146 toBind = toBind [:0 ]
130147 }
131148
132- for _ , b := range bindings {
133- if err := n .setPerPortIptables (b , true ); err != nil {
149+ for i := range bindings {
150+ if pdc != nil && bindings [i ].HostPort != 0 {
151+ var err error
152+ b := & bindings [i ]
153+ hip , ok := netip .AddrFromSlice (b .HostIP )
154+ if ! ok {
155+ return nil , fmt .Errorf ("invalid host IP address in %s" , b )
156+ }
157+ chip , ok := netip .AddrFromSlice (b .childHostIP )
158+ if ! ok {
159+ return nil , fmt .Errorf ("invalid child host IP address %s in %s" , b .childHostIP , b )
160+ }
161+ b .portDriverRemove , err = pdc .AddPort (context .TODO (), b .Proto .String (), hip , chip , int (b .HostPort ))
162+ if err != nil {
163+ return nil , err
164+ }
165+ }
166+ if err := n .setPerPortIptables (bindings [i ], true ); err != nil {
134167 return nil , err
135168 }
136169 }
@@ -263,7 +296,7 @@ func needSamePort(a, b types.PortBinding) bool {
263296
264297// configurePortBindingIPv4 returns a new port binding with the HostIP field populated
265298// if a binding is required, else nil.
266- func configurePortBindingIPv4 (disableNAT bool , bnd types.PortBinding , containerIPv4 , defHostIP net.IP ) (portBindingReq , bool ) {
299+ func configurePortBindingIPv4 (pdc portDriverClient , disableNAT bool , bnd types.PortBinding , containerIPv4 , defHostIP net.IP ) (portBindingReq , bool ) {
267300 if len (containerIPv4 ) == 0 {
268301 return portBindingReq {}, false
269302 }
@@ -282,15 +315,15 @@ func configurePortBindingIPv4(disableNAT bool, bnd types.PortBinding, containerI
282315 // Unmap the addresses if they're IPv4-mapped IPv6.
283316 bnd .HostIP = bnd .HostIP .To4 ()
284317 bnd .IP = containerIPv4 .To4 ()
285- return portBindingReq {
318+ return setChildHostIP ( pdc , portBindingReq {
286319 PortBinding : bnd ,
287320 disableNAT : disableNAT ,
288- }, true
321+ }) , true
289322}
290323
291324// configurePortBindingIPv6 returns a new port binding with the HostIP field populated
292325// if a binding is required, else nil.
293- func configurePortBindingIPv6 (disableNAT bool , bnd types.PortBinding , containerIP , defHostIP net.IP ) (portBindingReq , bool ) {
326+ func configurePortBindingIPv6 (pdc portDriverClient , disableNAT bool , bnd types.PortBinding , containerIP , defHostIP net.IP ) (portBindingReq , bool ) {
294327 if containerIP == nil {
295328 return portBindingReq {}, false
296329 }
@@ -317,10 +350,20 @@ func configurePortBindingIPv6(disableNAT bool, bnd types.PortBinding, containerI
317350 }
318351 }
319352 bnd .IP = containerIP
320- return portBindingReq {
353+ return setChildHostIP ( pdc , portBindingReq {
321354 PortBinding : bnd ,
322355 disableNAT : disableNAT ,
323- }, true
356+ }), true
357+ }
358+
359+ func setChildHostIP (pdc portDriverClient , req portBindingReq ) portBindingReq {
360+ if pdc == nil {
361+ req .childHostIP = req .HostIP
362+ return req
363+ }
364+ hip , _ := netip .AddrFromSlice (req .HostIP )
365+ req .childHostIP = pdc .ChildHostIP (hip ).AsSlice ()
366+ return req
324367}
325368
326369// bindHostPorts allocates ports and starts docker-proxy for the given cfg. The
@@ -410,7 +453,7 @@ func attemptBindHostPorts(
410453 if c .disableNAT {
411454 pb .HostPort = 0
412455 } else {
413- pb .stopProxy , err = startProxy (c .Proto .String (), c .HostIP , port , c .IP , int (c .Port ), proxyPath )
456+ pb .stopProxy , err = startProxy (c .Proto .String (), c .childHostIP , port , c .IP , int (c .Port ), proxyPath )
414457 if err != nil {
415458 return nil , fmt .Errorf ("failed to bind port %s:%d/%s: %w" , c .HostIP , port , c .Proto , err )
416459 }
@@ -424,6 +467,7 @@ func attemptBindHostPorts(
424467 pb .HostPort = uint16 (port )
425468 }
426469 pb .HostPortEnd = pb .HostPort
470+ pb .childHostIP = c .childHostIP
427471 res = append (res , pb )
428472 }
429473 return res , nil
@@ -442,7 +486,10 @@ func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
442486func (n * bridgeNetwork ) releasePortBindings (pbs []portBinding ) error {
443487 var errs []error
444488 for _ , pb := range pbs {
445- var errP error
489+ var errPD , errP error
490+ if pb .portDriverRemove != nil {
491+ errPD = pb .portDriverRemove ()
492+ }
446493 if pb .stopProxy != nil {
447494 errP = pb .stopProxy ()
448495 if errP != nil {
@@ -456,7 +503,7 @@ func (n *bridgeNetwork) releasePortBindings(pbs []portBinding) error {
456503 if pb .HostPort > 0 {
457504 portallocator .Get ().ReleasePort (pb .HostIP , pb .Proto .String (), int (pb .HostPort ))
458505 }
459- errs = append (errs , errP , errN )
506+ errs = append (errs , errPD , errP , errN )
460507 }
461508 return errors .Join (errs ... )
462509}
0 commit comments