github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/drivers/ipvlan/ipvlan_network.go (about)

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