github.com/moby/docker@v26.1.3+incompatible/libnetwork/drivers/macvlan/macvlan_network.go (about)

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