@@ -503,7 +503,10 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li
503503 return runconfig .ErrConflictNoNetwork
504504 }
505505 }
506- container .NetworkSettings .Networks [n .Name ()] = new (networktypes.EndpointSettings )
506+
507+ if _ , ok := container .NetworkSettings .Networks [n .Name ()]; ! ok {
508+ container .NetworkSettings .Networks [n .Name ()] = new (networktypes.EndpointSettings )
509+ }
507510
508511 return nil
509512}
@@ -562,7 +565,12 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
562565}
563566
564567// updateContainerNetworkSettings update the network settings
565- func (daemon * Daemon ) updateContainerNetworkSettings (container * container.Container ) error {
568+ func (daemon * Daemon ) updateContainerNetworkSettings (container * container.Container , endpointsConfig map [string ]* networktypes.EndpointSettings ) error {
569+ var (
570+ n libnetwork.Network
571+ err error
572+ )
573+
566574 mode := container .HostConfig .NetworkMode
567575 if container .Config .NetworkDisabled || mode .IsContainer () {
568576 return nil
@@ -573,14 +581,35 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
573581 networkName = daemon .netController .Config ().Daemon .DefaultNetwork
574582 }
575583 if mode .IsUserDefined () {
576- n , err : = daemon .FindNetwork (networkName )
584+ n , err = daemon .FindNetwork (networkName )
577585 if err != nil {
578586 return err
579587 }
580588 networkName = n .Name ()
581589 }
582- container .NetworkSettings .Networks = make (map [string ]* networktypes.EndpointSettings )
583- container .NetworkSettings .Networks [networkName ] = new (networktypes.EndpointSettings )
590+ if container .NetworkSettings == nil {
591+ container .NetworkSettings = & network.Settings {}
592+ }
593+ if endpointsConfig != nil {
594+ container .NetworkSettings .Networks = endpointsConfig
595+ }
596+ if container .NetworkSettings .Networks == nil {
597+ container .NetworkSettings .Networks = make (map [string ]* networktypes.EndpointSettings )
598+ container .NetworkSettings .Networks [networkName ] = new (networktypes.EndpointSettings )
599+ }
600+ if ! mode .IsUserDefined () {
601+ return nil
602+ }
603+ // Make sure to internally store the per network endpoint config by network name
604+ if _ , ok := container .NetworkSettings .Networks [networkName ]; ok {
605+ return nil
606+ }
607+ if nwConfig , ok := container .NetworkSettings .Networks [n .ID ()]; ok {
608+ container .NetworkSettings .Networks [networkName ] = nwConfig
609+ delete (container .NetworkSettings .Networks , n .ID ())
610+ return nil
611+ }
612+
584613 return nil
585614}
586615
@@ -598,15 +627,15 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) error {
598627 return nil
599628 }
600629
601- err := daemon .updateContainerNetworkSettings (container )
630+ err := daemon .updateContainerNetworkSettings (container , nil )
602631 if err != nil {
603632 return err
604633 }
605634 updateSettings = true
606635 }
607636
608- for n := range container .NetworkSettings .Networks {
609- if err := daemon .connectToNetwork (container , n , updateSettings ); err != nil {
637+ for n , nConf := range container .NetworkSettings .Networks {
638+ if err := daemon .connectToNetwork (container , n , nConf , updateSettings ); err != nil {
610639 return err
611640 }
612641 }
@@ -626,12 +655,65 @@ func (daemon *Daemon) getNetworkSandbox(container *container.Container) libnetwo
626655 return sb
627656}
628657
658+ // hasUserDefinedIPAddress returns whether the passed endpoint configuration contains IP address configuration
659+ func hasUserDefinedIPAddress (epConfig * networktypes.EndpointSettings ) bool {
660+ return epConfig != nil && epConfig .IPAMConfig != nil && (len (epConfig .IPAMConfig .IPv4Address ) > 0 || len (epConfig .IPAMConfig .IPv6Address ) > 0 )
661+ }
662+
663+ // User specified ip address is acceptable only for networks with user specified subnets.
664+ func validateNetworkingConfig (n libnetwork.Network , epConfig * networktypes.EndpointSettings ) error {
665+ if ! hasUserDefinedIPAddress (epConfig ) {
666+ return nil
667+ }
668+ _ , nwIPv4Configs , nwIPv6Configs := n .Info ().IpamConfig ()
669+ for _ , s := range []struct {
670+ ipConfigured bool
671+ subnetConfigs []* libnetwork.IpamConf
672+ }{
673+ {
674+ ipConfigured : len (epConfig .IPAMConfig .IPv4Address ) > 0 ,
675+ subnetConfigs : nwIPv4Configs ,
676+ },
677+ {
678+ ipConfigured : len (epConfig .IPAMConfig .IPv6Address ) > 0 ,
679+ subnetConfigs : nwIPv6Configs ,
680+ },
681+ } {
682+ if s .ipConfigured {
683+ foundSubnet := false
684+ for _ , cfg := range s .subnetConfigs {
685+ if len (cfg .PreferredPool ) > 0 {
686+ foundSubnet = true
687+ break
688+ }
689+ }
690+ if ! foundSubnet {
691+ return runconfig .ErrUnsupportedNetworkNoSubnetAndIP
692+ }
693+ }
694+ }
695+
696+ return nil
697+ }
698+
699+ // cleanOperationalData resets the operational data from the passed endpoint settings
700+ func cleanOperationalData (es * networktypes.EndpointSettings ) {
701+ es .EndpointID = ""
702+ es .Gateway = ""
703+ es .IPAddress = ""
704+ es .IPPrefixLen = 0
705+ es .IPv6Gateway = ""
706+ es .GlobalIPv6Address = ""
707+ es .GlobalIPv6PrefixLen = 0
708+ es .MacAddress = ""
709+ }
710+
629711// ConnectToNetwork connects a container to a network
630- func (daemon * Daemon ) ConnectToNetwork (container * container.Container , idOrName string ) error {
712+ func (daemon * Daemon ) ConnectToNetwork (container * container.Container , idOrName string , endpointConfig * networktypes. EndpointSettings ) error {
631713 if ! container .Running {
632714 return derr .ErrorCodeNotRunning .WithArgs (container .ID )
633715 }
634- if err := daemon .connectToNetwork (container , idOrName , true ); err != nil {
716+ if err := daemon .connectToNetwork (container , idOrName , endpointConfig , true ); err != nil {
635717 return err
636718 }
637719 if err := container .ToDiskLocking (); err != nil {
@@ -640,11 +722,15 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
640722 return nil
641723}
642724
643- func (daemon * Daemon ) connectToNetwork (container * container.Container , idOrName string , updateSettings bool ) (err error ) {
725+ func (daemon * Daemon ) connectToNetwork (container * container.Container , idOrName string , endpointConfig * networktypes. EndpointSettings , updateSettings bool ) (err error ) {
644726 if container .HostConfig .NetworkMode .IsContainer () {
645727 return runconfig .ErrConflictSharedNetwork
646728 }
647729
730+ if ! containertypes .NetworkMode (idOrName ).IsUserDefined () && hasUserDefinedIPAddress (endpointConfig ) {
731+ return runconfig .ErrUnsupportedNetworkAndIP
732+ }
733+
648734 if containertypes .NetworkMode (idOrName ).IsBridge () &&
649735 daemon .configStore .DisableBridge {
650736 container .Config .NetworkDisabled = true
@@ -658,12 +744,20 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
658744 return err
659745 }
660746
747+ if err := validateNetworkingConfig (n , endpointConfig ); err != nil {
748+ return err
749+ }
750+
661751 if updateSettings {
662752 if err := daemon .updateNetworkSettings (container , n ); err != nil {
663753 return err
664754 }
665755 }
666756
757+ if endpointConfig != nil {
758+ container .NetworkSettings .Networks [n .Name ()] = endpointConfig
759+ }
760+
667761 ep , err := container .GetEndpointInNetwork (n )
668762 if err == nil {
669763 return fmt .Errorf ("Conflict. A container with name %q is already connected to network %s." , strings .TrimPrefix (container .Name , "/" ), idOrName )
@@ -869,18 +963,16 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) {
869963
870964 sid := container .NetworkSettings .SandboxID
871965 settings := container .NetworkSettings .Networks
966+ if sid == "" || len (settings ) == 0 {
967+ return
968+ }
969+
872970 var networks []libnetwork.Network
873- for n := range settings {
971+ for n , epSettings := range settings {
874972 if nw , err := daemon .FindNetwork (n ); err == nil {
875973 networks = append (networks , nw )
876974 }
877- settings [n ] = & networktypes.EndpointSettings {}
878- }
879-
880- container .NetworkSettings = & network.Settings {Networks : settings }
881-
882- if sid == "" || len (settings ) == 0 {
883- return
975+ cleanOperationalData (epSettings )
884976 }
885977
886978 sb , err := daemon .netController .SandboxByID (sid )
0 commit comments