@@ -6,11 +6,11 @@ import (
66 "errors"
77 "fmt"
88 "net"
9+ "net/netip"
910 "os"
1011 "path"
1112 "path/filepath"
1213 "reflect"
13- "strconv"
1414 "strings"
1515 "time"
1616
@@ -52,7 +52,7 @@ type containerOptions struct {
5252 deviceWriteBps opts.ThrottledeviceOpt
5353 links opts.ListOpts
5454 aliases opts.ListOpts
55- linkLocalIPs opts.ListOpts
55+ linkLocalIPs opts.ListOpts // TODO(thaJeztah): we need a flag-type to handle []netip.Addr directly
5656 deviceReadIOps opts.ThrottledeviceOpt
5757 deviceWriteIOps opts.ThrottledeviceOpt
5858 env opts.ListOpts
@@ -64,7 +64,7 @@ type containerOptions struct {
6464 sysctls * opts.MapOpts
6565 publish opts.ListOpts
6666 expose opts.ListOpts
67- dns opts.ListOpts
67+ dns opts.ListOpts // TODO(thaJeztah): we need a flag-type to handle []netip.Addr directly
6868 dnsSearch opts.ListOpts
6969 dnsOptions opts.ListOpts
7070 extraHosts opts.ListOpts
@@ -112,8 +112,8 @@ type containerOptions struct {
112112 swappiness int64
113113 netMode opts.NetworkOpt
114114 macAddress string
115- ipv4Address string
116- ipv6Address string
115+ ipv4Address net. IP // TODO(thaJeztah): we need a flag-type to handle netip.Addr directly
116+ ipv6Address net. IP // TODO(thaJeztah): we need a flag-type to handle netip.Addr directly
117117 ipcMode string
118118 pidsLimit int64
119119 restartPolicy string
@@ -239,8 +239,8 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
239239 flags .MarkHidden ("dns-opt" )
240240 flags .Var (& copts .dnsSearch , "dns-search" , "Set custom DNS search domains" )
241241 flags .Var (& copts .expose , "expose" , "Expose a port or a range of ports" )
242- flags .StringVar (& copts .ipv4Address , "ip" , "" , "IPv4 address (e.g., 172.30.100.104)" )
243- flags .StringVar (& copts .ipv6Address , "ip6" , "" , "IPv6 address (e.g., 2001:db8::33)" )
242+ flags .IPVar (& copts .ipv4Address , "ip" , nil , "IPv4 address (e.g., 172.30.100.104)" )
243+ flags .IPVar (& copts .ipv6Address , "ip6" , nil , "IPv6 address (e.g., 2001:db8::33)" )
244244 flags .Var (& copts .links , "link" , "Add link to another container" )
245245 flags .Var (& copts .linkLocalIPs , "link-local-ip" , "Container IPv4/IPv6 link-local addresses" )
246246 flags .StringVar (& copts .macAddress , "mac-address" , "" , "Container MAC address (e.g., 92:d0:c6:0a:29:33)" )
@@ -426,38 +426,60 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
426426 entrypoint = []string {"" }
427427 }
428428
429+ // TODO(thaJeztah): remove uses of go-connections/nat here.
429430 convertedOpts , err := convertToStandardNotation (copts .publish .GetSlice ())
430431 if err != nil {
431432 return nil , err
432433 }
433434
434- ports , portBindings , err := nat .ParsePortSpecs (convertedOpts )
435+ ports , natPortBindings , err := nat .ParsePortSpecs (convertedOpts )
435436 if err != nil {
436437 return nil , err
437438 }
439+ portBindings := network.PortMap {}
440+ for port , bindings := range natPortBindings {
441+ p , err := network .ParsePort (string (port ))
442+ if err != nil {
443+ return nil , err
444+ }
445+ portBindings [p ] = []network.PortBinding {}
446+ for _ , b := range bindings {
447+ var hostIP netip.Addr
448+ if b .HostIP != "" {
449+ hostIP , err = netip .ParseAddr (b .HostIP )
450+ if err != nil {
451+ return nil , err
452+ }
453+ }
454+ portBindings [p ] = append (portBindings [p ], network.PortBinding {
455+ HostIP : hostIP ,
456+ HostPort : b .HostPort ,
457+ })
458+ }
459+ }
460+
461+ // Add published ports as exposed ports.
462+ exposedPorts := network.PortSet {}
463+ for port := range ports {
464+ p , err := network .ParsePort (string (port ))
465+ if err != nil {
466+ return nil , err
467+ }
468+ exposedPorts [p ] = struct {}{}
469+ }
438470
439471 // Merge in exposed ports to the map of published ports
440472 for _ , e := range copts .expose .GetSlice () {
441- if strings .Contains (e , ":" ) {
442- return nil , fmt .Errorf ("invalid port format for --expose: %s" , e )
443- }
444473 // support two formats for expose, original format <portnum>/[<proto>]
445474 // or <startport-endport>/[<proto>]
446- proto , port := nat .SplitProtoPort (e )
447- // parse the start and end port and create a sequence of ports to expose
448- // if expose a port, the start and end port are the same
449- start , end , err := nat .ParsePortRange (port )
475+ pr , err := network .ParsePortRange (e )
450476 if err != nil {
451- return nil , fmt .Errorf ("invalid range format for --expose: %s, error: %w" , e , err )
477+ return nil , fmt .Errorf ("invalid range format for --expose: %w" , err )
452478 }
453- for i := start ; i <= end ; i ++ {
454- p , err := nat .NewPort (proto , strconv .FormatUint (i , 10 ))
455- if err != nil {
456- return nil , err
457- }
458- if _ , exists := ports [p ]; ! exists {
459- ports [p ] = struct {}{}
460- }
479+ // parse the start and end port and create a sequence of ports to expose
480+ // if expose a port, the start and end port are the same
481+ for p := range pr .All () {
482+ exposedPorts [p ] = struct {}{}
461483 }
462484 }
463485
@@ -626,7 +648,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
626648 config := & container.Config {
627649 Hostname : copts .hostname ,
628650 Domainname : copts .domainname ,
629- ExposedPorts : ports ,
651+ ExposedPorts : exposedPorts ,
630652 User : copts .user ,
631653 Tty : copts .tty ,
632654 OpenStdin : copts .stdin ,
@@ -662,7 +684,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
662684 // but pre created containers can still have those nil values.
663685 // See https://github.com/docker/docker/pull/17779
664686 // for a more detailed explanation on why we don't want that.
665- DNS : copts .dns .GetAllOrEmpty (),
687+ DNS : toNetipAddrSlice ( copts .dns .GetAllOrEmpty () ),
666688 DNSSearch : copts .dnsSearch .GetAllOrEmpty (),
667689 DNSOptions : copts .dnsOptions .GetAllOrEmpty (),
668690 ExtraHosts : copts .extraHosts .GetSlice (),
@@ -805,10 +827,10 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
805827 if len (n .Links ) > 0 && copts .links .Len () > 0 {
806828 return invalidParameter (errors .New ("conflicting options: cannot specify both --link and per-network links" ))
807829 }
808- if n .IPv4Address != "" && copts .ipv4Address != "" {
830+ if n .IPv4Address . IsValid () && copts .ipv4Address != nil {
809831 return invalidParameter (errors .New ("conflicting options: cannot specify both --ip and per-network IPv4 address" ))
810832 }
811- if n .IPv6Address != "" && copts .ipv6Address != "" {
833+ if n .IPv6Address . IsValid () && copts .ipv6Address != nil {
812834 return invalidParameter (errors .New ("conflicting options: cannot specify both --ip6 and per-network IPv6 address" ))
813835 }
814836 if n .MacAddress != "" && copts .macAddress != "" {
@@ -827,18 +849,21 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption
827849 n .Links = make ([]string , copts .links .Len ())
828850 copy (n .Links , copts .links .GetSlice ())
829851 }
830- if copts .ipv4Address != "" {
831- n .IPv4Address = copts .ipv4Address
852+ if copts .ipv4Address != nil {
853+ if ipv4 , ok := netip .AddrFromSlice (copts .ipv4Address .To4 ()); ok {
854+ n .IPv4Address = ipv4
855+ }
832856 }
833- if copts .ipv6Address != "" {
834- n .IPv6Address = copts .ipv6Address
857+ if copts .ipv6Address != nil {
858+ if ipv6 , ok := netip .AddrFromSlice (copts .ipv6Address .To16 ()); ok {
859+ n .IPv6Address = ipv6
860+ }
835861 }
836862 if copts .macAddress != "" {
837863 n .MacAddress = copts .macAddress
838864 }
839865 if copts .linkLocalIPs .Len () > 0 {
840- n .LinkLocalIPs = make ([]string , copts .linkLocalIPs .Len ())
841- copy (n .LinkLocalIPs , copts .linkLocalIPs .GetSlice ())
866+ n .LinkLocalIPs = toNetipAddrSlice (copts .linkLocalIPs .GetSlice ())
842867 }
843868 return nil
844869}
@@ -867,7 +892,7 @@ func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*network.Endpoint
867892 if len (ep .Links ) > 0 {
868893 epConfig .Links = ep .Links
869894 }
870- if ep .IPv4Address != "" || ep .IPv6Address != "" || len (ep .LinkLocalIPs ) > 0 {
895+ if ep .IPv4Address . IsValid () || ep .IPv6Address . IsValid () || len (ep .LinkLocalIPs ) > 0 {
871896 epConfig .IPAMConfig = & network.EndpointIPAMConfig {
872897 IPv4Address : ep .IPv4Address ,
873898 IPv6Address : ep .IPv6Address ,
@@ -1131,3 +1156,15 @@ func validateAttach(val string) (string, error) {
11311156 }
11321157 return val , errors .New ("valid streams are STDIN, STDOUT and STDERR" )
11331158}
1159+
1160+ func toNetipAddrSlice (ips []string ) []netip.Addr {
1161+ netips := make ([]netip.Addr , 0 , len (ips ))
1162+ for _ , ip := range ips {
1163+ addr , err := netip .ParseAddr (ip )
1164+ if err != nil {
1165+ continue
1166+ }
1167+ netips = append (netips , addr )
1168+ }
1169+ return netips
1170+ }
0 commit comments