github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/network/create.go (about) 1 package network 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strconv" 10 11 "github.com/containernetworking/cni/pkg/version" 12 "github.com/containers/common/pkg/config" 13 "github.com/containers/podman/v3/pkg/domain/entities" 14 "github.com/containers/podman/v3/pkg/util" 15 "github.com/pkg/errors" 16 "github.com/sirupsen/logrus" 17 ) 18 19 // Create the CNI network 20 func Create(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (*entities.NetworkCreateReport, error) { 21 var fileName string 22 if err := isSupportedDriver(options.Driver); err != nil { 23 return nil, err 24 } 25 // Acquire a lock for CNI 26 l, err := acquireCNILock(runtimeConfig) 27 if err != nil { 28 return nil, err 29 } 30 defer l.releaseCNILock() 31 if len(options.MacVLAN) > 0 || options.Driver == MacVLANNetworkDriver { 32 fileName, err = createMacVLAN(name, options, runtimeConfig) 33 } else { 34 fileName, err = createBridge(name, options, runtimeConfig) 35 } 36 if err != nil { 37 return nil, err 38 } 39 return &entities.NetworkCreateReport{Filename: fileName}, nil 40 } 41 42 // validateBridgeOptions validate the bridge networking options 43 func validateBridgeOptions(options entities.NetworkCreateOptions) error { 44 subnet := &options.Subnet 45 ipRange := &options.Range 46 gateway := options.Gateway 47 // if IPv6 is set an IPv6 subnet MUST be specified 48 if options.IPv6 && ((subnet.IP == nil) || (subnet.IP != nil && !IsIPv6(subnet.IP))) { 49 return errors.Errorf("ipv6 option requires an IPv6 --subnet to be provided") 50 } 51 // range and gateway depend on subnet 52 if subnet.IP == nil && (ipRange.IP != nil || gateway != nil) { 53 return errors.Errorf("every ip-range or gateway must have a corresponding subnet") 54 } 55 56 // if a range is given, we need to ensure it is "in" the network range. 57 if ipRange.IP != nil { 58 firstIP, err := FirstIPInSubnet(ipRange) 59 if err != nil { 60 return errors.Wrapf(err, "failed to get first IP address from ip-range") 61 } 62 lastIP, err := LastIPInSubnet(ipRange) 63 if err != nil { 64 return errors.Wrapf(err, "failed to get last IP address from ip-range") 65 } 66 if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) { 67 return errors.Errorf("the ip range %s does not fall within the subnet range %s", ipRange.String(), subnet.String()) 68 } 69 } 70 71 // if network is provided and if gateway is provided, make sure it is "in" network 72 if gateway != nil && !subnet.Contains(gateway) { 73 return errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String()) 74 } 75 76 return nil 77 } 78 79 // parseMTU parses the mtu option 80 func parseMTU(mtu string) (int, error) { 81 if mtu == "" { 82 return 0, nil // default 83 } 84 m, err := strconv.Atoi(mtu) 85 if err != nil { 86 return 0, err 87 } 88 if m < 0 { 89 return 0, errors.Errorf("the value %d for mtu is less than zero", m) 90 } 91 return m, nil 92 } 93 94 // parseVlan parses the vlan option 95 func parseVlan(vlan string) (int, error) { 96 if vlan == "" { 97 return 0, nil // default 98 } 99 return strconv.Atoi(vlan) 100 } 101 102 // createBridge creates a CNI network 103 func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { 104 var ( 105 ipamRanges [][]IPAMLocalHostRangeConf 106 err error 107 routes []IPAMRoute 108 ) 109 isGateway := true 110 ipMasq := true 111 112 // validate options 113 if err := validateBridgeOptions(options); err != nil { 114 return "", err 115 } 116 117 // For compatibility with the docker implementation: 118 // if IPv6 is enabled (it really means dual-stack) then an IPv6 subnet has to be provided, and one free network is allocated for IPv4 119 // if IPv6 is not specified the subnet may be specified and can be either IPv4 or IPv6 (podman, unlike docker, allows IPv6 only networks) 120 // If not subnet is specified an IPv4 subnet will be allocated 121 subnet := &options.Subnet 122 ipRange := &options.Range 123 gateway := options.Gateway 124 if subnet.IP != nil { 125 // if network is provided, does it conflict with existing CNI or live networks 126 err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet) 127 if err != nil { 128 return "", err 129 } 130 // obtain CNI subnet default route 131 defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP)) 132 if err != nil { 133 return "", err 134 } 135 routes = append(routes, defaultRoute) 136 // obtain CNI range 137 ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway) 138 if err != nil { 139 return "", err 140 } 141 ipamRanges = append(ipamRanges, ipamRange) 142 } 143 // if no network is provided or IPv6 flag used, figure out the IPv4 network 144 if options.IPv6 || len(routes) == 0 { 145 subnetV4, err := GetFreeNetwork(runtimeConfig) 146 if err != nil { 147 return "", err 148 } 149 // obtain IPv4 default route 150 defaultRoute, err := NewIPAMDefaultRoute(false) 151 if err != nil { 152 return "", err 153 } 154 routes = append(routes, defaultRoute) 155 // the CNI bridge plugin does not need to set 156 // the range or gateway options explicitly 157 ipamRange, err := NewIPAMLocalHostRange(subnetV4, nil, nil) 158 if err != nil { 159 return "", err 160 } 161 ipamRanges = append(ipamRanges, ipamRange) 162 } 163 164 // create CNI config 165 ipamConfig, err := NewIPAMHostLocalConf(routes, ipamRanges) 166 if err != nil { 167 return "", err 168 } 169 170 if options.Internal { 171 isGateway = false 172 ipMasq = false 173 } 174 175 var mtu int 176 var vlan int 177 for k, v := range options.Options { 178 var err error 179 switch k { 180 case "mtu": 181 mtu, err = parseMTU(v) 182 if err != nil { 183 return "", err 184 } 185 186 case "vlan": 187 vlan, err = parseVlan(v) 188 if err != nil { 189 return "", err 190 } 191 192 default: 193 return "", errors.Errorf("unsupported option %s", k) 194 } 195 } 196 197 // obtain host bridge name 198 bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig) 199 if err != nil { 200 return "", err 201 } 202 203 if len(name) > 0 { 204 netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) 205 if err != nil { 206 return "", err 207 } 208 if util.StringInSlice(name, netNames) { 209 return "", errors.Errorf("the network name %s is already used", name) 210 } 211 } else { 212 // If no name is given, we give the name of the bridge device 213 name = bridgeDeviceName 214 } 215 216 // create CNI plugin configuration 217 ncList := NewNcList(name, version.Current(), options.Labels) 218 var plugins []CNIPlugins 219 // TODO need to iron out the role of isDefaultGW and IPMasq 220 bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, mtu, vlan, ipamConfig) 221 plugins = append(plugins, bridge) 222 plugins = append(plugins, NewPortMapPlugin()) 223 plugins = append(plugins, NewFirewallPlugin()) 224 plugins = append(plugins, NewTuningPlugin()) 225 // if we find the dnsname plugin we add configuration for it 226 if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS { 227 if options.Internal { 228 logrus.Warnf("dnsname and --internal networks are incompatible. dnsname plugin not configured for network %s", name) 229 } else { 230 // Note: in the future we might like to allow for dynamic domain names 231 plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName)) 232 } 233 } 234 // Add the podman-machine CNI plugin if we are in a machine 235 if runtimeConfig.MachineEnabled() { // check if we are in a machine vm 236 plugins = append(plugins, NewPodmanMachinePlugin()) 237 } 238 ncList["plugins"] = plugins 239 b, err := json.MarshalIndent(ncList, "", " ") 240 if err != nil { 241 return "", err 242 } 243 if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil { 244 return "", err 245 } 246 cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) 247 err = ioutil.WriteFile(cniPathName, b, 0644) 248 return cniPathName, err 249 } 250 251 func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { 252 var ( 253 mtu int 254 plugins []CNIPlugins 255 ) 256 liveNetNames, err := GetLiveNetworkNames() 257 if err != nil { 258 return "", err 259 } 260 261 // The parent can be defined with --macvlan or as an option (-o parent:device) 262 parentNetworkDevice := options.MacVLAN 263 if len(parentNetworkDevice) < 1 { 264 if parent, ok := options.Options["parent"]; ok { 265 parentNetworkDevice = parent 266 } 267 } 268 269 // Make sure the host-device exists if provided 270 if len(parentNetworkDevice) > 0 && !util.StringInSlice(parentNetworkDevice, liveNetNames) { 271 return "", errors.Errorf("failed to find network interface %q", parentNetworkDevice) 272 } 273 if len(name) > 0 { 274 netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) 275 if err != nil { 276 return "", err 277 } 278 if util.StringInSlice(name, netNames) { 279 return "", errors.Errorf("the network name %s is already used", name) 280 } 281 } else { 282 name, err = GetFreeDeviceName(runtimeConfig) 283 if err != nil { 284 return "", err 285 } 286 } 287 ncList := NewNcList(name, version.Current(), options.Labels) 288 if val, ok := options.Options["mtu"]; ok { 289 intVal, err := strconv.Atoi(val) 290 if err != nil { 291 return "", err 292 } 293 if intVal > 0 { 294 mtu = intVal 295 } 296 } 297 macvlan, err := NewMacVLANPlugin(parentNetworkDevice, options.Gateway, &options.Range, &options.Subnet, mtu) 298 if err != nil { 299 return "", err 300 } 301 plugins = append(plugins, macvlan) 302 ncList["plugins"] = plugins 303 b, err := json.MarshalIndent(ncList, "", " ") 304 if err != nil { 305 return "", err 306 } 307 cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) 308 err = ioutil.WriteFile(cniPathName, b, 0644) 309 return cniPathName, err 310 }