github.com/moby/docker@v26.1.3+incompatible/libnetwork/drivers/ipvlan/ipvlan_network.go (about) 1 //go:build linux 2 3 package ipvlan 4 5 import ( 6 "context" 7 "fmt" 8 9 "github.com/containerd/log" 10 "github.com/docker/docker/libnetwork/driverapi" 11 "github.com/docker/docker/libnetwork/netlabel" 12 "github.com/docker/docker/libnetwork/ns" 13 "github.com/docker/docker/libnetwork/options" 14 "github.com/docker/docker/libnetwork/types" 15 "github.com/docker/docker/pkg/parsers/kernel" 16 "github.com/docker/docker/pkg/stringid" 17 ) 18 19 // CreateNetwork the network for the specified driver type 20 func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { 21 kv, err := kernel.GetKernelVersion() 22 if err != nil { 23 return fmt.Errorf("failed to check kernel version for ipvlan driver support: %v", err) 24 } 25 // ensure Kernel version is >= v4.2 for ipvlan support 26 if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) { 27 return fmt.Errorf("kernel version failed to meet the minimum ipvlan kernel requirement of %d.%d, found %d.%d.%d", 28 ipvlanKernelVer, ipvlanMajorVer, kv.Kernel, kv.Major, kv.Minor) 29 } 30 // reject a null v4 network 31 if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" { 32 return fmt.Errorf("ipv4 pool is empty") 33 } 34 // parse and validate the config and bind to networkConfiguration 35 config, err := parseNetworkOptions(nid, option) 36 if err != nil { 37 return err 38 } 39 config.processIPAM(ipV4Data, ipV6Data) 40 41 // if parent interface not specified, create a dummy type link to use named dummy+net_id 42 if config.Parent == "" { 43 config.Parent = getDummyName(stringid.TruncateID(config.ID)) 44 config.Internal = true 45 } 46 foundExisting, err := d.createNetwork(config) 47 if err != nil { 48 return err 49 } 50 51 if foundExisting { 52 return types.InternalMaskableErrorf("restoring existing network %s", config.ID) 53 } 54 55 // update persistent db, rollback on fail 56 err = d.storeUpdate(config) 57 if err != nil { 58 d.deleteNetwork(config.ID) 59 log.G(context.TODO()).Debugf("encountered an error rolling back a network create for %s : %v", config.ID, err) 60 return err 61 } 62 63 return nil 64 } 65 66 // createNetwork is used by new network callbacks and persistent network cache 67 func (d *driver) createNetwork(config *configuration) (bool, error) { 68 foundExisting := false 69 networkList := d.getNetworks() 70 for _, nw := range networkList { 71 if config.Parent == nw.config.Parent { 72 if config.ID != nw.config.ID { 73 return false, fmt.Errorf("network %s is already using parent interface %s", 74 getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent) 75 } 76 log.G(context.TODO()).Debugf("Create Network for the same ID %s\n", config.ID) 77 foundExisting = true 78 break 79 } 80 } 81 if !parentExists(config.Parent) { 82 // Create a dummy link if a dummy name is set for parent 83 if dummyName := getDummyName(stringid.TruncateID(config.ID)); dummyName == config.Parent { 84 err := createDummyLink(config.Parent, dummyName) 85 if err != nil { 86 return false, err 87 } 88 config.CreatedSlaveLink = true 89 90 // notify the user in logs that they have limited communications 91 log.G(context.TODO()).Debugf("Empty -o parent= flags limit communications to other containers inside of network: %s", 92 config.Parent) 93 } else { 94 // if the subinterface parent_iface.vlan_id checks do not pass, return err. 95 // a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10' 96 err := createVlanLink(config.Parent) 97 if err != nil { 98 return false, err 99 } 100 // if driver created the networks slave link, record it for future deletion 101 config.CreatedSlaveLink = true 102 } 103 } 104 if !foundExisting { 105 n := &network{ 106 id: config.ID, 107 driver: d, 108 endpoints: endpointTable{}, 109 config: config, 110 } 111 // add the network 112 d.addNetwork(n) 113 } 114 115 return foundExisting, nil 116 } 117 118 // DeleteNetwork deletes the network for the specified driver type 119 func (d *driver) DeleteNetwork(nid string) error { 120 n := d.network(nid) 121 if n == nil { 122 return fmt.Errorf("network id %s not found", nid) 123 } 124 // if the driver created the slave interface, delete it, otherwise leave it 125 if ok := n.config.CreatedSlaveLink; ok { 126 // if the interface exists, only delete if it matches iface.vlan or dummy.net_id naming 127 if ok := parentExists(n.config.Parent); ok { 128 // only delete the link if it is named the net_id 129 if n.config.Parent == getDummyName(stringid.TruncateID(nid)) { 130 err := delDummyLink(n.config.Parent) 131 if err != nil { 132 log.G(context.TODO()).Debugf("link %s was not deleted, continuing the delete network operation: %v", 133 n.config.Parent, err) 134 } 135 } else { 136 // only delete the link if it matches iface.vlan naming 137 err := delVlanLink(n.config.Parent) 138 if err != nil { 139 log.G(context.TODO()).Debugf("link %s was not deleted, continuing the delete network operation: %v", 140 n.config.Parent, err) 141 } 142 } 143 } 144 } 145 for _, ep := range n.endpoints { 146 if link, err := ns.NlHandle().LinkByName(ep.srcName); err == nil { 147 if err := ns.NlHandle().LinkDel(link); err != nil { 148 log.G(context.TODO()).WithError(err).Warnf("Failed to delete interface (%s)'s link on endpoint (%s) delete", ep.srcName, ep.id) 149 } 150 } 151 152 if err := d.storeDelete(ep); err != nil { 153 log.G(context.TODO()).Warnf("Failed to remove ipvlan endpoint %.7s from store: %v", ep.id, err) 154 } 155 } 156 // delete the *network 157 d.deleteNetwork(nid) 158 // delete the network record from persistent cache 159 err := d.storeDelete(n.config) 160 if err != nil { 161 return fmt.Errorf("error deleting id %s from datastore: %v", nid, err) 162 } 163 return nil 164 } 165 166 // parseNetworkOptions parses docker network options 167 func parseNetworkOptions(id string, option options.Generic) (*configuration, error) { 168 var ( 169 err error 170 config = &configuration{} 171 ) 172 // parse generic labels first 173 if genData, ok := option[netlabel.GenericData]; ok && genData != nil { 174 if config, err = parseNetworkGenericOptions(genData); err != nil { 175 return nil, err 176 } 177 } 178 if val, ok := option[netlabel.Internal]; ok { 179 if internal, ok := val.(bool); ok && internal { 180 config.Internal = true 181 } 182 } 183 184 // verify the ipvlan mode from -o ipvlan_mode option 185 switch config.IpvlanMode { 186 case "": 187 // default to ipvlan L2 mode if -o ipvlan_mode is empty 188 config.IpvlanMode = modeL2 189 case modeL2, modeL3, modeL3S: 190 // valid option 191 default: 192 return nil, fmt.Errorf("requested ipvlan mode '%s' is not valid, 'l2' mode is the ipvlan driver default", config.IpvlanMode) 193 } 194 195 // verify the ipvlan flag from -o ipvlan_flag option 196 switch config.IpvlanFlag { 197 case "": 198 // default to bridge if -o ipvlan_flag is empty 199 config.IpvlanFlag = flagBridge 200 case flagBridge, flagPrivate, flagVepa: 201 // valid option 202 default: 203 return nil, fmt.Errorf("requested ipvlan flag '%s' is not valid, 'bridge' is the ipvlan driver default", config.IpvlanFlag) 204 } 205 206 // loopback is not a valid parent link 207 if config.Parent == "lo" { 208 return nil, fmt.Errorf("loopback interface is not a valid ipvlan parent link") 209 } 210 211 config.ID = id 212 return config, nil 213 } 214 215 // parseNetworkGenericOptions parse generic driver docker network options 216 func parseNetworkGenericOptions(data interface{}) (*configuration, error) { 217 switch opt := data.(type) { 218 case *configuration: 219 return opt, nil 220 case map[string]string: 221 return newConfigFromLabels(opt), nil 222 case options.Generic: 223 var config *configuration 224 opaqueConfig, err := options.GenerateFromModel(opt, config) 225 if err != nil { 226 return nil, err 227 } 228 return opaqueConfig.(*configuration), nil 229 default: 230 return nil, types.InvalidParameterErrorf("unrecognized network configuration format: %v", opt) 231 } 232 } 233 234 // newConfigFromLabels creates a new configuration from the given labels. 235 func newConfigFromLabels(labels map[string]string) *configuration { 236 config := &configuration{} 237 for label, value := range labels { 238 switch label { 239 case parentOpt: 240 // parse driver option '-o parent' 241 config.Parent = value 242 case driverModeOpt: 243 // parse driver option '-o ipvlan_mode' 244 config.IpvlanMode = value 245 case driverFlagOpt: 246 // parse driver option '-o ipvlan_flag' 247 config.IpvlanFlag = value 248 } 249 } 250 251 return config 252 } 253 254 // processIPAM parses v4 and v6 IP information and binds it to the network configuration 255 func (config *configuration) processIPAM(ipamV4Data, ipamV6Data []driverapi.IPAMData) { 256 for _, ipd := range ipamV4Data { 257 config.Ipv4Subnets = append(config.Ipv4Subnets, &ipSubnet{ 258 SubnetIP: ipd.Pool.String(), 259 GwIP: ipd.Gateway.String(), 260 }) 261 } 262 for _, ipd := range ipamV6Data { 263 config.Ipv6Subnets = append(config.Ipv6Subnets, &ipSubnet{ 264 SubnetIP: ipd.Pool.String(), 265 GwIP: ipd.Gateway.String(), 266 }) 267 } 268 }