mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #21122 from mavenugo/mviv
Vendor libnetwork v0.7.0-dev.7 : Experimental MacVlan and IPVlan network drivers
This commit is contained in:
commit
2d74f78ab5
32 changed files with 3558 additions and 4 deletions
1
docs/userguide/networking/images/ipvlan_l2_simple.gliffy
Normal file
1
docs/userguide/networking/images/ipvlan_l2_simple.gliffy
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/userguide/networking/images/ipvlan_l2_simple.png
Normal file
BIN
docs/userguide/networking/images/ipvlan_l2_simple.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
1
docs/userguide/networking/images/ipvlan_l2_simple.svg
Normal file
1
docs/userguide/networking/images/ipvlan_l2_simple.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 39 KiB |
File diff suppressed because one or more lines are too long
BIN
docs/userguide/networking/images/macvlan_bridge_simple.png
Normal file
BIN
docs/userguide/networking/images/macvlan_bridge_simple.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 39 KiB |
File diff suppressed because one or more lines are too long
BIN
docs/userguide/networking/images/multi_tenant_8021q_vlans.png
Normal file
BIN
docs/userguide/networking/images/multi_tenant_8021q_vlans.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 48 KiB |
685
experimental/vlan-networks.md
Normal file
685
experimental/vlan-networks.md
Normal file
|
@ -0,0 +1,685 @@
|
|||
|
||||
|
||||
# Macvlan and Ipvlan Network Drivers
|
||||
|
||||
### Getting Started
|
||||
|
||||
The Macvlan and Ipvlan drivers are currently in experimental mode in order to incubate Docker users use cases and vet the implementation to ensure a hardened, production ready driver in a future release. Libnetwork now gives users total control over both IPv4 and IPv6 adressing. The VLAN drivers build on top of that in giving operators complete control of layer 2 VLAN tagging and even Ipvlan L3 routing for users interested in underlay network integration. For overlay deployments that abstract away physical constraints see the [multi-host overlay ](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) driver.
|
||||
|
||||
Macvlan and Ipvlan are a new twist on the tried and true network virtualization technique. The Linux implementations are extremely lightweight because rather then using the traditional Linux bridge for isolation, they are simply associated to a Linux Ethernet interface or sub-interface to enforce seperation between networks and connectivty to the physical network.
|
||||
|
||||
Macvlan and Ipvlan offer a number of unique features and plenty of room for further innovations with the various modes. Two high level advantages of these approaches are, the positive performance implications of bypassing the Linux bridge and the simplicity of having less moving parts. Removing the bridge that traditionally resides in between the Docker host NIC and container interface leaves a very simple setup consisting of container interfaces, attached directly to the Docker host interface. This result is easy access for external facing services as there is no port mappings in these scenarios.
|
||||
|
||||
|
||||
### Pre-Requisites
|
||||
|
||||
- The examples on this page are all single host and setup using Docker experimental builds that can be installed with the following instructions: [Install Docker experimental](https://github.com/docker/docker/tree/master/experimental)
|
||||
|
||||
- All of the examples can be performed on a single host running Docker. Any examples using a sub-interface like `eth0.10` can be replaced with `eth0` or any other valid parent interface on the Docker host. Sub-interfaces with a `.` are created on the fly. `-o parent` interfaces can also be left out of the `docker network create` all together and the driver will create a `dummy` interface that will enable local host connectivity to perform the examples.
|
||||
|
||||
- Kernel requirements:
|
||||
|
||||
- To check your current kernel version, use `uname -r` to display your kernel version
|
||||
- Macvlan Linux kernel v3.9–3.19 and 4.0+
|
||||
- Ipvlan Linux kernel v4.2+ (support for earlier kernels exists but is buggy)
|
||||
|
||||
|
||||
### MacVlan Bridge Mode Example Usage
|
||||
|
||||
Macvlan Bridge mode has a unique MAC address per container used to track MAC to port mappings by the Docker host. This is the largest difference from Ipvlan L2 mode which uses the same MAC address as the parent interface for each container `eth0` interface.
|
||||
|
||||
- Macvlan and Ipvlan driver networks are attached to a parent Docker host interface. Examples are a physical interface such as `eth0`, a sub-interface for 802.1q VLAN tagging like `eth0.10` (`.10` representing VLAN `10`) or even bonded host adaptors which bundle two Ethernet interfaces into a single logical interface.
|
||||
|
||||
- The specified gateway is external to the host provided by the network infrastructure.
|
||||
|
||||
- Each Macvlan Bridge mode Docker network is isolated from one another and there can be only one network attached to a parent interface at a time. There is a theoretical limit of 4,094 sub-interfaces per host adaptor that a Docker network could be attached to.
|
||||
|
||||
- It is not recommended to mix ipvlan and macvlan networks on the same `-o parent=` interface. Older kernel versions will throw uninformative netlink errors such as `device is busy`.
|
||||
|
||||
- Any container inside the same subnet can talk any other container in the same network without a gateway in both `macvlan bridge` mode and `ipvlan L2` modes.
|
||||
|
||||
- The same `docker network` commands apply to the vlan drivers. Some are irrelevant such as `-icc` or `--set-macaddress` for the Ipvlan driver.
|
||||
|
||||
- In Macvlan and Ipvlan L2 mode, containers on separate networks cannot reach one another without an external process routing between the two networks/subnets. This also applies to multiple subnets within the same `docker network`. See Ipvlan L3 mode for inter-subnet communications without a router.
|
||||
|
||||
In the following example, `eth0` on the docker host has an IP on the `192.168.1.0/24` network and a default gateway of `192.168.1.1`. The gateway is an external router with an address of `192.168.1.1`. An IP address is not required on the Docker host interface `eth0` in `bridge` mode, it merely needs to be on the proper upstream network to get forwarded by a network switch or network router.
|
||||
|
||||
![Simple Macvlan Bridge Mode Example](../docs/userguide/networking/../docs/userguide/networking/images/macvlan_bridge_simple.png)
|
||||
|
||||
|
||||
**Note** For Macvlan bridge mode and Ipvlan L2 mode the subnet values need to match the NIC's interface of the Docker host. For example, Use the same subnet and gateway of the Docker host ethernet interface that is specified by the `-o parent=` option.
|
||||
|
||||
- The parent interface used in this example is `eth0` and it is on the subnet `172.16.86.0/24`. The containers in the `docker network` will also need to be on this same subnet as the parent `-o parent=`. The gateway is an external router on the network, not any ip masquerading or any other local proxy.
|
||||
|
||||
- The driver is specified with `-d driver_name` option. In this case `-d macvlan`
|
||||
|
||||
- The parent interface `-o parent=eth0` is configured as followed:
|
||||
|
||||
```
|
||||
ip addr show eth0
|
||||
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
inet 172.16.86.250/24 brd 172.16.86.255 scope global eth0
|
||||
```
|
||||
|
||||
Create the macvlan network and run a couple of containers attached to it:
|
||||
|
||||
```
|
||||
# Macvlan (-o macvlan_mode= Defaults to Bridge mode if not specified)
|
||||
docker network create -d macvlan \
|
||||
--subnet=172.16.86.0/24 \
|
||||
--gateway=172.16.86.1 \
|
||||
-o parent=eth0 pub_net
|
||||
|
||||
# Run a container on the new network specifying the --ip address.
|
||||
docker run --net=pub_net --ip=172.16.86.10 -itd alpine /bin/sh
|
||||
|
||||
# Start a second container and ping the first
|
||||
docker run --net=pub_net -it --rm alpine /bin/sh
|
||||
ping -c 4 172.16.86.10
|
||||
|
||||
# Take a look at the containers ip and routing table
|
||||
|
||||
ip a show eth0
|
||||
eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
|
||||
link/ether 46:b2:6b:26:2f:69 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.16.86.1/24 scope global eth0
|
||||
|
||||
ip route
|
||||
default via 172.16.86.2 dev eth0
|
||||
172.16.86.0/24 dev eth0 src 172.16.86.1
|
||||
|
||||
# NOTE: the containers can NOT ping the underlying host interfaces as
|
||||
# they are intentionally filtered by Linux for additional isolation.
|
||||
# In this case the containers cannot ping the -o parent=172.16.86.250
|
||||
```
|
||||
|
||||
|
||||
You can explicitly specify the `bridge` mode option `-o macvlan_mode=bridge`. It is the default so will be in `bridge` mode either way.
|
||||
|
||||
While the `eth0` interface does not need to have an IP address in Macvlan Bridge mode or Ipvlan L2 mode it is not uncommon to have an IP address on the interface. Addresses can be excluded from getting an address from the default built in IPAM by using the `--aux-address=x.x.x.x` flag. This will blacklist the specified address from being handed out to containers. The same network example above blocking the `-o parent=eth0` address from being handed out to a container.
|
||||
|
||||
```
|
||||
docker network create -d macvlan \
|
||||
--subnet=172.16.86.0/24 \
|
||||
--gateway=172.16.86.1 \
|
||||
--aux-address="exclude_host=172.16.86.250" \
|
||||
-o parent=eth0 pub_net
|
||||
```
|
||||
|
||||
The network can then be deleted with:
|
||||
|
||||
```
|
||||
docker network rm pub_net
|
||||
```
|
||||
|
||||
- **Note:** In both Macvlan and Ipvlan you are not able to ping or communicate with the default namespace IP address. For example, if you create a container and try to ping the Docker host's `eth0` it will **not** work. That traffic is explicitly filtered by the kernel modules themselves to offer additional provider isolation and security.
|
||||
|
||||
For more on Docker networking commands see [Working with Docker network commands](https://docs.docker.com/engine/userguide/networking/work-with-networks/)
|
||||
|
||||
### Ipvlan L2 Mode Example Usage
|
||||
|
||||
The ipvlan `L2` mode example is virtually identical to the macvlan `bridge` mode example. The driver is specified with `-d driver_name` option. In this case `-d ipvlan`
|
||||
|
||||
![Simple Ipvlan L2 Mode Example](../docs/userguide/networking/images/macvlan_bridge_simple.png)
|
||||
|
||||
The parent interface in the next example `-o parent=eth0` is configured as followed:
|
||||
|
||||
```
|
||||
ip addr show eth0
|
||||
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
inet 192.168.1.250/24 brd 192.168.1.255 scope global eth0
|
||||
```
|
||||
|
||||
Use the network from the host's interface as the `--subnet` in the `docker network create`. The container will be attached to the same network as the host interface as set via the `-o parent=` option.
|
||||
|
||||
Create the ipvlan network and run a container attaching to it:
|
||||
|
||||
```
|
||||
# Ipvlan (-o ipvlan_mode= Defaults to L2 mode if not specified)
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.1.0/24 \
|
||||
--gateway=192.168.1.1 \
|
||||
-o ipvlan_mode=l2 \
|
||||
-o parent=eth0 db_net
|
||||
|
||||
# Start a container on the db_net network
|
||||
docker run --net=db_net -it --rm alpine /bin/sh
|
||||
|
||||
# NOTE: the containers can NOT ping the underlying host interfaces as
|
||||
# they are intentionally filtered by Linux for additional isolation.
|
||||
```
|
||||
|
||||
The default mode for Ipvlan is `l2`. The default mode for Macvlan is `bridge`. If `-o ipvlan_mode=` or `-o macvlan_mode=` are left unspecified, the default modes will be used. Similarly, if the `--gateway` is left empty, the first usable address on the network will be set as the gateway. For example, if the subnet provided in the network create is `--subnet=192.168.1.0/24` then the gateway the container receives is `192.168.1.1`.
|
||||
|
||||
The following will create the exact same network as the network `db_net` created prior, with the driver defaults for `--gateway=192.168.1.1` and `-o ipvlan_mode=l2`.
|
||||
|
||||
```
|
||||
# Ipvlan (-o ipvlan_mode= Defaults to L2 mode if not specified)
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.1.0/24 \
|
||||
-o parent=eth0 db_net_ipv
|
||||
|
||||
# Start a container with an explicit name in daemon mode
|
||||
docker run --net=db_net_ipv --name=ipv1 -itd alpine /bin/sh
|
||||
|
||||
# Start a second container and ping using the container name
|
||||
# to see the docker included name resolution functionality
|
||||
docker run --net=db_net_ipv --name=ipv2 -it --rm alpine /bin/sh
|
||||
ping -c 4 ipv1
|
||||
|
||||
# NOTE: the containers can NOT ping the underlying host interfaces as
|
||||
# they are intentionally filtered by Linux for additional isolation.
|
||||
```
|
||||
|
||||
The drivers also support the `--internal` flag that will completely isolate containers on a network from any communications external to that network. Since network isolation is tightly coupled to the network's parent interface the result of leaving the `-o parent=` option off of a network create is the exact same as the `--internal` option. If the parent interface is not specified or the `--internal` flag is used, a netlink type `dummy` parent interface is created for the user and used as the parent interface effectively isolating the network completely.
|
||||
|
||||
The following two `docker network create` examples result in identical networks that you can attach container to:
|
||||
|
||||
```
|
||||
# Empty '-o parent=' creates an isolated network
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.10.0/24 isolated1
|
||||
|
||||
# Explicit '--internal' flag is the same:
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.11.0/24 --internal isolated2
|
||||
|
||||
# Even the '--subnet=' can be left empty and the default
|
||||
# IPAM subnet of 172.18.0.0/16 will be assigned
|
||||
docker network create -d ipvlan isolated3
|
||||
|
||||
docker run --net=isolated1 --name=cid1 -it --rm alpine /bin/sh
|
||||
docker run --net=isolated2 --name=cid2 -it --rm alpine /bin/sh
|
||||
docker run --net=isolated3 --name=cid3 -it --rm alpine /bin/sh
|
||||
|
||||
# To attach to any use `docker exec` and start a shell
|
||||
docker exec -it cid1 /bin/sh
|
||||
docker exec -it cid2 /bin/sh
|
||||
docker exec -it cid3 /bin/sh
|
||||
```
|
||||
|
||||
### Macvlan 802.1q Trunk Bridge Mode Example Usage
|
||||
|
||||
VLANs (Virtual Local Area Networks) have long been a primary means of virtualizing data center networks and are still in virtually all existing networks today. VLANs work by tagging a Layer-2 isolation domain with a 12-bit identifier ranging from 1-4094 that is inserted into a packet header that enables a logical grouping of a single or multiple subnets of both IPv4 and IPv6. It is very common for network operators to separate traffic using VLANs based on a subnet(s) function or security profile such as `web`, `db` or any other isolation needs.
|
||||
|
||||
It is very common to have a compute host requirement of running multiple virtual networks concurrently on a host. Linux networking has long supported VLAN tagging, also known by it's standard 802.1q, for maintaining datapath isolation between networks. The Ethernet link connected to a Docker host can be configured to support the 802.1q VLAN IDs, by creating Linux sub-interfaces, each one dedicated to a unique VLAN ID.
|
||||
|
||||
![Simple Ipvlan L2 Mode Example](../docs/userguide/networking/images/multi_tenant_8021q_vlans.png)
|
||||
|
||||
Trunking 802.1q to a host is notoriously painful. It requires configuration file changes in order to be persistent through a reboot and if a bridge is involved a physical NIC needs to be moved into the bridge and the bridge then gets the IP address or risk being cut off from the network.
|
||||
|
||||
Like all of the Docker network drivers, the overarching goal is to alleviate the operational pains of managing network resources. To that end, when a network receives a sub-interface as the parent that does not exist, the drivers create the VLAN tagged interfaces while creating the network.
|
||||
|
||||
In the case of a host reboot, instead of needing to modify often complex network configuration files the driver will recreate all network links when the Docker daemon restarts. The driver tracks if it created the VLAN tagged sub-interface originally with the network create and will **only** recreate the sub-interface after a restart or delete `docker network rm` the link if it created it in the first place with `docker network create`.
|
||||
|
||||
If the user doesn't want Docker to modify the `-o parent` sub-interface, the user simply needs to pass an existing link that already exists as the parent interface. Parent interfaces such as `eth0` are not deleted, only sub-interfaces that are not master links.
|
||||
|
||||
For the driver to add/delete the vlan sub-interfaces the format needs to be `interface_name.vlan_tag`.
|
||||
|
||||
For example: `eth0.50` denotes a parent interface of `eth0` with a slave of `eth0.50` tagged with vlan id `50`. The equivalent `ip link` command would be `ip link add link eth0 name eth0.50 type vlan id 50`.
|
||||
|
||||
Replace the `macvlan` with `ipvlan` in the `-d` driver argument to create macvlan 802.1q trunks.
|
||||
|
||||
**Vlan ID 50**
|
||||
|
||||
In the first network tagged and isolated by the Docker host, `eth0.50` is the parent interface tagged with vlan id `50` specified with `-o parent=eth0.50`. Other naming formats can be used, but the links need to be added and deleted manually using `ip link` or Linux configuration files. As long as the `-o parent` exists anything can be used if compliant with Linux netlink.
|
||||
|
||||
```
|
||||
# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged
|
||||
docker network create -d macvlan \
|
||||
--subnet=192.168.50.0/24 \
|
||||
--gateway=192.168.50.1 \
|
||||
-o parent=eth0.50 macvlan50
|
||||
|
||||
# In two separate terminals, start a Docker container and the containers can now ping one another.
|
||||
docker run --net=macvlan50 -it --name macvlan_test5 --rm alpine /bin/sh
|
||||
docker run --net=macvlan50 -it --name macvlan_test6 --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
**Vlan ID 60**
|
||||
|
||||
In the second network, tagged and isolated by the Docker host, `eth0.60` is the parent interface tagged with vlan id `60` specified with `-o parent=eth0.60`. The `macvlan_mode=` defaults to `macvlan_mode=bridge`. It can also be explicitly set with the same result as shown in the next example.
|
||||
|
||||
```
|
||||
# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged.
|
||||
docker network create -d macvlan \
|
||||
--subnet=192.168.60.0/24 \
|
||||
--gateway=192.168.60.1 \
|
||||
-o parent=eth0.60 -o \
|
||||
-o macvlan_mode=bridge macvlan60
|
||||
|
||||
# In two separate terminals, start a Docker container and the containers can now ping one another.
|
||||
docker run --net=macvlan60 -it --name macvlan_test7 --rm alpine /bin/sh
|
||||
docker run --net=macvlan60 -it --name macvlan_test8 --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
**Example:** Multi-Subnet Macvlan 802.1q Trunking
|
||||
|
||||
The same as the example before except there is an additional subnet bound to the network that the user can choose to provision containers on. In MacVlan/Bridge mode, containers can only ping one another if they are on the same subnet/broadcast domain unless there is an external router that routes the traffic (answers ARP etc) between the two subnets.
|
||||
|
||||
```
|
||||
### Create multiple L2 subnets
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.210.0/24 \
|
||||
--subnet=192.168.212.0/24 \
|
||||
--gateway=192.168.210.254 \
|
||||
--gateway=192.168.212.254 \
|
||||
-o ipvlan_mode=l2 ipvlan210
|
||||
|
||||
# Test 192.168.210.0/24 connectivity between containers
|
||||
docker run --net=ipvlan210 --ip=192.168.210.10 -itd alpine /bin/sh
|
||||
docker run --net=ipvlan210 --ip=192.168.210.9 -it --rm alpine ping -c 2 192.168.210.10
|
||||
|
||||
# Test 192.168.212.0/24 connectivity between containers
|
||||
docker run --net=ipvlan210 --ip=192.168.212.10 -itd alpine /bin/sh
|
||||
docker run --net=ipvlan210 --ip=192.168.212.9 -it --rm alpine ping -c 2 192.168.212.10
|
||||
|
||||
```
|
||||
|
||||
### Ipvlan 802.1q Trunk L2 Mode Example Usage
|
||||
|
||||
Architecturally, Ipvlan L2 mode trunking is the same as Macvlan with regard to gateways and L2 path isolation. There are nuances that can be advantageous for CAM table pressure in ToR switches, one MAC per port and MAC exhaustion on a host's parent NIC to name a few. The 802.1q trunk scenario looks the same. Both modes adhere to tagging standards and have seamless integration with the physical network for underlay integration and hardware vendor plugin integrations.
|
||||
|
||||
![Simple Ipvlan L2 Mode Example](../docs/userguide/networking/images/multi_tenant_8021q_vlans.png)
|
||||
|
||||
The Linux sub-interface tagged with a vlan can either already exist or will be created when you call a `docker network create`. `docker network rm` will delete the sub-interface. Parent interfaces such as `eth0` are not deleted, only sub-interfaces with a netlink parent index > 0.
|
||||
|
||||
For the driver to add/delete the vlan sub-interfaces the format needs to be `interface_name.vlan_tag`. Other sub-interface naming can be used as the specified parent, but the link will not be deleted automatically when `docker network rm` is invoked.
|
||||
|
||||
The option to use either existing parent vlan sub-interfaces or let Docker manage them enables the user to either completely manage the Linux interfaces and networking or let Docker create and delete the Vlan parent sub-interfaces (netlink `ip link`) with no effort from the user.
|
||||
|
||||
For example: `eth0.10` to denote a sub-interface of `eth0` tagged with vlan id `10`. The equivalent `ip link` command would be `ip link add link eth0 name eth0.10 type vlan id 10`.
|
||||
|
||||
The example creates the vlan tagged networks and then start two containers to test connectivity between containers. Different Vlans cannot ping one another without a router routing between the two networks. The default namespace is not reachable per ipvlan design in order to isolate container namespaces from the underlying host.
|
||||
|
||||
**Vlan ID 20**
|
||||
|
||||
In the first network tagged and isolated by the Docker host, `eth0.20` is the parent interface tagged with vlan id `20` specified with `-o parent=eth0.20`. Other naming formats can be used, but the links need to be added and deleted manually using `ip link` or Linux configuration files. As long as the `-o parent` exists anything can be used if compliant with Linux netlink.
|
||||
|
||||
```
|
||||
# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.20.0/24 \
|
||||
--gateway=192.168.20.1 \
|
||||
-o parent=eth0.20 ipvlan20
|
||||
|
||||
# in two separate terminals, start a Docker container and the containers can now ping one another.
|
||||
docker run --net=ipvlan20 -it --name ivlan_test1 --rm alpine /bin/sh
|
||||
docker run --net=ipvlan20 -it --name ivlan_test2 --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
**Vlan ID 30**
|
||||
|
||||
In the second network, tagged and isolated by the Docker host, `eth0.30` is the parent interface tagged with vlan id `30` specified with `-o parent=eth0.30`. The `ipvlan_mode=` defaults to l2 mode `ipvlan_mode=l2`. It can also be explicitly set with the same result as shown in the next example.
|
||||
|
||||
```
|
||||
# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged.
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.30.0/24 \
|
||||
--gateway=192.168.30.1 \
|
||||
-o parent=eth0.30 \
|
||||
-o ipvlan_mode=l2 ipvlan30
|
||||
|
||||
# in two separate terminals, start a Docker container and the containers can now ping one another.
|
||||
docker run --net=ipvlan30 -it --name ivlan_test3 --rm alpine /bin/sh
|
||||
docker run --net=ipvlan30 -it --name ivlan_test4 --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
The gateway is set inside of the container as the default gateway. That gateway would typically be an external router on the network.
|
||||
|
||||
```
|
||||
$ ip route
|
||||
default via 192.168.30.1 dev eth0
|
||||
192.168.30.0/24 dev eth0 src 192.168.30.2
|
||||
```
|
||||
|
||||
Example: Multi-Subnet Ipvlan L2 Mode starting two containers on the same subnet and pinging one another. In order for the `192.168.114.0/24` to reach `192.168.116.0/24` it requires an external router in L2 mode. L3 mode can route between subnets that share a common `-o parent=`. This same multi-subnet example is also valid for Macvlan `bridge` mode.
|
||||
|
||||
Secondary addresses on network routers are common as an address space becomes exhausted to add another secondary to a L3 vlan interface or commonly referred to as a "switched virtual interface" (SVI).
|
||||
|
||||
```
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.114.0/24 --subnet=192.168.116.0/24 \
|
||||
--gateway=192.168.114.254 --gateway=192.168.116.254 \
|
||||
-o parent=eth0.114 \
|
||||
-o ipvlan_mode=l2 ipvlan114
|
||||
|
||||
docker run --net=ipvlan114 --ip=192.168.114.10 -it --rm alpine /bin/sh
|
||||
docker run --net=ipvlan114 --ip=192.168.114.11 -it --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
A key takeaway is, operators have the ability to map their physical network into their virtual network for integrating containers into their environment with no operational overhauls required. NetOps simply drops an 802.1q trunk into the Docker host. That virtual link would be the `-o parent=` passed in the network creation. For untagged (non-VLAN) links, it is as simple as `-o parent=eth0` or for 802.1q trunks with VLAN IDs each network gets mapped to the corresponding VLAN/Subnet from the network.
|
||||
|
||||
An example being, NetOps provides VLAN ID and the associated subnets for VLANs being passed on the Ethernet link to the Docker host server. Those values are simply plugged into the `docker network create` commands when provisioning the Docker networks. These are persistent configurations that are applied every time the Docker engine starts which alleviates having to manage often complex configuration files. The network interfaces can also be managed manually by being pre-created and docker networking will never modify them, simply use them as parent interfaces. Example mappings from NetOps to Docker network commands are as follows:
|
||||
|
||||
- VLAN: 10, Subnet: 172.16.80.0/24, Gateway: 172.16.80.1
|
||||
|
||||
- `--subnet=172.16.80.0/24 --gateway=172.16.80.1 -o parent=eth0.10`
|
||||
|
||||
- VLAN: 20, IP subnet: 172.16.50.0/22, Gateway: 172.16.50.1
|
||||
|
||||
- `--subnet=172.16.50.0/22 --gateway=172.16.50.1 -o parent=eth0.20 `
|
||||
|
||||
- VLAN: 30, Subnet: 10.1.100.0/16, Gateway: 10.1.100.1
|
||||
|
||||
- `--subnet=10.1.100.0/16 --gateway=10.1.100.1 -o parent=eth0.30`
|
||||
|
||||
### IPVlan L3 Mode Example
|
||||
|
||||
IPVlan will require routes to be distributed to each endpoint. The driver only builds the Ipvlan L3 mode port and attaches the container to the interface. Route distribution throughout a cluster is beyond the initial implementation of this single host scoped driver. In L3 mode the Docker host is very similar to a router starting new networks in the container. They are on networks that the upstream network will not know about without route distribution. For those curious how Ipvlan L3 will fit into container networking see the following examples.
|
||||
|
||||
- Ipvlan L3 mode drops all broadcast and multicast traffic.
|
||||
|
||||
- L3 mode needs to be on a separate subnet as the default namespace since it requires a netlink route in the default namespace pointing to the Ipvlan parent interface.
|
||||
|
||||
- The parent interface used in this example is `eth0` and it is on the subnet `192.168.1.0/24`. Notice the `docker network` is **not** on the same subnet as `eth0`.
|
||||
|
||||
- Unlike macvlan bridge mode and ipvlan l2 modes, different subnets/networks can ping one another as long as they share the same parent interface `-o parent=`.
|
||||
|
||||
```
|
||||
ip a show eth0
|
||||
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 00:50:56:39:45:2e brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.1.250/24 brd 192.168.1.255 scope global eth0
|
||||
```
|
||||
|
||||
-A traditional gateway doesn't mean much to an L3 mode Ipvlan interface since there is no broadcast traffic allowed. Because of that, the container default gateway simply point the the containers `eth0` device. See below for CLI output of `ip route` or `ip -6 route` from inside an L3 container for details.
|
||||
|
||||
The mode ` -o ipvlan_mode=l3` must be explicitly specified since the default ipvlan mode is `l2`.
|
||||
|
||||
```
|
||||
# Create the Ipvlan L3 network
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.214.0/24 \
|
||||
--subnet=10.1.214.0/24 \
|
||||
-o ipvlan_mode=l3 ipnet210
|
||||
|
||||
# Test 192.168.214.0/24 connectivity
|
||||
docker run --net=ipnet210 --ip=192.168.214.10 -itd alpine /bin/sh
|
||||
docker run --net=ipnet210 --ip=10.1.214.10 -itd alpine /bin/sh
|
||||
|
||||
# Test L3 connectivity from 10.1.214.0/24 to 192.168.212.0/24
|
||||
docker run --net=ipnet210 --ip=192.168.214.9 -it --rm alpine ping -c 2 10.1.214.10
|
||||
|
||||
# Test L3 connectivity from 192.168.212.0/24 to 10.1.214.0/24
|
||||
docker run --net=ipnet210 --ip=10.1.214.9 -it --rm alpine ping -c 2 192.168.214.10
|
||||
|
||||
```
|
||||
|
||||
Notice there is no `--gateway=` option in the network create. The field is ignored if one is specified `l3` mode. Take a look at the container routing table from inside of the container:
|
||||
|
||||
```
|
||||
# Inside an L3 mode container
|
||||
$ ip route
|
||||
default dev eth0
|
||||
192.168.120.0/24 dev eth0 src 192.168.120.2
|
||||
```
|
||||
|
||||
In order to ping the containers from a remote Docker host or the container be able to ping a remote host, the remote host or the physical network in between need to have a route pointing to the host IP address of the container's Docker host eth interface. More on this as we evolve the Ipvlan `L3` story.
|
||||
|
||||
### Dual Stack IPv4 IPv6 Macvlan Bridge Mode
|
||||
|
||||
**Example:** Macvlan Bridge mode, 802.1q trunk, VLAN ID: 218, Multi-Subnet, Dual Stack
|
||||
|
||||
```
|
||||
# Create multiple bridge subnets with a gateway of x.x.x.1:
|
||||
docker network create -d macvlan \
|
||||
--subnet=192.168.216.0/24 --subnet=192.168.218.0/24 \
|
||||
--gateway=192.168.216.1 --gateway=192.168.218.1 \
|
||||
--subnet=2001:db8:abc8::/64 --gateway=2001:db8:abc8::10 \
|
||||
-o parent=eth0.218 \
|
||||
-o macvlan_mode=bridge macvlan216
|
||||
|
||||
docker run --net=macvlan216 --name=macnet216_test --ip=192.168.216.10 -itd alpine /bin/sh
|
||||
docker run --net=macvlan216 --name=macnet216_test --ip=192.168.218.10 -itd alpine /bin/sh
|
||||
docker run --net=macvlan216 --ip=192.168.216.11 -it --rm alpine /bin/sh
|
||||
docker run --net=macvlan216 --ip=192.168.218.11 -it --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
View the details of one of the containers:
|
||||
|
||||
```
|
||||
docker run --net=macvlan216 --ip=192.168.216.11 -it --rm alpine /bin/sh
|
||||
|
||||
root@526f3060d759:/# ip a show eth0
|
||||
eth0@if92: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 8e:9a:99:25:b6:16 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.216.11/24 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc4::8c9a:99ff:fe25:b616/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc8::2/64 scope link nodad
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
# Specified v4 gateway of 192.168.216.1
|
||||
root@526f3060d759:/# ip route
|
||||
default via 192.168.216.1 dev eth0
|
||||
192.168.216.0/24 dev eth0 proto kernel scope link src 192.168.216.11
|
||||
|
||||
# Specified v6 gateway of 2001:db8:abc8::10
|
||||
root@526f3060d759:/# ip -6 route
|
||||
2001:db8:abc4::/64 dev eth0 proto kernel metric 256
|
||||
2001:db8:abc8::/64 dev eth0 proto kernel metric 256
|
||||
default via 2001:db8:abc8::10 dev eth0 metric 1024
|
||||
```
|
||||
|
||||
### Dual Stack IPv4 IPv6 Ipvlan L2 Mode
|
||||
|
||||
- Not only does Libnetwork give you complete control over IPv4 addressing, but it also gives you total control over IPv6 addressing as well as feature parity between the two address families.
|
||||
|
||||
- The next example will start with IPv6 only. Start two containers on the same VLAN `139` and ping one another. Since the IPv4 subnet is not specified, the default IPAM will provision a default IPv4 subnet. That subnet is isolated unless the upstream network is explicitly routing it on VLAN `139`.
|
||||
|
||||
```
|
||||
# Create a v6 network
|
||||
docker network create -d ipvlan \
|
||||
--subnet=2001:db8:abc2::/64 --gateway=2001:db8:abc2::22 \
|
||||
-o parent=eth0.139 v6ipvlan139
|
||||
|
||||
# Start a container on the network
|
||||
docker run --net=v6ipvlan139 -it --rm alpine /bin/sh
|
||||
|
||||
```
|
||||
|
||||
View the container eth0 interface and v6 routing table:
|
||||
|
||||
```
|
||||
eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 00:50:56:2b:29:40 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.18.0.2/16 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc4::250:56ff:fe2b:2940/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc2::1/64 scope link nodad
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
root@5c1dc74b1daa:/# ip -6 route
|
||||
2001:db8:abc4::/64 dev eth0 proto kernel metric 256
|
||||
2001:db8:abc2::/64 dev eth0 proto kernel metric 256
|
||||
default via 2001:db8:abc2::22 dev eth0 metric 1024
|
||||
```
|
||||
|
||||
Start a second container and ping the first container's v6 address.
|
||||
|
||||
```
|
||||
$ docker run --net=v6ipvlan139 -it --rm alpine /bin/sh
|
||||
|
||||
root@b817e42fcc54:/# ip a show eth0
|
||||
75: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 00:50:56:2b:29:40 brd ff:ff:ff:ff:ff:ff
|
||||
inet 172.18.0.3/16 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc4::250:56ff:fe2b:2940/64 scope link tentative dadfailed
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc2::2/64 scope link nodad
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
root@b817e42fcc54:/# ping6 2001:db8:abc2::1
|
||||
PING 2001:db8:abc2::1 (2001:db8:abc2::1): 56 data bytes
|
||||
64 bytes from 2001:db8:abc2::1%eth0: icmp_seq=0 ttl=64 time=0.044 ms
|
||||
64 bytes from 2001:db8:abc2::1%eth0: icmp_seq=1 ttl=64 time=0.058 ms
|
||||
|
||||
2 packets transmitted, 2 packets received, 0% packet loss
|
||||
round-trip min/avg/max/stddev = 0.044/0.051/0.058/0.000 ms
|
||||
```
|
||||
|
||||
The next example with setup a dual stack IPv4/IPv6 network with an example VLAN ID of `140`.
|
||||
|
||||
Next create a network with two IPv4 subnets and one IPv6 subnets, all of which have explicit gateways:
|
||||
|
||||
```
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.140.0/24 --subnet=192.168.142.0/24 \
|
||||
--gateway=192.168.140.1 --gateway=192.168.142.1 \
|
||||
--subnet=2001:db8:abc9::/64 --gateway=2001:db8:abc9::22 \
|
||||
-o parent=eth0.140 \
|
||||
-o ipvlan_mode=l2 ipvlan140
|
||||
```
|
||||
|
||||
Start a container and view eth0 and both v4 & v6 routing tables:
|
||||
|
||||
```
|
||||
docker run --net=v6ipvlan139 --ip6=2001:db8:abc2::51 -it --rm alpine /bin/sh
|
||||
|
||||
root@3cce0d3575f3:/# ip a show eth0
|
||||
78: eth0@if77: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 00:50:56:2b:29:40 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.140.2/24 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc4::250:56ff:fe2b:2940/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc9::1/64 scope link nodad
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
root@3cce0d3575f3:/# ip route
|
||||
default via 192.168.140.1 dev eth0
|
||||
192.168.140.0/24 dev eth0 proto kernel scope link src 192.168.140.2
|
||||
|
||||
root@3cce0d3575f3:/# ip -6 route
|
||||
2001:db8:abc4::/64 dev eth0 proto kernel metric 256
|
||||
2001:db8:abc9::/64 dev eth0 proto kernel metric 256
|
||||
default via 2001:db8:abc9::22 dev eth0 metric 1024
|
||||
```
|
||||
|
||||
Start a second container with a specific `--ip4` address and ping the first host using ipv4 packets:
|
||||
|
||||
```
|
||||
docker run --net=ipvlan140 --ip=192.168.140.10 -it --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
**Note**: Different subnets on the same parent interface in both Ipvlan `L2` mode and Macvlan `bridge` mode cannot ping one another. That requires a router to proxy-arp the requests with a secondary subnet. However, Ipvlan `L3` will route the unicast traffic between disparate subnets as long as they share the same `-o parent` parent link.
|
||||
|
||||
|
||||
|
||||
### Dual Stack IPv4 IPv6 Ipvlan L3 Mode
|
||||
|
||||
|
||||
**Example:** IpVlan L3 Mode Dual Stack IPv4/IPv6, Multi-Subnet w/ 802.1q Vlan Tag:118
|
||||
|
||||
As in all of the examples, a tagged VLAN interface does not have to be used. The sub-interfaces can be swapped with `eth0`, `eth1` or any other valid interface on the host other then the `lo` loopback.
|
||||
|
||||
The primary difference you will see is that L3 mode does not create a default route with a next-hop but rather sets a default route pointing to `dev eth` only since ARP/Broadcasts/Multicast are all filtered by Linux as per the design.
|
||||
|
||||
```
|
||||
# Create an IPv6+IPv4 Dual Stack Ipvlan L3 network
|
||||
# Gateways for both v4 and v6 are set to a dev e.g. 'default dev eth0'
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.110.0/24 \
|
||||
--subnet=192.168.112.0/24 \
|
||||
--subnet=2001:db8:abc6::/64 \
|
||||
-o parent=eth0.118 \
|
||||
-o ipvlan_mode=l3 ipnet110
|
||||
|
||||
|
||||
# Start a few of containers on the network (ipnet110)
|
||||
# in seperate terminals and check connectivity
|
||||
docker run --net=ipnet110 -it --rm alpine /bin/sh
|
||||
# Start a second container specifying the v6 address
|
||||
docker run --net=ipnet110 --ip6=2001:db8:abc6::10 -it --rm alpine /bin/sh
|
||||
# Start a third specifying the IPv4 address
|
||||
docker run --net=ipnet110 --ip=192.168.112.50 -it --rm alpine /bin/sh
|
||||
# Start a 4th specifying both the IPv4 and IPv6 addresses
|
||||
docker run --net=ipnet110 --ip6=2001:db8:abc6::50 --ip=192.168.112.50 -it --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
Interface and routing table outputs are as follows:
|
||||
|
||||
```
|
||||
root@3a368b2a982e:/# ip a show eth0
|
||||
63: eth0@if59: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 00:50:56:2b:29:40 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.112.2/24 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc4::250:56ff:fe2b:2940/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 2001:db8:abc6::10/64 scope link nodad
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
# Note the default route is simply the eth device because ARPs are filtered.
|
||||
root@3a368b2a982e:/# ip route
|
||||
default dev eth0 scope link
|
||||
192.168.112.0/24 dev eth0 proto kernel scope link src 192.168.112.2
|
||||
|
||||
root@3a368b2a982e:/# ip -6 route
|
||||
2001:db8:abc4::/64 dev eth0 proto kernel metric 256
|
||||
2001:db8:abc6::/64 dev eth0 proto kernel metric 256
|
||||
default dev eth0 metric 1024
|
||||
```
|
||||
|
||||
*Note:* There may be a bug when specifying `--ip6=` addresses when you delete a container with a specified v6 address and then start a new container with the same v6 address it throws the following like the address isn't properly being released to the v6 pool. It will fail to unmount the container and be left dead.
|
||||
|
||||
```
|
||||
docker: Error response from daemon: Address already in use.
|
||||
```
|
||||
|
||||
### Manually Creating 802.1q Links
|
||||
|
||||
**Vlan ID 40**
|
||||
|
||||
If a user does not want the driver to create the vlan sub-interface it simply needs to exist prior to the `docker network create`. If you have sub-interface naming that is not `interface.vlan_id` it is honored in the `-o parent=` option again as long as the interface exists and us up.
|
||||
|
||||
Links if manually created can be named anything you want. As long as the exist when the network is created that is all that matters. Manually created links do not get deleted regardless of the name when the network is deleted with `docker network rm`.
|
||||
|
||||
```
|
||||
# create a new sub-interface tied to dot1q vlan 40
|
||||
ip link add link eth0 name eth0.40 type vlan id 40
|
||||
|
||||
# enable the new sub-interface
|
||||
ip link set eth0.40 up
|
||||
|
||||
# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.40.0/24 \
|
||||
--gateway=192.168.40.1 \
|
||||
-o parent=eth0.40 ipvlan40
|
||||
|
||||
# in two separate terminals, start a Docker container and the containers can now ping one another.
|
||||
docker run --net=ipvlan40 -it --name ivlan_test5 --rm alpine /bin/sh
|
||||
docker run --net=ipvlan40 -it --name ivlan_test6 --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
**Example:** Vlan sub-interface manually created with any name:
|
||||
|
||||
```
|
||||
# create a new sub interface tied to dot1q vlan 40
|
||||
ip link add link eth0 name foo type vlan id 40
|
||||
|
||||
# enable the new sub-interface
|
||||
ip link set foo up
|
||||
|
||||
# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged
|
||||
docker network create -d ipvlan \
|
||||
--subnet=192.168.40.0/24 --gateway=192.168.40.1 \
|
||||
-o parent=foo ipvlan40
|
||||
|
||||
# in two separate terminals, start a Docker container and the containers can now ping one another.
|
||||
docker run --net=ipvlan40 -it --name ivlan_test5 --rm alpine /bin/sh
|
||||
docker run --net=ipvlan40 -it --name ivlan_test6 --rm alpine /bin/sh
|
||||
```
|
||||
|
||||
Manually created links can be cleaned up with:
|
||||
|
||||
```
|
||||
ip link del foo
|
||||
```
|
||||
|
||||
As with all of the Libnetwork drivers, they can be mixed and matched, even as far as running 3rd party ecosystem drivers in parallel for maximum flexibility to the Docker user.
|
|
@ -29,7 +29,7 @@ clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
|
|||
clone git github.com/imdario/mergo 0.2.1
|
||||
|
||||
#get libnetwork packages
|
||||
clone git github.com/docker/libnetwork v0.7.0-dev.5
|
||||
clone git github.com/docker/libnetwork v0.7.0-dev.7
|
||||
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
|
||||
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
|
||||
|
|
590
integration-cli/docker_experimental_network_test.go
Normal file
590
integration-cli/docker_experimental_network_test.go
Normal file
|
@ -0,0 +1,590 @@
|
|||
// +build experimental
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
var (
|
||||
MacvlanKernelSupport = testRequirement{
|
||||
func() bool {
|
||||
const macvlanKernelVer = 3 // minimum macvlan kernel support
|
||||
const macvlanMajorVer = 9 // minimum macvlan major kernel support
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// ensure Kernel version is >= v3.9 for macvlan support
|
||||
if kv.Kernel < macvlanKernelVer || (kv.Kernel == macvlanKernelVer && kv.Major < macvlanMajorVer) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
"kernel version failed to meet the minimum macvlan kernel requirement of 3.9",
|
||||
}
|
||||
IpvlanKernelSupport = testRequirement{
|
||||
func() bool {
|
||||
const ipvlanKernelVer = 4 // minimum ipvlan kernel support
|
||||
const ipvlanMajorVer = 2 // minimum ipvlan major kernel support
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// ensure Kernel version is >= v4.2 for ipvlan support
|
||||
if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
"kernel version failed to meet the minimum ipvlan kernel requirement of 4.0.0",
|
||||
}
|
||||
)
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) {
|
||||
// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
// master dummy interface 'dm' abbreviation represents 'docker macvlan'
|
||||
master := "dm-dummy0"
|
||||
// simulate the master link the vlan tagged subinterface parent link will use
|
||||
out, err := createMasterDummy(c, master)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network specifying the desired sub-interface name
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.60", "dm-persist")
|
||||
assertNwIsAvailable(c, "dm-persist")
|
||||
// Restart docker daemon to test the config has persisted to disk
|
||||
s.d.Restart()
|
||||
// verify network is recreated from persistence
|
||||
assertNwIsAvailable(c, "dm-persist")
|
||||
// cleanup the master interface that also collects the slave dev
|
||||
deleteInterface(c, "dm-dummy0")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) {
|
||||
// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
// master dummy interface 'di' notation represent 'docker ipvlan'
|
||||
master := "di-dummy0"
|
||||
// simulate the master link the vlan tagged subinterface parent link will use
|
||||
out, err := createMasterDummy(c, master)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network specifying the desired sub-interface name
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.70", "di-persist")
|
||||
assertNwIsAvailable(c, "di-persist")
|
||||
// Restart docker daemon to test the config has persisted to disk
|
||||
s.d.Restart()
|
||||
// verify network is recreated from persistence
|
||||
assertNwIsAvailable(c, "di-persist")
|
||||
// cleanup the master interface that also collects the slave dev
|
||||
deleteInterface(c, "di-dummy0")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) {
|
||||
// verify the driver automatically provisions the 802.1q link (dm-dummy0.50)
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
// master dummy interface 'dm' abbreviation represents 'docker macvlan'
|
||||
master := "dm-dummy0"
|
||||
// simulate the master link the vlan tagged subinterface parent link will use
|
||||
out, err := createMasterDummy(c, master)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network specifying the desired sub-interface name
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.50", "dm-subinterface")
|
||||
assertNwIsAvailable(c, "dm-subinterface")
|
||||
// cleanup the master interface which also collects the slave dev
|
||||
deleteInterface(c, "dm-dummy0")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) {
|
||||
// verify the driver automatically provisions the 802.1q link (di-dummy0.50)
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
// master dummy interface 'dm' abbreviation represents 'docker ipvlan'
|
||||
master := "di-dummy0"
|
||||
// simulate the master link the vlan tagged subinterface parent link will use
|
||||
out, err := createMasterDummy(c, master)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network specifying the desired sub-interface name
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.60", "di-subinterface")
|
||||
assertNwIsAvailable(c, "di-subinterface")
|
||||
// cleanup the master interface which also collects the slave dev
|
||||
deleteInterface(c, "di-dummy0")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) {
|
||||
// verify the same parent interface cannot be used if already in use by an existing network
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
// master dummy interface 'dm' abbreviation represents 'docker macvlan'
|
||||
master := "dm-dummy0"
|
||||
out, err := createMasterDummy(c, master)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
out, err = createVlanInterface(c, master, "dm-dummy0.40", "40")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network using an existing parent interface
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.40", "dm-subinterface")
|
||||
assertNwIsAvailable(c, "dm-subinterface")
|
||||
// attempt to create another network using the same parent iface that should fail
|
||||
out, _, err = dockerCmdWithError("network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.40", "dm-parent-net-overlap")
|
||||
// verify that the overlap returns an error
|
||||
c.Assert(err, check.NotNil)
|
||||
// cleanup the master interface which also collects the slave dev
|
||||
deleteInterface(c, "dm-dummy0")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) {
|
||||
// verify the same parent interface cannot be used if already in use by an existing network
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
// master dummy interface 'dm' abbreviation represents 'docker ipvlan'
|
||||
master := "di-dummy0"
|
||||
out, err := createMasterDummy(c, master)
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
out, err = createVlanInterface(c, master, "di-dummy0.30", "30")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network using an existing parent interface
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.30", "di-subinterface")
|
||||
assertNwIsAvailable(c, "di-subinterface")
|
||||
// attempt to create another network using the same parent iface that should fail
|
||||
out, _, err = dockerCmdWithError("network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.30", "di-parent-net-overlap")
|
||||
// verify that the overlap returns an error
|
||||
c.Assert(err, check.NotNil)
|
||||
// cleanup the master interface which also collects the slave dev
|
||||
deleteInterface(c, "di-dummy0")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkMacvlanMultiSubnet(c *check.C) {
|
||||
// create a dual stack multi-subnet Macvlan bridge mode network and validate connectivity between four containers, two on each subnet
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "--subnet=172.28.100.0/24", "--subnet=172.28.102.0/24", "--gateway=172.28.102.254",
|
||||
"--subnet=2001:db8:abc2::/64", "--subnet=2001:db8:abc4::/64", "--gateway=2001:db8:abc4::254", "dualstackbridge")
|
||||
// Ensure the network was created
|
||||
assertNwIsAvailable(c, "dualstackbridge")
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=first", "--ip", "172.28.100.20", "--ip6", "2001:db8:abc2::20", "busybox", "top")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=second", "--ip", "172.28.100.21", "--ip6", "2001:db8:abc2::21", "busybox", "top")
|
||||
|
||||
// Inspect and store the v4 address from specified container on the network dualstackbridge
|
||||
ip := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.IPAddress")
|
||||
// Inspect and store the v6 address from specified container on the network dualstackbridge
|
||||
ip6 := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.GlobalIPv6Address")
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address second to first
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", strings.TrimSpace(ip))
|
||||
c.Assert(err, check.IsNil)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address second to first
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping6", "-c", "1", strings.TrimSpace(ip6))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=third", "--ip", "172.28.102.20", "--ip6", "2001:db8:abc4::20", "busybox", "top")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=fourth", "--ip", "172.28.102.21", "--ip6", "2001:db8:abc4::21", "busybox", "top")
|
||||
|
||||
// Inspect and store the v4 address from specified container on the network dualstackbridge
|
||||
ip = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.IPAddress")
|
||||
// Inspect and store the v6 address from specified container on the network dualstackbridge
|
||||
ip6 = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.GlobalIPv6Address")
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address from third to fourth
|
||||
_, _, err = dockerCmdWithError("exec", "fourth", "ping", "-c", "1", strings.TrimSpace(ip))
|
||||
c.Assert(err, check.IsNil)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
|
||||
_, _, err = dockerCmdWithError("exec", "fourth", "ping6", "-c", "1", strings.TrimSpace(ip6))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Inspect the v4 gateway to ensure the proper default GW was assigned
|
||||
ip4gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.Gateway")
|
||||
c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.100.1")
|
||||
// Inspect the v6 gateway to ensure the proper default GW was assigned
|
||||
ip6gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.IPv6Gateway")
|
||||
c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc2::1")
|
||||
|
||||
// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
ip4gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.Gateway")
|
||||
c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.102.254")
|
||||
// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
ip6gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.IPv6Gateway")
|
||||
c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc4::254")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL2MultiSubnet(c *check.C) {
|
||||
// create a dual stack multi-subnet Ipvlan L2 network and validate connectivity within the subnets, two on each subnet
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.200.0/24", "--subnet=172.28.202.0/24", "--gateway=172.28.202.254",
|
||||
"--subnet=2001:db8:abc8::/64", "--subnet=2001:db8:abc6::/64", "--gateway=2001:db8:abc6::254", "dualstackl2")
|
||||
// Ensure the network was created
|
||||
assertNwIsAvailable(c, "dualstackl2")
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.200.0/24 and 2001:db8:abc8::/64
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=first", "--ip", "172.28.200.20", "--ip6", "2001:db8:abc8::20", "busybox", "top")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=second", "--ip", "172.28.200.21", "--ip6", "2001:db8:abc8::21", "busybox", "top")
|
||||
|
||||
// Inspect and store the v4 address from specified container on the network dualstackl2
|
||||
ip := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.IPAddress")
|
||||
// Inspect and store the v6 address from specified container on the network dualstackl2
|
||||
ip6 := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.GlobalIPv6Address")
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address second to first
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", strings.TrimSpace(ip))
|
||||
c.Assert(err, check.IsNil)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address second to first
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping6", "-c", "1", strings.TrimSpace(ip6))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.202.0/24 and 2001:db8:abc6::/64
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=third", "--ip", "172.28.202.20", "--ip6", "2001:db8:abc6::20", "busybox", "top")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=fourth", "--ip", "172.28.202.21", "--ip6", "2001:db8:abc6::21", "busybox", "top")
|
||||
|
||||
// Inspect and store the v4 address from specified container on the network dualstackl2
|
||||
ip = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.IPAddress")
|
||||
// Inspect and store the v6 address from specified container on the network dualstackl2
|
||||
ip6 = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.GlobalIPv6Address")
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address from third to fourth
|
||||
_, _, err = dockerCmdWithError("exec", "fourth", "ping", "-c", "1", strings.TrimSpace(ip))
|
||||
c.Assert(err, check.IsNil)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
|
||||
_, _, err = dockerCmdWithError("exec", "fourth", "ping6", "-c", "1", strings.TrimSpace(ip6))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Inspect the v4 gateway to ensure the proper default GW was assigned
|
||||
ip4gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.Gateway")
|
||||
c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.200.1")
|
||||
// Inspect the v6 gateway to ensure the proper default GW was assigned
|
||||
ip6gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.IPv6Gateway")
|
||||
c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc8::1")
|
||||
|
||||
// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
ip4gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.Gateway")
|
||||
c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.202.254")
|
||||
// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
|
||||
ip6gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.IPv6Gateway")
|
||||
c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc6::254")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL3MultiSubnet(c *check.C) {
|
||||
// create a dual stack multi-subnet Ipvlan L3 network and validate connectivity between all four containers per L3 mode
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.10.0/24", "--subnet=172.28.12.0/24", "--gateway=172.28.12.254",
|
||||
"--subnet=2001:db8:abc9::/64", "--subnet=2001:db8:abc7::/64", "--gateway=2001:db8:abc7::254", "-o", "ipvlan_mode=l3", "dualstackl3")
|
||||
// Ensure the network was created
|
||||
assertNwIsAvailable(c, "dualstackl3")
|
||||
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.10.0/24 and 2001:db8:abc9::/64
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=first", "--ip", "172.28.10.20", "--ip6", "2001:db8:abc9::20", "busybox", "top")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=second", "--ip", "172.28.10.21", "--ip6", "2001:db8:abc9::21", "busybox", "top")
|
||||
|
||||
// Inspect and store the v4 address from specified container on the network dualstackl3
|
||||
ip := inspectField(c, "first", "NetworkSettings.Networks.dualstackl3.IPAddress")
|
||||
// Inspect and store the v6 address from specified container on the network dualstackl3
|
||||
ip6 := inspectField(c, "first", "NetworkSettings.Networks.dualstackl3.GlobalIPv6Address")
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address second to first
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", strings.TrimSpace(ip))
|
||||
c.Assert(err, check.IsNil)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address second to first
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping6", "-c", "1", strings.TrimSpace(ip6))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.12.0/24 and 2001:db8:abc7::/64
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=third", "--ip", "172.28.12.20", "--ip6", "2001:db8:abc7::20", "busybox", "top")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=fourth", "--ip", "172.28.12.21", "--ip6", "2001:db8:abc7::21", "busybox", "top")
|
||||
|
||||
// Inspect and store the v4 address from specified container on the network dualstackl3
|
||||
ip = inspectField(c, "third", "NetworkSettings.Networks.dualstackl3.IPAddress")
|
||||
// Inspect and store the v6 address from specified container on the network dualstackl3
|
||||
ip6 = inspectField(c, "third", "NetworkSettings.Networks.dualstackl3.GlobalIPv6Address")
|
||||
|
||||
// verify ipv4 connectivity to the explicit --ipv address from third to fourth
|
||||
_, _, err = dockerCmdWithError("exec", "fourth", "ping", "-c", "1", strings.TrimSpace(ip))
|
||||
c.Assert(err, check.IsNil)
|
||||
// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
|
||||
_, _, err = dockerCmdWithError("exec", "fourth", "ping6", "-c", "1", strings.TrimSpace(ip6))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Inspect and store the v4 address from specified container on the network dualstackl3
|
||||
ip = inspectField(c, "second", "NetworkSettings.Networks.dualstackl3.IPAddress")
|
||||
// Inspect and store the v6 address from specified container on the network dualstackl3
|
||||
ip6 = inspectField(c, "second", "NetworkSettings.Networks.dualstackl3.GlobalIPv6Address")
|
||||
|
||||
// Verify connectivity across disparate subnets which is unique to L3 mode only
|
||||
_, _, err = dockerCmdWithError("exec", "third", "ping", "-c", "1", strings.TrimSpace(ip))
|
||||
c.Assert(err, check.IsNil)
|
||||
_, _, err = dockerCmdWithError("exec", "third", "ping6", "-c", "1", strings.TrimSpace(ip6))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
|
||||
ip4gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackl3.Gateway")
|
||||
c.Assert(strings.TrimSpace(ip4gw), check.Equals, "")
|
||||
// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
|
||||
ip6gw := inspectField(c, "third", "NetworkSettings.Networks.dualstackl3.IPv6Gateway")
|
||||
c.Assert(strings.TrimSpace(ip6gw), check.Equals, "")
|
||||
}
|
||||
|
||||
func (s *DockerNetworkSuite) TestDockerNetworkIpvlanAddressing(c *check.C) {
|
||||
// Ensure the default gateways, next-hops and default dev devices are properly set
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "--subnet=172.28.130.0/24",
|
||||
"--subnet=2001:db8:abca::/64", "--gateway=2001:db8:abca::254", "-o", "macvlan_mode=bridge", "dualstackbridge")
|
||||
assertNwIsAvailable(c, "dualstackbridge")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=first", "busybox", "top")
|
||||
// Validate macvlan bridge mode defaults gateway sets the default IPAM next-hop inferred from the subnet
|
||||
out, _, err := dockerCmdWithError("exec", "first", "ip", "route")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(out, checker.Contains, "default via 172.28.130.1 dev eth0")
|
||||
// Validate macvlan bridge mode sets the v6 gateway to the user specified default gateway/next-hop
|
||||
out, _, err = dockerCmdWithError("exec", "first", "ip", "-6", "route")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(out, checker.Contains, "default via 2001:db8:abca::254 dev eth0")
|
||||
|
||||
// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
|
||||
// for either an explicitly set route by the user or inferred via default IPAM
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.140.0/24", "--gateway=172.28.140.254",
|
||||
"--subnet=2001:db8:abcb::/64", "-o", "ipvlan_mode=l2", "dualstackl2")
|
||||
assertNwIsAvailable(c, "dualstackl2")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=second", "busybox", "top")
|
||||
// Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
|
||||
out, _, err = dockerCmdWithError("exec", "second", "ip", "route")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(out, checker.Contains, "default via 172.28.140.254 dev eth0")
|
||||
// Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
|
||||
out, _, err = dockerCmdWithError("exec", "second", "ip", "-6", "route")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(out, checker.Contains, "default via 2001:db8:abcb::1 dev eth0")
|
||||
|
||||
// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.160.0/24", "--gateway=172.28.160.254",
|
||||
"--subnet=2001:db8:abcd::/64", "--gateway=2001:db8:abcd::254", "-o", "ipvlan_mode=l3", "dualstackl3")
|
||||
assertNwIsAvailable(c, "dualstackl3")
|
||||
dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=third", "busybox", "top")
|
||||
// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
|
||||
out, _, err = dockerCmdWithError("exec", "third", "ip", "route")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(out, checker.Contains, "default dev eth0")
|
||||
// Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
|
||||
out, _, err = dockerCmdWithError("exec", "third", "ip", "-6", "route")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(out, checker.Contains, "default dev eth0")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkMacVlanBridgeNilParent(c *check.C) {
|
||||
// macvlan bridge mode - dummy parent interface is provisioned dynamically
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "dm-nil-parent")
|
||||
assertNwIsAvailable(c, "dm-nil-parent")
|
||||
|
||||
// start two containers on the same subnet
|
||||
dockerCmd(c, "run", "-d", "--net=dm-nil-parent", "--name=first", "busybox", "top")
|
||||
c.Assert(waitRun("first"), check.IsNil)
|
||||
dockerCmd(c, "run", "-d", "--net=dm-nil-parent", "--name=second", "busybox", "top")
|
||||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// intra-network communications should succeed
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) {
|
||||
// macvlan bridge mode --internal containers can communicate inside the network but not externally
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "--internal", "dm-internal")
|
||||
assertNwIsAvailable(c, "dm-internal")
|
||||
nr := getNetworkResource(c, "dm-internal")
|
||||
c.Assert(nr.Internal, checker.True)
|
||||
|
||||
// start two containers on the same subnet
|
||||
dockerCmd(c, "run", "-d", "--net=dm-internal", "--name=first", "busybox", "top")
|
||||
c.Assert(waitRun("first"), check.IsNil)
|
||||
dockerCmd(c, "run", "-d", "--net=dm-internal", "--name=second", "busybox", "top")
|
||||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// access outside of the network should fail
|
||||
_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(err, check.NotNil)
|
||||
// intra-network communications should succeed
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkIpvlanL2NilParent(c *check.C) {
|
||||
// ipvlan l2 mode - dummy parent interface is provisioned dynamically
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "di-nil-parent")
|
||||
assertNwIsAvailable(c, "di-nil-parent")
|
||||
|
||||
// start two containers on the same subnet
|
||||
dockerCmd(c, "run", "-d", "--net=di-nil-parent", "--name=first", "busybox", "top")
|
||||
c.Assert(waitRun("first"), check.IsNil)
|
||||
dockerCmd(c, "run", "-d", "--net=di-nil-parent", "--name=second", "busybox", "top")
|
||||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// intra-network communications should succeed
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) {
|
||||
// ipvlan l2 mode --internal containers can communicate inside the network but not externally
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "--internal", "di-internal")
|
||||
assertNwIsAvailable(c, "di-internal")
|
||||
nr := getNetworkResource(c, "di-internal")
|
||||
c.Assert(nr.Internal, checker.True)
|
||||
|
||||
// start two containers on the same subnet
|
||||
dockerCmd(c, "run", "-d", "--net=di-internal", "--name=first", "busybox", "top")
|
||||
c.Assert(waitRun("first"), check.IsNil)
|
||||
dockerCmd(c, "run", "-d", "--net=di-internal", "--name=second", "busybox", "top")
|
||||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// access outside of the network should fail
|
||||
_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(err, check.NotNil)
|
||||
// intra-network communications should succeed
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkIpvlanL3NilParent(c *check.C) {
|
||||
// ipvlan l3 mode - dummy parent interface is provisioned dynamically
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24",
|
||||
"--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "di-nil-parent-l3")
|
||||
assertNwIsAvailable(c, "di-nil-parent-l3")
|
||||
|
||||
// start two containers on separate subnets
|
||||
dockerCmd(c, "run", "-d", "--ip=172.28.220.10", "--net=di-nil-parent-l3", "--name=first", "busybox", "top")
|
||||
c.Assert(waitRun("first"), check.IsNil)
|
||||
dockerCmd(c, "run", "-d", "--ip=172.28.230.10", "--net=di-nil-parent-l3", "--name=second", "busybox", "top")
|
||||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// intra-network communications should succeed
|
||||
_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) {
|
||||
// ipvlan l3 mode --internal containers can communicate inside the network but not externally
|
||||
testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24",
|
||||
"--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "--internal", "di-internal-l3")
|
||||
assertNwIsAvailable(c, "di-internal-l3")
|
||||
nr := getNetworkResource(c, "di-internal-l3")
|
||||
c.Assert(nr.Internal, checker.True)
|
||||
|
||||
// start two containers on separate subnets
|
||||
dockerCmd(c, "run", "-d", "--ip=172.28.220.10", "--net=di-internal-l3", "--name=first", "busybox", "top")
|
||||
c.Assert(waitRun("first"), check.IsNil)
|
||||
dockerCmd(c, "run", "-d", "--ip=172.28.230.10", "--net=di-internal-l3", "--name=second", "busybox", "top")
|
||||
c.Assert(waitRun("second"), check.IsNil)
|
||||
|
||||
// access outside of the network should fail
|
||||
_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
|
||||
c.Assert(err, check.NotNil)
|
||||
// intra-network communications should succeed
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) {
|
||||
// macvlan bridge mode - empty parent interface containers can reach each other internally but not externally
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
netName := "dm-parent-exists"
|
||||
out, err := createMasterDummy(c, "dm-dummy0")
|
||||
//out, err := createVlanInterface(c, "dm-parent", "dm-slave", "macvlan", "bridge")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network using an existing parent interface
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0", netName)
|
||||
assertNwIsAvailable(c, netName)
|
||||
// delete the network while preserving the parent link
|
||||
dockerCmd(c, "network", "rm", netName)
|
||||
assertNwNotAvailable(c, netName)
|
||||
// verify the network delete did not delete the predefined link
|
||||
out, err = linkExists(c, "dm-dummy0")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
deleteInterface(c, "dm-dummy0")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkMacVlanSubinterface(c *check.C) {
|
||||
// macvlan bridge mode - empty parent interface containers can reach each other internally but not externally
|
||||
testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
|
||||
netName := "dm-subinterface"
|
||||
out, err := createMasterDummy(c, "dm-dummy0")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
out, err = createVlanInterface(c, "dm-dummy0", "dm-dummy0.20", "20")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// create a network using an existing parent interface
|
||||
dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.20", netName)
|
||||
assertNwIsAvailable(c, netName)
|
||||
|
||||
// start containers on 802.1q tagged '-o parent' sub-interface
|
||||
dockerCmd(c, "run", "-d", "--net=dm-subinterface", "--name=first", "busybox", "top")
|
||||
c.Assert(waitRun("first"), check.IsNil)
|
||||
dockerCmd(c, "run", "-d", "--net=dm-subinterface", "--name=second", "busybox", "top")
|
||||
c.Assert(waitRun("second"), check.IsNil)
|
||||
// verify containers can communicate
|
||||
_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// remove the containers
|
||||
dockerCmd(c, "rm", "-f", "first")
|
||||
dockerCmd(c, "rm", "-f", "second")
|
||||
// delete the network while preserving the parent link
|
||||
dockerCmd(c, "network", "rm", netName)
|
||||
assertNwNotAvailable(c, netName)
|
||||
// verify the network delete did not delete the predefined sub-interface
|
||||
out, err = linkExists(c, "dm-dummy0.20")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
// delete the parent interface which also collects the slave
|
||||
deleteInterface(c, "dm-dummy0")
|
||||
c.Assert(err, check.IsNil, check.Commentf(out))
|
||||
}
|
||||
|
||||
func createMasterDummy(c *check.C, master string) (string, error) {
|
||||
// ip link add <dummy_name> type dummy
|
||||
args := []string{"link", "add", master, "type", "dummy"}
|
||||
ipLinkCmd := exec.Command("ip", args...)
|
||||
out, _, err := runCommandWithOutput(ipLinkCmd)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
// ip link set dummy_name up
|
||||
args = []string{"link", "set", master, "up"}
|
||||
ipLinkCmd = exec.Command("ip", args...)
|
||||
out, _, err = runCommandWithOutput(ipLinkCmd)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
func createVlanInterface(c *check.C, master, slave, id string) (string, error) {
|
||||
// ip link add link <master> name <master>.<VID> type vlan id <VID>
|
||||
args := []string{"link", "add", "link", master, "name", slave, "type", "vlan", "id", id}
|
||||
ipLinkCmd := exec.Command("ip", args...)
|
||||
out, _, err := runCommandWithOutput(ipLinkCmd)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
// ip link set <sub_interface_name> up
|
||||
args = []string{"link", "set", slave, "up"}
|
||||
ipLinkCmd = exec.Command("ip", args...)
|
||||
out, _, err = runCommandWithOutput(ipLinkCmd)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
func linkExists(c *check.C, master string) (string, error) {
|
||||
// verify the specified link exists, ip link show <link_name>
|
||||
args := []string{"link", "show", master}
|
||||
ipLinkCmd := exec.Command("ip", args...)
|
||||
out, _, err := runCommandWithOutput(ipLinkCmd)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, err
|
||||
}
|
|
@ -1,5 +1,13 @@
|
|||
# Changelog
|
||||
|
||||
## 0.7.0-dev.7 (2016-03-11)
|
||||
- Bumped up the minimum kernel version for ipvlan to 4.2
|
||||
- Removed modprobe from macvlan/ipvlan drivers to resolve docker IT failures
|
||||
- Close dbus connection if firewalld is not started
|
||||
|
||||
## 0.7.0-dev.6 (2016-03-10)
|
||||
- Experimental support for macvlan and ipvlan drivers
|
||||
|
||||
## 0.7.0-dev.5 (2016-03-08)
|
||||
- Fixes https://github.com/docker/docker/issues/20847
|
||||
- Fixes https://github.com/docker/docker/issues/20997
|
||||
|
|
|
@ -23,7 +23,7 @@ build: ${build_image}.created
|
|||
|
||||
build-local:
|
||||
@mkdir -p "bin"
|
||||
$(shell which godep) go build -o "bin/dnet" ./cmd/dnet
|
||||
$(shell which godep) go build -tags experimental -o "bin/dnet" ./cmd/dnet
|
||||
|
||||
clean:
|
||||
@if [ -d bin ]; then \
|
||||
|
|
91
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go
vendored
Normal file
91
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
package ipvlan
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
)
|
||||
|
||||
const (
|
||||
vethLen = 7
|
||||
containerVethPrefix = "eth"
|
||||
vethPrefix = "veth"
|
||||
ipvlanType = "ipvlan" // driver type name
|
||||
modeL2 = "l2" // ipvlan mode l2 is the default
|
||||
modeL3 = "l3" // ipvlan L3 mode
|
||||
parentOpt = "parent" // parent interface -o parent
|
||||
modeOpt = "_mode" // ipvlan mode ux opt suffix
|
||||
)
|
||||
|
||||
var driverModeOpt = ipvlanType + modeOpt // mode -o ipvlan_mode
|
||||
|
||||
type endpointTable map[string]*endpoint
|
||||
|
||||
type networkTable map[string]*network
|
||||
|
||||
type driver struct {
|
||||
networks networkTable
|
||||
sync.Once
|
||||
sync.Mutex
|
||||
store datastore.DataStore
|
||||
}
|
||||
|
||||
type endpoint struct {
|
||||
id string
|
||||
mac net.HardwareAddr
|
||||
addr *net.IPNet
|
||||
addrv6 *net.IPNet
|
||||
srcName string
|
||||
}
|
||||
|
||||
type network struct {
|
||||
id string
|
||||
sbox osl.Sandbox
|
||||
endpoints endpointTable
|
||||
driver *driver
|
||||
config *configuration
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// Init initializes and registers the libnetwork ipvlan driver
|
||||
func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
||||
c := driverapi.Capability{
|
||||
DataScope: datastore.LocalScope,
|
||||
}
|
||||
d := &driver{
|
||||
networks: networkTable{},
|
||||
}
|
||||
d.initStore(config)
|
||||
|
||||
return dc.RegisterDriver(ipvlanType, d, c)
|
||||
}
|
||||
|
||||
func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
||||
return make(map[string]interface{}, 0), nil
|
||||
}
|
||||
|
||||
func (d *driver) Type() string {
|
||||
return ipvlanType
|
||||
}
|
||||
|
||||
func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverNew is a notification for a new discovery event.
|
||||
func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverDelete is a notification for a discovery delete event.
|
||||
func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
78
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go
vendored
Normal file
78
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
package ipvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// CreateEndpoint assigns the mac, ip and endpoint id for the new container
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
|
||||
epOptions map[string]interface{}) error {
|
||||
defer osl.InitOSContext()()
|
||||
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("network id %q not found", nid)
|
||||
}
|
||||
if ifInfo.MacAddress() != nil {
|
||||
return fmt.Errorf("%s interfaces do not support custom mac address assigment", ipvlanType)
|
||||
}
|
||||
ep := &endpoint{
|
||||
id: eid,
|
||||
addr: ifInfo.Address(),
|
||||
addrv6: ifInfo.AddressIPv6(),
|
||||
mac: ifInfo.MacAddress(),
|
||||
}
|
||||
if ep.addr == nil {
|
||||
return fmt.Errorf("create endpoint was not passed an IP address")
|
||||
}
|
||||
// disallow port mapping -p
|
||||
if opt, ok := epOptions[netlabel.PortMap]; ok {
|
||||
if _, ok := opt.([]types.PortBinding); ok {
|
||||
if len(opt.([]types.PortBinding)) > 0 {
|
||||
logrus.Warnf("%s driver does not support port mappings", ipvlanType)
|
||||
}
|
||||
}
|
||||
}
|
||||
// disallow port exposure --expose
|
||||
if opt, ok := epOptions[netlabel.ExposedPorts]; ok {
|
||||
if _, ok := opt.([]types.TransportPort); ok {
|
||||
if len(opt.([]types.TransportPort)) > 0 {
|
||||
logrus.Warnf("%s driver does not support port exposures", ipvlanType)
|
||||
}
|
||||
}
|
||||
}
|
||||
n.addEndpoint(ep)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpoint remove the endpoint and associated netlink interface
|
||||
func (d *driver) DeleteEndpoint(nid, eid string) error {
|
||||
defer osl.InitOSContext()()
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
return err
|
||||
}
|
||||
n := d.network(nid)
|
||||
if n == nil {
|
||||
return fmt.Errorf("network id %q not found", nid)
|
||||
}
|
||||
ep := n.endpoint(eid)
|
||||
if ep == nil {
|
||||
return fmt.Errorf("endpoint id %q not found", eid)
|
||||
}
|
||||
if link, err := netlink.LinkByName(ep.srcName); err == nil {
|
||||
netlink.LinkDel(link)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
195
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go
vendored
Normal file
195
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go
vendored
Normal file
|
@ -0,0 +1,195 @@
|
|||
package ipvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
type staticRoute struct {
|
||||
Destination *net.IPNet
|
||||
RouteType int
|
||||
NextHop net.IP
|
||||
}
|
||||
|
||||
const (
|
||||
defaultV4RouteCidr = "0.0.0.0/0"
|
||||
defaultV6RouteCidr = "::/0"
|
||||
)
|
||||
|
||||
// Join method is invoked when a Sandbox is attached to an endpoint.
|
||||
func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
||||
defer osl.InitOSContext()()
|
||||
n, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint := n.endpoint(eid)
|
||||
if endpoint == nil {
|
||||
return fmt.Errorf("could not find endpoint with id %s", eid)
|
||||
}
|
||||
// generate a name for the iface that will be renamed to eth0 in the sbox
|
||||
containerIfName, err := netutils.GenerateIfaceName(vethPrefix, vethLen)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating an interface name: %v", err)
|
||||
}
|
||||
// create the netlink ipvlan interface
|
||||
vethName, err := createIPVlan(containerIfName, n.config.Parent, n.config.IpvlanMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// bind the generated iface name to the endpoint
|
||||
endpoint.srcName = vethName
|
||||
ep := n.endpoint(eid)
|
||||
if ep == nil {
|
||||
return fmt.Errorf("could not find endpoint with id %s", eid)
|
||||
}
|
||||
if n.config.IpvlanMode == modeL3 {
|
||||
// disable gateway services to add a default gw using dev eth0 only
|
||||
jinfo.DisableGatewayService()
|
||||
defaultRoute, err := ifaceGateway(defaultV4RouteCidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jinfo.AddStaticRoute(defaultRoute.Destination, defaultRoute.RouteType, defaultRoute.NextHop); err != nil {
|
||||
return fmt.Errorf("failed to set an ipvlan l3 mode ipv4 default gateway: %v", err)
|
||||
}
|
||||
logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, Ipvlan_Mode: %s, Parent: %s",
|
||||
ep.addr.IP.String(), n.config.IpvlanMode, n.config.Parent)
|
||||
// If the endpoint has a v6 address, set a v6 default route
|
||||
if ep.addrv6 != nil {
|
||||
default6Route, err := ifaceGateway(defaultV6RouteCidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = jinfo.AddStaticRoute(default6Route.Destination, default6Route.RouteType, default6Route.NextHop); err != nil {
|
||||
return fmt.Errorf("failed to set an ipvlan l3 mode ipv6 default gateway: %v", err)
|
||||
}
|
||||
logrus.Debugf("Ipvlan Endpoint Joined with IPv6_Addr: %s, Ipvlan_Mode: %s, Parent: %s",
|
||||
ep.addrv6.IP.String(), n.config.IpvlanMode, n.config.Parent)
|
||||
}
|
||||
}
|
||||
if n.config.IpvlanMode == modeL2 {
|
||||
// parse and correlate the endpoint v4 address with the available v4 subnets
|
||||
if len(n.config.Ipv4Subnets) > 0 {
|
||||
s := n.getSubnetforIPv4(ep.addr)
|
||||
if s == nil {
|
||||
return fmt.Errorf("could not find a valid ipv4 subnet for endpoint %s", eid)
|
||||
}
|
||||
v4gw, _, err := net.ParseCIDR(s.GwIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gatway %s is not a valid ipv4 address: %v", s.GwIP, err)
|
||||
}
|
||||
err = jinfo.SetGateway(v4gw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, Gateway: %s, Ipvlan_Mode: %s, Parent: %s",
|
||||
ep.addr.IP.String(), v4gw.String(), n.config.IpvlanMode, n.config.Parent)
|
||||
}
|
||||
// parse and correlate the endpoint v6 address with the available v6 subnets
|
||||
if len(n.config.Ipv6Subnets) > 0 {
|
||||
s := n.getSubnetforIPv6(ep.addrv6)
|
||||
if s == nil {
|
||||
return fmt.Errorf("could not find a valid ipv6 subnet for endpoint %s", eid)
|
||||
}
|
||||
v6gw, _, err := net.ParseCIDR(s.GwIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gatway %s is not a valid ipv6 address: %v", s.GwIP, err)
|
||||
}
|
||||
err = jinfo.SetGatewayIPv6(v6gw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("Ipvlan Endpoint Joined with IPv6_Addr: %s, Gateway: %s, Ipvlan_Mode: %s, Parent: %s",
|
||||
ep.addrv6.IP.String(), v6gw.String(), n.config.IpvlanMode, n.config.Parent)
|
||||
}
|
||||
}
|
||||
iNames := jinfo.InterfaceName()
|
||||
err = iNames.SetNames(vethName, containerVethPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Leave method is invoked when a Sandbox detaches from an endpoint.
|
||||
func (d *driver) Leave(nid, eid string) error {
|
||||
defer osl.InitOSContext()()
|
||||
network, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint, err := network.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if endpoint == nil {
|
||||
return fmt.Errorf("could not find endpoint with id %s", eid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ifaceGateway returns a static route for either v4/v6 to be set to the container eth0
|
||||
func ifaceGateway(dfNet string) (*staticRoute, error) {
|
||||
nh, dst, err := net.ParseCIDR(dfNet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse default route %v", err)
|
||||
}
|
||||
defaultRoute := &staticRoute{
|
||||
Destination: dst,
|
||||
RouteType: types.CONNECTED,
|
||||
NextHop: nh,
|
||||
}
|
||||
|
||||
return defaultRoute, nil
|
||||
}
|
||||
|
||||
// getSubnetforIPv4 returns the ipv4 subnet to which the given IP belongs
|
||||
func (n *network) getSubnetforIPv4(ip *net.IPNet) *ipv4Subnet {
|
||||
for _, s := range n.config.Ipv4Subnets {
|
||||
_, snet, err := net.ParseCIDR(s.SubnetIP)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// first check if the mask lengths are the same
|
||||
i, _ := snet.Mask.Size()
|
||||
j, _ := ip.Mask.Size()
|
||||
if i != j {
|
||||
continue
|
||||
}
|
||||
if snet.Contains(ip.IP) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSubnetforIPv6 returns the ipv6 subnet to which the given IP belongs
|
||||
func (n *network) getSubnetforIPv6(ip *net.IPNet) *ipv6Subnet {
|
||||
for _, s := range n.config.Ipv6Subnets {
|
||||
_, snet, err := net.ParseCIDR(s.SubnetIP)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// first check if the mask lengths are the same
|
||||
i, _ := snet.Mask.Size()
|
||||
j, _ := ip.Mask.Size()
|
||||
if i != j {
|
||||
continue
|
||||
}
|
||||
if snet.Contains(ip.IP) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
236
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go
vendored
Normal file
236
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go
vendored
Normal file
|
@ -0,0 +1,236 @@
|
|||
package ipvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// CreateNetwork the network for the specified driver type
|
||||
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
defer osl.InitOSContext()()
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to check kernel version for %s driver support: %v", ipvlanType, err)
|
||||
}
|
||||
// ensure Kernel version is >= v4.2 for ipvlan support
|
||||
if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) {
|
||||
return fmt.Errorf("kernel version failed to meet the minimum ipvlan kernel requirement of %d.%d, found %d.%d.%d",
|
||||
ipvlanKernelVer, ipvlanMajorVer, kv.Kernel, kv.Major, kv.Minor)
|
||||
}
|
||||
// parse and validate the config and bind to networkConfiguration
|
||||
config, err := parseNetworkOptions(nid, option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ID = nid
|
||||
err = config.processIPAM(nid, ipV4Data, ipV6Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// verify the ipvlan mode from -o ipvlan_mode option
|
||||
switch config.IpvlanMode {
|
||||
case "", modeL2:
|
||||
// default to ipvlan L2 mode if -o ipvlan_mode is empty
|
||||
config.IpvlanMode = modeL2
|
||||
case modeL3:
|
||||
config.IpvlanMode = modeL3
|
||||
default:
|
||||
return fmt.Errorf("requested ipvlan mode '%s' is not valid, 'l2' mode is the ipvlan driver default", config.IpvlanMode)
|
||||
}
|
||||
// loopback is not a valid parent link
|
||||
if config.Parent == "lo" {
|
||||
return fmt.Errorf("loopback interface is not a valid %s parent link", ipvlanType)
|
||||
}
|
||||
// if parent interface not specified, create a dummy type link to use named dummy+net_id
|
||||
if config.Parent == "" {
|
||||
config.Parent = getDummyName(stringid.TruncateID(config.ID))
|
||||
// empty parent and --internal are handled the same. Set here to update k/v
|
||||
config.Internal = true
|
||||
}
|
||||
err = d.createNetwork(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// update persistent db, rollback on fail
|
||||
err = d.storeUpdate(config)
|
||||
if err != nil {
|
||||
d.deleteNetwork(config.ID)
|
||||
logrus.Debugf("encoutered an error rolling back a network create for %s : %v", config.ID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createNetwork is used by new network callbacks and persistent network cache
|
||||
func (d *driver) createNetwork(config *configuration) error {
|
||||
networkList := d.getNetworks()
|
||||
for _, nw := range networkList {
|
||||
if config.Parent == nw.config.Parent {
|
||||
return fmt.Errorf("network %s is already using parent interface %s",
|
||||
getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
|
||||
}
|
||||
}
|
||||
if !parentExists(config.Parent) {
|
||||
// if the --internal flag is set, create a dummy link
|
||||
if config.Internal {
|
||||
err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.CreatedSlaveLink = true
|
||||
// notify the user in logs they have limited comunicatins
|
||||
if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
|
||||
logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
|
||||
config.Parent)
|
||||
}
|
||||
} else {
|
||||
// if the subinterface parent_iface.vlan_id checks do not pass, return err.
|
||||
// a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
|
||||
err := createVlanLink(config.Parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if driver created the networks slave link, record it for future deletion
|
||||
config.CreatedSlaveLink = true
|
||||
}
|
||||
}
|
||||
n := &network{
|
||||
id: config.ID,
|
||||
driver: d,
|
||||
endpoints: endpointTable{},
|
||||
config: config,
|
||||
}
|
||||
// add the *network
|
||||
d.addNetwork(n)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNetwork the network for the specified driver type
|
||||
func (d *driver) DeleteNetwork(nid string) error {
|
||||
defer osl.InitOSContext()()
|
||||
n := d.network(nid)
|
||||
if n == nil {
|
||||
return fmt.Errorf("network id %s not found", nid)
|
||||
}
|
||||
// if the driver created the slave interface, delete it, otherwise leave it
|
||||
if ok := n.config.CreatedSlaveLink; ok {
|
||||
// if the interface exists, only delete if it matches iface.vlan or dummy.net_id naming
|
||||
if ok := parentExists(n.config.Parent); ok {
|
||||
// only delete the link if it is named the net_id
|
||||
if n.config.Parent == getDummyName(stringid.TruncateID(nid)) {
|
||||
err := delDummyLink(n.config.Parent)
|
||||
if err != nil {
|
||||
logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
|
||||
n.config.Parent, err)
|
||||
}
|
||||
} else {
|
||||
// only delete the link if it matches iface.vlan naming
|
||||
err := delVlanLink(n.config.Parent)
|
||||
if err != nil {
|
||||
logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
|
||||
n.config.Parent, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// delete the *network
|
||||
d.deleteNetwork(nid)
|
||||
// delete the network record from persistent cache
|
||||
err := d.storeDelete(n.config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting deleting id %s from datastore: %v", nid, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseNetworkOptions parse docker network options
|
||||
func parseNetworkOptions(id string, option options.Generic) (*configuration, error) {
|
||||
var (
|
||||
err error
|
||||
config = &configuration{}
|
||||
)
|
||||
// parse generic labels first
|
||||
if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
|
||||
if config, err = parseNetworkGenericOptions(genData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// setting the parent to "" will trigger an isolated network dummy parent link
|
||||
if _, ok := option[netlabel.Internal]; ok {
|
||||
config.Internal = true
|
||||
// empty --parent= and --internal are handled the same.
|
||||
config.Parent = ""
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// parseNetworkGenericOptions parse generic driver docker network options
|
||||
func parseNetworkGenericOptions(data interface{}) (*configuration, error) {
|
||||
var (
|
||||
err error
|
||||
config *configuration
|
||||
)
|
||||
switch opt := data.(type) {
|
||||
case *configuration:
|
||||
config = opt
|
||||
case map[string]string:
|
||||
config = &configuration{}
|
||||
err = config.fromOptions(opt)
|
||||
case options.Generic:
|
||||
var opaqueConfig interface{}
|
||||
if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
|
||||
config = opaqueConfig.(*configuration)
|
||||
}
|
||||
default:
|
||||
err = types.BadRequestErrorf("unrecognized network configuration format: %v", opt)
|
||||
}
|
||||
return config, err
|
||||
}
|
||||
|
||||
// fromOptions binds the generic options to networkConfiguration to cache
|
||||
func (config *configuration) fromOptions(labels map[string]string) error {
|
||||
for label, value := range labels {
|
||||
switch label {
|
||||
case parentOpt:
|
||||
// parse driver option '-o parent'
|
||||
config.Parent = value
|
||||
case driverModeOpt:
|
||||
// parse driver option '-o ipvlan_mode'
|
||||
config.IpvlanMode = value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// processIPAM parses v4 and v6 IP information and binds it to the network configuration
|
||||
func (config *configuration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
|
||||
if len(ipamV4Data) > 0 {
|
||||
for _, ipd := range ipamV4Data {
|
||||
s := &ipv4Subnet{
|
||||
SubnetIP: ipd.Pool.String(),
|
||||
GwIP: ipd.Gateway.String(),
|
||||
}
|
||||
config.Ipv4Subnets = append(config.Ipv4Subnets, s)
|
||||
}
|
||||
}
|
||||
if len(ipamV6Data) > 0 {
|
||||
for _, ipd := range ipamV6Data {
|
||||
s := &ipv6Subnet{
|
||||
SubnetIP: ipd.Pool.String(),
|
||||
GwIP: ipd.Gateway.String(),
|
||||
}
|
||||
config.Ipv6Subnets = append(config.Ipv6Subnets, s)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
204
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_setup.go
vendored
Normal file
204
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_setup.go
vendored
Normal file
|
@ -0,0 +1,204 @@
|
|||
package ipvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
const (
|
||||
dummyPrefix = "di-" // ipvlan prefix for dummy parent interface
|
||||
ipvlanKernelVer = 4 // minimum ipvlan kernel support
|
||||
ipvlanMajorVer = 2 // minimum ipvlan major kernel support
|
||||
)
|
||||
|
||||
// createIPVlan Create the ipvlan slave specifying the source name
|
||||
func createIPVlan(containerIfName, parent, ipvlanMode string) (string, error) {
|
||||
// Set the ipvlan mode. Default is bridge mode
|
||||
mode, err := setIPVlanMode(ipvlanMode)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unsupported %s ipvlan mode: %v", ipvlanMode, err)
|
||||
}
|
||||
// verify the Docker host interface acting as the macvlan parent iface exists
|
||||
if !parentExists(parent) {
|
||||
return "", fmt.Errorf("the requested parent interface %s was not found on the Docker host", parent)
|
||||
}
|
||||
// Get the link for the master index (Example: the docker host eth iface)
|
||||
parentLink, err := netlink.LinkByName(parent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", ipvlanType, parent, err)
|
||||
}
|
||||
// Create a ipvlan link
|
||||
ipvlan := &netlink.IPVlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: containerIfName,
|
||||
ParentIndex: parentLink.Attrs().Index,
|
||||
},
|
||||
Mode: mode,
|
||||
}
|
||||
if err := netlink.LinkAdd(ipvlan); err != nil {
|
||||
// If a user creates a macvlan and ipvlan on same parent, only one slave iface can be active at a time.
|
||||
return "", fmt.Errorf("failed to create the %s port: %v", ipvlanType, err)
|
||||
}
|
||||
|
||||
return ipvlan.Attrs().Name, nil
|
||||
}
|
||||
|
||||
// setIPVlanMode setter for one of the two ipvlan port types
|
||||
func setIPVlanMode(mode string) (netlink.IPVlanMode, error) {
|
||||
switch mode {
|
||||
case modeL2:
|
||||
return netlink.IPVLAN_MODE_L2, nil
|
||||
case modeL3:
|
||||
return netlink.IPVLAN_MODE_L3, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("Unknown ipvlan mode: %s", mode)
|
||||
}
|
||||
}
|
||||
|
||||
// parentExists check if the specified interface exists in the default namespace
|
||||
func parentExists(ifaceStr string) bool {
|
||||
_, err := netlink.LinkByName(ifaceStr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// createVlanLink parses sub-interfaces and vlan id for creation
|
||||
func createVlanLink(parentName string) error {
|
||||
if strings.Contains(parentName, ".") {
|
||||
parent, vidInt, err := parseVlan(parentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// VLAN identifier or VID is a 12-bit field specifying the VLAN to which the frame belongs
|
||||
if vidInt > 4094 || vidInt < 1 {
|
||||
return fmt.Errorf("vlan id must be between 1-4094, received: %d", vidInt)
|
||||
}
|
||||
// get the parent link to attach a vlan subinterface
|
||||
parentLink, err := netlink.LinkByName(parent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parent, err)
|
||||
}
|
||||
vlanLink := &netlink.Vlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: parentName,
|
||||
ParentIndex: parentLink.Attrs().Index,
|
||||
},
|
||||
VlanId: vidInt,
|
||||
}
|
||||
// create the subinterface
|
||||
if err := netlink.LinkAdd(vlanLink); err != nil {
|
||||
return fmt.Errorf("failed to create %s vlan link: %v", vlanLink.Name, err)
|
||||
}
|
||||
// Bring the new netlink iface up
|
||||
if err := netlink.LinkSetUp(vlanLink); err != nil {
|
||||
return fmt.Errorf("failed to enable %s the ipvlan parent link %v", vlanLink.Name, err)
|
||||
}
|
||||
logrus.Debugf("Added a vlan tagged netlink subinterface: %s with a vlan id: %d", parentName, vidInt)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("invalid subinterface vlan name %s, example formatting is eth0.10", parentName)
|
||||
}
|
||||
|
||||
// delVlanLink verifies only sub-interfaces with a vlan id get deleted
|
||||
func delVlanLink(linkName string) error {
|
||||
if strings.Contains(linkName, ".") {
|
||||
_, _, err := parseVlan(linkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// delete the vlan subinterface
|
||||
vlanLink, err := netlink.LinkByName(linkName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find interface %s on the Docker host : %v", linkName, err)
|
||||
}
|
||||
// verify a parent interface isn't being deleted
|
||||
if vlanLink.Attrs().ParentIndex == 0 {
|
||||
return fmt.Errorf("interface %s does not appear to be a slave device: %v", linkName, err)
|
||||
}
|
||||
// delete the ipvlan slave device
|
||||
if err := netlink.LinkDel(vlanLink); err != nil {
|
||||
return fmt.Errorf("failed to delete %s link: %v", linkName, err)
|
||||
}
|
||||
logrus.Debugf("Deleted a vlan tagged netlink subinterface: %s", linkName)
|
||||
}
|
||||
// if the subinterface doesn't parse to iface.vlan_id leave the interface in
|
||||
// place since it could be a user specified name not created by the driver.
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseVlan parses and verifies a slave interface name: -o parent=eth0.10
|
||||
func parseVlan(linkName string) (string, int, error) {
|
||||
// parse -o parent=eth0.10
|
||||
splitName := strings.Split(linkName, ".")
|
||||
if len(splitName) != 2 {
|
||||
return "", 0, fmt.Errorf("required interface name format is: name.vlan_id, ex. eth0.10 for vlan 10, instead received %s", linkName)
|
||||
}
|
||||
parent, vidStr := splitName[0], splitName[1]
|
||||
// validate type and convert vlan id to int
|
||||
vidInt, err := strconv.Atoi(vidStr)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("unable to parse a valid vlan id from: %s (ex. eth0.10 for vlan 10)", vidStr)
|
||||
}
|
||||
// Check if the interface exists
|
||||
if !parentExists(parent) {
|
||||
return "", 0, fmt.Errorf("-o parent interface does was not found on the host: %s", parent)
|
||||
}
|
||||
|
||||
return parent, vidInt, nil
|
||||
}
|
||||
|
||||
// createDummyLink creates a dummy0 parent link
|
||||
func createDummyLink(dummyName, truncNetID string) error {
|
||||
// create a parent interface since one was not specified
|
||||
parent := &netlink.Dummy{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: dummyName,
|
||||
},
|
||||
}
|
||||
if err := netlink.LinkAdd(parent); err != nil {
|
||||
return err
|
||||
}
|
||||
parentDummyLink, err := netlink.LinkByName(dummyName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", ipvlanType, dummyName, err)
|
||||
}
|
||||
// bring the new netlink iface up
|
||||
if err := netlink.LinkSetUp(parentDummyLink); err != nil {
|
||||
return fmt.Errorf("failed to enable %s the ipvlan parent link: %v", dummyName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// delDummyLink deletes the link type dummy used when -o parent is not passed
|
||||
func delDummyLink(linkName string) error {
|
||||
// delete the vlan subinterface
|
||||
dummyLink, err := netlink.LinkByName(linkName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find link %s on the Docker host : %v", linkName, err)
|
||||
}
|
||||
// verify a parent interface is being deleted
|
||||
if dummyLink.Attrs().ParentIndex != 0 {
|
||||
return fmt.Errorf("link %s is not a parent dummy interface", linkName)
|
||||
}
|
||||
// delete the ipvlan dummy device
|
||||
if err := netlink.LinkDel(dummyLink); err != nil {
|
||||
return fmt.Errorf("failed to delete the dummy %s link: %v", linkName, err)
|
||||
}
|
||||
logrus.Debugf("Deleted a dummy parent link: %s", linkName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDummyName returns the name of a dummy parent with truncated net ID and driver prefix
|
||||
func getDummyName(netID string) string {
|
||||
return fmt.Sprintf("%s%s", dummyPrefix, netID)
|
||||
}
|
115
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_state.go
vendored
Normal file
115
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_state.go
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
package ipvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
func (d *driver) network(nid string) *network {
|
||||
d.Lock()
|
||||
n, ok := d.networks[nid]
|
||||
d.Unlock()
|
||||
if !ok {
|
||||
logrus.Errorf("network id %s not found", nid)
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (d *driver) addNetwork(n *network) {
|
||||
d.Lock()
|
||||
d.networks[n.id] = n
|
||||
d.Unlock()
|
||||
}
|
||||
|
||||
func (d *driver) deleteNetwork(nid string) {
|
||||
d.Lock()
|
||||
delete(d.networks, nid)
|
||||
d.Unlock()
|
||||
}
|
||||
|
||||
// getNetworks Safely returns a slice of existng networks
|
||||
func (d *driver) getNetworks() []*network {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
ls := make([]*network, 0, len(d.networks))
|
||||
for _, nw := range d.networks {
|
||||
ls = append(ls, nw)
|
||||
}
|
||||
|
||||
return ls
|
||||
}
|
||||
|
||||
func (n *network) endpoint(eid string) *endpoint {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return n.endpoints[eid]
|
||||
}
|
||||
|
||||
func (n *network) addEndpoint(ep *endpoint) {
|
||||
n.Lock()
|
||||
n.endpoints[ep.id] = ep
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (n *network) deleteEndpoint(eid string) {
|
||||
n.Lock()
|
||||
delete(n.endpoints, eid)
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (n *network) getEndpoint(eid string) (*endpoint, error) {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
if eid == "" {
|
||||
return nil, fmt.Errorf("endpoint id %s not found", eid)
|
||||
}
|
||||
if ep, ok := n.endpoints[eid]; ok {
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func validateID(nid, eid string) error {
|
||||
if nid == "" {
|
||||
return fmt.Errorf("invalid network id")
|
||||
}
|
||||
if eid == "" {
|
||||
return fmt.Errorf("invalid endpoint id")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) sandbox() osl.Sandbox {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return n.sbox
|
||||
}
|
||||
|
||||
func (n *network) setSandbox(sbox osl.Sandbox) {
|
||||
n.Lock()
|
||||
n.sbox = sbox
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (d *driver) getNetwork(id string) (*network, error) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
if id == "" {
|
||||
return nil, types.BadRequestErrorf("invalid network id: %s", id)
|
||||
}
|
||||
|
||||
if nw, ok := d.networks[id]; ok {
|
||||
return nw, nil
|
||||
}
|
||||
|
||||
return nil, types.NotFoundErrorf("network not found: %s", id)
|
||||
}
|
217
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go
vendored
Normal file
217
vendor/src/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
package ipvlan
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const ipvlanPrefix = "ipvlan" // prefix used for persistent driver storage
|
||||
|
||||
// networkConfiguration for this driver's network specific configuration
|
||||
type configuration struct {
|
||||
ID string
|
||||
Mtu int
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
Internal bool
|
||||
Parent string
|
||||
IpvlanMode string
|
||||
CreatedSlaveLink bool
|
||||
Ipv4Subnets []*ipv4Subnet
|
||||
Ipv6Subnets []*ipv6Subnet
|
||||
}
|
||||
|
||||
type ipv4Subnet struct {
|
||||
SubnetIP string
|
||||
GwIP string
|
||||
}
|
||||
|
||||
type ipv6Subnet struct {
|
||||
SubnetIP string
|
||||
GwIP string
|
||||
}
|
||||
|
||||
// initStore drivers are responsible for caching their own persistent state
|
||||
func (d *driver) initStore(option map[string]interface{}) error {
|
||||
if data, ok := option[netlabel.LocalKVClient]; ok {
|
||||
var err error
|
||||
dsc, ok := data.(discoverapi.DatastoreConfigData)
|
||||
if !ok {
|
||||
return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
|
||||
}
|
||||
d.store, err = datastore.NewDataStoreFromConfig(dsc)
|
||||
if err != nil {
|
||||
return types.InternalErrorf("ipvlan driver failed to initialize data store: %v", err)
|
||||
}
|
||||
|
||||
return d.populateNetworks()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populateNetworks is invoked at driver init to recreate persistently stored networks
|
||||
func (d *driver) populateNetworks() error {
|
||||
kvol, err := d.store.List(datastore.Key(ipvlanPrefix), &configuration{})
|
||||
if err != nil && err != datastore.ErrKeyNotFound && err != boltdb.ErrBoltBucketNotFound {
|
||||
return fmt.Errorf("failed to get ipvlan network configurations from store: %v", err)
|
||||
}
|
||||
// If empty it simply means no ipvlan networks have been created yet
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
return nil
|
||||
}
|
||||
for _, kvo := range kvol {
|
||||
config := kvo.(*configuration)
|
||||
if err = d.createNetwork(config); err != nil {
|
||||
logrus.Warnf("could not create ipvlan network for id %s from persistent state", config.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// storeUpdate used to update persistent ipvlan network records as they are created
|
||||
func (d *driver) storeUpdate(kvObject datastore.KVObject) error {
|
||||
if d.store == nil {
|
||||
logrus.Warnf("ipvlan store not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
}
|
||||
if err := d.store.PutObjectAtomic(kvObject); err != nil {
|
||||
return fmt.Errorf("failed to update ipvlan store for object type %T: %v", kvObject, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// storeDelete used to delete ipvlan network records from persistent cache as they are deleted
|
||||
func (d *driver) storeDelete(kvObject datastore.KVObject) error {
|
||||
if d.store == nil {
|
||||
logrus.Debugf("ipvlan store not initialized. kv object %s is not deleted from store", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
}
|
||||
retry:
|
||||
if err := d.store.DeleteObjectAtomic(kvObject); err != nil {
|
||||
if err == datastore.ErrKeyModified {
|
||||
if err := d.store.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil {
|
||||
return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err)
|
||||
}
|
||||
goto retry
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *configuration) MarshalJSON() ([]byte, error) {
|
||||
nMap := make(map[string]interface{})
|
||||
nMap["ID"] = config.ID
|
||||
nMap["Mtu"] = config.Mtu
|
||||
nMap["Parent"] = config.Parent
|
||||
nMap["IpvlanMode"] = config.IpvlanMode
|
||||
nMap["Internal"] = config.Internal
|
||||
nMap["CreatedSubIface"] = config.CreatedSlaveLink
|
||||
if len(config.Ipv4Subnets) > 0 {
|
||||
iis, err := json.Marshal(config.Ipv4Subnets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nMap["Ipv4Subnets"] = string(iis)
|
||||
}
|
||||
if len(config.Ipv6Subnets) > 0 {
|
||||
iis, err := json.Marshal(config.Ipv6Subnets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nMap["Ipv6Subnets"] = string(iis)
|
||||
}
|
||||
|
||||
return json.Marshal(nMap)
|
||||
}
|
||||
|
||||
func (config *configuration) UnmarshalJSON(b []byte) error {
|
||||
var (
|
||||
err error
|
||||
nMap map[string]interface{}
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(b, &nMap); err != nil {
|
||||
return err
|
||||
}
|
||||
config.ID = nMap["ID"].(string)
|
||||
config.Mtu = int(nMap["Mtu"].(float64))
|
||||
config.Parent = nMap["Parent"].(string)
|
||||
config.IpvlanMode = nMap["IpvlanMode"].(string)
|
||||
config.Internal = nMap["Internal"].(bool)
|
||||
config.CreatedSlaveLink = nMap["CreatedSubIface"].(bool)
|
||||
if v, ok := nMap["Ipv4Subnets"]; ok {
|
||||
if err := json.Unmarshal([]byte(v.(string)), &config.Ipv4Subnets); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v, ok := nMap["Ipv6Subnets"]; ok {
|
||||
if err := json.Unmarshal([]byte(v.(string)), &config.Ipv6Subnets); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *configuration) Key() []string {
|
||||
return []string{ipvlanPrefix, config.ID}
|
||||
}
|
||||
|
||||
func (config *configuration) KeyPrefix() []string {
|
||||
return []string{ipvlanPrefix}
|
||||
}
|
||||
|
||||
func (config *configuration) Value() []byte {
|
||||
b, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (config *configuration) SetValue(value []byte) error {
|
||||
return json.Unmarshal(value, config)
|
||||
}
|
||||
|
||||
func (config *configuration) Index() uint64 {
|
||||
return config.dbIndex
|
||||
}
|
||||
|
||||
func (config *configuration) SetIndex(index uint64) {
|
||||
config.dbIndex = index
|
||||
config.dbExists = true
|
||||
}
|
||||
|
||||
func (config *configuration) Exists() bool {
|
||||
return config.dbExists
|
||||
}
|
||||
|
||||
func (config *configuration) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (config *configuration) New() datastore.KVObject {
|
||||
return &configuration{}
|
||||
}
|
||||
|
||||
func (config *configuration) CopyTo(o datastore.KVObject) error {
|
||||
dstNcfg := o.(*configuration)
|
||||
*dstNcfg = *config
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *configuration) DataScope() string {
|
||||
return datastore.LocalScope
|
||||
}
|
93
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan.go
vendored
Normal file
93
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan.go
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
package macvlan
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
)
|
||||
|
||||
const (
|
||||
vethLen = 7
|
||||
containerVethPrefix = "eth"
|
||||
vethPrefix = "veth"
|
||||
macvlanType = "macvlan" // driver type name
|
||||
modePrivate = "private" // macvlan mode private
|
||||
modeVepa = "vepa" // macvlan mode vepa
|
||||
modeBridge = "bridge" // macvlan mode bridge
|
||||
modePassthru = "passthru" // macvlan mode passthrough
|
||||
parentOpt = "parent" // parent interface -o parent
|
||||
modeOpt = "_mode" // macvlan mode ux opt suffix
|
||||
)
|
||||
|
||||
var driverModeOpt = macvlanType + modeOpt // mode --option macvlan_mode
|
||||
|
||||
type endpointTable map[string]*endpoint
|
||||
|
||||
type networkTable map[string]*network
|
||||
|
||||
type driver struct {
|
||||
networks networkTable
|
||||
sync.Once
|
||||
sync.Mutex
|
||||
store datastore.DataStore
|
||||
}
|
||||
|
||||
type endpoint struct {
|
||||
id string
|
||||
mac net.HardwareAddr
|
||||
addr *net.IPNet
|
||||
addrv6 *net.IPNet
|
||||
srcName string
|
||||
}
|
||||
|
||||
type network struct {
|
||||
id string
|
||||
sbox osl.Sandbox
|
||||
endpoints endpointTable
|
||||
driver *driver
|
||||
config *configuration
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// Init initializes and registers the libnetwork macvlan driver
|
||||
func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
||||
c := driverapi.Capability{
|
||||
DataScope: datastore.LocalScope,
|
||||
}
|
||||
d := &driver{
|
||||
networks: networkTable{},
|
||||
}
|
||||
d.initStore(config)
|
||||
|
||||
return dc.RegisterDriver(macvlanType, d, c)
|
||||
}
|
||||
|
||||
func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
||||
return make(map[string]interface{}, 0), nil
|
||||
}
|
||||
|
||||
func (d *driver) Type() string {
|
||||
return macvlanType
|
||||
}
|
||||
|
||||
func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverNew is a notification for a new discovery event
|
||||
func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverDelete is a notification for a discovery delete event
|
||||
func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
82
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go
vendored
Normal file
82
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
package macvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// CreateEndpoint assigns the mac, ip and endpoint id for the new container
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
|
||||
epOptions map[string]interface{}) error {
|
||||
defer osl.InitOSContext()()
|
||||
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("network id %q not found", nid)
|
||||
}
|
||||
ep := &endpoint{
|
||||
id: eid,
|
||||
addr: ifInfo.Address(),
|
||||
addrv6: ifInfo.AddressIPv6(),
|
||||
mac: ifInfo.MacAddress(),
|
||||
}
|
||||
if ep.addr == nil {
|
||||
return fmt.Errorf("create endpoint was not passed an IP address")
|
||||
}
|
||||
if ep.mac == nil {
|
||||
ep.mac = netutils.GenerateMACFromIP(ep.addr.IP)
|
||||
if err := ifInfo.SetMacAddress(ep.mac); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// disallow portmapping -p
|
||||
if opt, ok := epOptions[netlabel.PortMap]; ok {
|
||||
if _, ok := opt.([]types.PortBinding); ok {
|
||||
if len(opt.([]types.PortBinding)) > 0 {
|
||||
logrus.Warnf("%s driver does not support port mappings", macvlanType)
|
||||
}
|
||||
}
|
||||
}
|
||||
// disallow port exposure --expose
|
||||
if opt, ok := epOptions[netlabel.ExposedPorts]; ok {
|
||||
if _, ok := opt.([]types.TransportPort); ok {
|
||||
if len(opt.([]types.TransportPort)) > 0 {
|
||||
logrus.Warnf("%s driver does not support port exposures", macvlanType)
|
||||
}
|
||||
}
|
||||
}
|
||||
n.addEndpoint(ep)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpoint remove the endpoint and associated netlink interface
|
||||
func (d *driver) DeleteEndpoint(nid, eid string) error {
|
||||
defer osl.InitOSContext()()
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
return err
|
||||
}
|
||||
n := d.network(nid)
|
||||
if n == nil {
|
||||
return fmt.Errorf("network id %q not found", nid)
|
||||
}
|
||||
ep := n.endpoint(eid)
|
||||
if ep == nil {
|
||||
return fmt.Errorf("endpoint id %q not found", eid)
|
||||
}
|
||||
if link, err := netlink.LinkByName(ep.srcName); err == nil {
|
||||
netlink.LinkDel(link)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
141
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go
vendored
Normal file
141
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
package macvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
)
|
||||
|
||||
// Join method is invoked when a Sandbox is attached to an endpoint.
|
||||
func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
||||
defer osl.InitOSContext()()
|
||||
n, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint := n.endpoint(eid)
|
||||
if endpoint == nil {
|
||||
return fmt.Errorf("could not find endpoint with id %s", eid)
|
||||
}
|
||||
// generate a name for the iface that will be renamed to eth0 in the sbox
|
||||
containerIfName, err := netutils.GenerateIfaceName(vethPrefix, vethLen)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating an interface name: %s", err)
|
||||
}
|
||||
// create the netlink macvlan interface
|
||||
vethName, err := createMacVlan(containerIfName, n.config.Parent, n.config.MacvlanMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// bind the generated iface name to the endpoint
|
||||
endpoint.srcName = vethName
|
||||
ep := n.endpoint(eid)
|
||||
if ep == nil {
|
||||
return fmt.Errorf("could not find endpoint with id %s", eid)
|
||||
}
|
||||
// parse and match the endpoint address with the available v4 subnets
|
||||
if len(n.config.Ipv4Subnets) > 0 {
|
||||
s := n.getSubnetforIPv4(ep.addr)
|
||||
if s == nil {
|
||||
return fmt.Errorf("could not find a valid ipv4 subnet for endpoint %s", eid)
|
||||
}
|
||||
v4gw, _, err := net.ParseCIDR(s.GwIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gatway %s is not a valid ipv4 address: %v", s.GwIP, err)
|
||||
}
|
||||
err = jinfo.SetGateway(v4gw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("Macvlan Endpoint Joined with IPv4_Addr: %s, Gateway: %s, MacVlan_Mode: %s, Parent: %s",
|
||||
ep.addr.IP.String(), v4gw.String(), n.config.MacvlanMode, n.config.Parent)
|
||||
}
|
||||
// parse and match the endpoint address with the available v6 subnets
|
||||
if len(n.config.Ipv6Subnets) > 0 {
|
||||
s := n.getSubnetforIPv6(ep.addrv6)
|
||||
if s == nil {
|
||||
return fmt.Errorf("could not find a valid ipv6 subnet for endpoint %s", eid)
|
||||
}
|
||||
v6gw, _, err := net.ParseCIDR(s.GwIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gatway %s is not a valid ipv6 address: %v", s.GwIP, err)
|
||||
}
|
||||
err = jinfo.SetGatewayIPv6(v6gw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("Macvlan Endpoint Joined with IPv6_Addr: %s Gateway: %s MacVlan_Mode: %s, Parent: %s",
|
||||
ep.addrv6.IP.String(), v6gw.String(), n.config.MacvlanMode, n.config.Parent)
|
||||
}
|
||||
iNames := jinfo.InterfaceName()
|
||||
err = iNames.SetNames(vethName, containerVethPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Leave method is invoked when a Sandbox detaches from an endpoint.
|
||||
func (d *driver) Leave(nid, eid string) error {
|
||||
defer osl.InitOSContext()()
|
||||
network, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint, err := network.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if endpoint == nil {
|
||||
return fmt.Errorf("could not find endpoint with id %s", eid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSubnetforIP returns the ipv4 subnet to which the given IP belongs
|
||||
func (n *network) getSubnetforIPv4(ip *net.IPNet) *ipv4Subnet {
|
||||
for _, s := range n.config.Ipv4Subnets {
|
||||
_, snet, err := net.ParseCIDR(s.SubnetIP)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// first check if the mask lengths are the same
|
||||
i, _ := snet.Mask.Size()
|
||||
j, _ := ip.Mask.Size()
|
||||
if i != j {
|
||||
continue
|
||||
}
|
||||
if snet.Contains(ip.IP) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSubnetforIPv6 returns the ipv6 subnet to which the given IP belongs
|
||||
func (n *network) getSubnetforIPv6(ip *net.IPNet) *ipv6Subnet {
|
||||
for _, s := range n.config.Ipv6Subnets {
|
||||
_, snet, err := net.ParseCIDR(s.SubnetIP)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// first check if the mask lengths are the same
|
||||
i, _ := snet.Mask.Size()
|
||||
j, _ := ip.Mask.Size()
|
||||
if i != j {
|
||||
continue
|
||||
}
|
||||
if snet.Contains(ip.IP) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
244
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go
vendored
Normal file
244
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go
vendored
Normal file
|
@ -0,0 +1,244 @@
|
|||
package macvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// CreateNetwork the network for the specified driver type
|
||||
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
defer osl.InitOSContext()()
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check kernel version for %s driver support: %v", macvlanType, err)
|
||||
}
|
||||
// ensure Kernel version is >= v3.9 for macvlan support
|
||||
if kv.Kernel < macvlanKernelVer || (kv.Kernel == macvlanKernelVer && kv.Major < macvlanMajorVer) {
|
||||
return fmt.Errorf("kernel version failed to meet the minimum macvlan kernel requirement of %d.%d, found %d.%d.%d",
|
||||
macvlanKernelVer, macvlanMajorVer, kv.Kernel, kv.Major, kv.Minor)
|
||||
}
|
||||
// parse and validate the config and bind to networkConfiguration
|
||||
config, err := parseNetworkOptions(nid, option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ID = nid
|
||||
err = config.processIPAM(nid, ipV4Data, ipV6Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// verify the macvlan mode from -o macvlan_mode option
|
||||
switch config.MacvlanMode {
|
||||
case "", modeBridge:
|
||||
// default to macvlan bridge mode if -o macvlan_mode is empty
|
||||
config.MacvlanMode = modeBridge
|
||||
case modeOpt:
|
||||
config.MacvlanMode = modeOpt
|
||||
case modePassthru:
|
||||
config.MacvlanMode = modePassthru
|
||||
case modeVepa:
|
||||
config.MacvlanMode = modeVepa
|
||||
default:
|
||||
return fmt.Errorf("requested macvlan mode '%s' is not valid, 'bridge' mode is the macvlan driver default", config.MacvlanMode)
|
||||
}
|
||||
// loopback is not a valid parent link
|
||||
if config.Parent == "lo" {
|
||||
return fmt.Errorf("loopback interface is not a valid %s parent link", macvlanType)
|
||||
}
|
||||
// if parent interface not specified, create a dummy type link to use named dummy+net_id
|
||||
if config.Parent == "" {
|
||||
config.Parent = getDummyName(stringid.TruncateID(config.ID))
|
||||
// empty parent and --internal are handled the same. Set here to update k/v
|
||||
config.Internal = true
|
||||
}
|
||||
err = d.createNetwork(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// update persistent db, rollback on fail
|
||||
err = d.storeUpdate(config)
|
||||
if err != nil {
|
||||
d.deleteNetwork(config.ID)
|
||||
logrus.Debugf("encoutered an error rolling back a network create for %s : %v", config.ID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createNetwork is used by new network callbacks and persistent network cache
|
||||
func (d *driver) createNetwork(config *configuration) error {
|
||||
networkList := d.getNetworks()
|
||||
for _, nw := range networkList {
|
||||
if config.Parent == nw.config.Parent {
|
||||
return fmt.Errorf("network %s is already using parent interface %s",
|
||||
getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
|
||||
}
|
||||
}
|
||||
if !parentExists(config.Parent) {
|
||||
// if the --internal flag is set, create a dummy link
|
||||
if config.Internal {
|
||||
err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.CreatedSlaveLink = true
|
||||
// notify the user in logs they have limited comunicatins
|
||||
if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
|
||||
logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
|
||||
config.Parent)
|
||||
}
|
||||
} else {
|
||||
// if the subinterface parent_iface.vlan_id checks do not pass, return err.
|
||||
// a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
|
||||
err := createVlanLink(config.Parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if driver created the networks slave link, record it for future deletion
|
||||
config.CreatedSlaveLink = true
|
||||
}
|
||||
}
|
||||
n := &network{
|
||||
id: config.ID,
|
||||
driver: d,
|
||||
endpoints: endpointTable{},
|
||||
config: config,
|
||||
}
|
||||
// add the *network
|
||||
d.addNetwork(n)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNetwork the network for the specified driver type
|
||||
func (d *driver) DeleteNetwork(nid string) error {
|
||||
defer osl.InitOSContext()()
|
||||
n := d.network(nid)
|
||||
if n == nil {
|
||||
return fmt.Errorf("network id %s not found", nid)
|
||||
}
|
||||
// if the driver created the slave interface, delete it, otherwise leave it
|
||||
if ok := n.config.CreatedSlaveLink; ok {
|
||||
// if the interface exists, only delete if it matches iface.vlan or dummy.net_id naming
|
||||
if ok := parentExists(n.config.Parent); ok {
|
||||
// only delete the link if it is named the net_id
|
||||
if n.config.Parent == getDummyName(stringid.TruncateID(nid)) {
|
||||
err := delDummyLink(n.config.Parent)
|
||||
if err != nil {
|
||||
logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
|
||||
n.config.Parent, err)
|
||||
}
|
||||
} else {
|
||||
// only delete the link if it matches iface.vlan naming
|
||||
err := delVlanLink(n.config.Parent)
|
||||
if err != nil {
|
||||
logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
|
||||
n.config.Parent, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// delete the *network
|
||||
d.deleteNetwork(nid)
|
||||
// delete the network record from persistent cache
|
||||
err := d.storeDelete(n.config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting deleting id %s from datastore: %v", nid, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseNetworkOptions parse docker network options
|
||||
func parseNetworkOptions(id string, option options.Generic) (*configuration, error) {
|
||||
var (
|
||||
err error
|
||||
config = &configuration{}
|
||||
)
|
||||
// parse generic labels first
|
||||
if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
|
||||
if config, err = parseNetworkGenericOptions(genData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// setting the parent to "" will trigger an isolated network dummy parent link
|
||||
if _, ok := option[netlabel.Internal]; ok {
|
||||
config.Internal = true
|
||||
// empty --parent= and --internal are handled the same.
|
||||
config.Parent = ""
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// parseNetworkGenericOptions parse generic driver docker network options
|
||||
func parseNetworkGenericOptions(data interface{}) (*configuration, error) {
|
||||
var (
|
||||
err error
|
||||
config *configuration
|
||||
)
|
||||
switch opt := data.(type) {
|
||||
case *configuration:
|
||||
config = opt
|
||||
case map[string]string:
|
||||
config = &configuration{}
|
||||
err = config.fromOptions(opt)
|
||||
case options.Generic:
|
||||
var opaqueConfig interface{}
|
||||
if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
|
||||
config = opaqueConfig.(*configuration)
|
||||
}
|
||||
default:
|
||||
err = types.BadRequestErrorf("unrecognized network configuration format: %v", opt)
|
||||
}
|
||||
|
||||
return config, err
|
||||
}
|
||||
|
||||
// fromOptions binds the generic options to networkConfiguration to cache
|
||||
func (config *configuration) fromOptions(labels map[string]string) error {
|
||||
for label, value := range labels {
|
||||
switch label {
|
||||
case parentOpt:
|
||||
// parse driver option '-o parent'
|
||||
config.Parent = value
|
||||
case driverModeOpt:
|
||||
// parse driver option '-o macvlan_mode'
|
||||
config.MacvlanMode = value
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processIPAM parses v4 and v6 IP information and binds it to the network configuration
|
||||
func (config *configuration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
|
||||
if len(ipamV4Data) > 0 {
|
||||
for _, ipd := range ipamV4Data {
|
||||
s := &ipv4Subnet{
|
||||
SubnetIP: ipd.Pool.String(),
|
||||
GwIP: ipd.Gateway.String(),
|
||||
}
|
||||
config.Ipv4Subnets = append(config.Ipv4Subnets, s)
|
||||
}
|
||||
}
|
||||
if len(ipamV6Data) > 0 {
|
||||
for _, ipd := range ipamV6Data {
|
||||
s := &ipv6Subnet{
|
||||
SubnetIP: ipd.Pool.String(),
|
||||
GwIP: ipd.Gateway.String(),
|
||||
}
|
||||
config.Ipv6Subnets = append(config.Ipv6Subnets, s)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
208
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_setup.go
vendored
Normal file
208
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_setup.go
vendored
Normal file
|
@ -0,0 +1,208 @@
|
|||
package macvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
const (
|
||||
dummyPrefix = "dm-" // macvlan prefix for dummy parent interface
|
||||
macvlanKernelVer = 3 // minimum macvlan kernel support
|
||||
macvlanMajorVer = 9 // minimum macvlan major kernel support
|
||||
)
|
||||
|
||||
// Create the macvlan slave specifying the source name
|
||||
func createMacVlan(containerIfName, parent, macvlanMode string) (string, error) {
|
||||
// Set the macvlan mode. Default is bridge mode
|
||||
mode, err := setMacVlanMode(macvlanMode)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unsupported %s macvlan mode: %v", macvlanMode, err)
|
||||
}
|
||||
// verify the Docker host interface acting as the macvlan parent iface exists
|
||||
if !parentExists(parent) {
|
||||
return "", fmt.Errorf("the requested parent interface %s was not found on the Docker host", parent)
|
||||
}
|
||||
// Get the link for the master index (Example: the docker host eth iface)
|
||||
parentLink, err := netlink.LinkByName(parent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", macvlanType, parent, err)
|
||||
}
|
||||
// Create a macvlan link
|
||||
macvlan := &netlink.Macvlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: containerIfName,
|
||||
ParentIndex: parentLink.Attrs().Index,
|
||||
},
|
||||
Mode: mode,
|
||||
}
|
||||
if err := netlink.LinkAdd(macvlan); err != nil {
|
||||
// If a user creates a macvlan and ipvlan on same parent, only one slave iface can be active at a time.
|
||||
return "", fmt.Errorf("failed to create the %s port: %v", macvlanType, err)
|
||||
}
|
||||
|
||||
return macvlan.Attrs().Name, nil
|
||||
}
|
||||
|
||||
// setMacVlanMode setter for one of the four macvlan port types
|
||||
func setMacVlanMode(mode string) (netlink.MacvlanMode, error) {
|
||||
switch mode {
|
||||
case modePrivate:
|
||||
return netlink.MACVLAN_MODE_PRIVATE, nil
|
||||
case modeVepa:
|
||||
return netlink.MACVLAN_MODE_VEPA, nil
|
||||
case modeBridge:
|
||||
return netlink.MACVLAN_MODE_BRIDGE, nil
|
||||
case modePassthru:
|
||||
return netlink.MACVLAN_MODE_PASSTHRU, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown macvlan mode: %s", mode)
|
||||
}
|
||||
}
|
||||
|
||||
// parentExists check if the specified interface exists in the default namespace
|
||||
func parentExists(ifaceStr string) bool {
|
||||
_, err := netlink.LinkByName(ifaceStr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// createVlanLink parses sub-interfaces and vlan id for creation
|
||||
func createVlanLink(parentName string) error {
|
||||
if strings.Contains(parentName, ".") {
|
||||
parent, vidInt, err := parseVlan(parentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// VLAN identifier or VID is a 12-bit field specifying the VLAN to which the frame belongs
|
||||
if vidInt > 4094 || vidInt < 1 {
|
||||
return fmt.Errorf("vlan id must be between 1-4094, received: %d", vidInt)
|
||||
}
|
||||
// get the parent link to attach a vlan subinterface
|
||||
parentLink, err := netlink.LinkByName(parent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parent, err)
|
||||
}
|
||||
vlanLink := &netlink.Vlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: parentName,
|
||||
ParentIndex: parentLink.Attrs().Index,
|
||||
},
|
||||
VlanId: vidInt,
|
||||
}
|
||||
// create the subinterface
|
||||
if err := netlink.LinkAdd(vlanLink); err != nil {
|
||||
return fmt.Errorf("failed to create %s vlan link: %v", vlanLink.Name, err)
|
||||
}
|
||||
// Bring the new netlink iface up
|
||||
if err := netlink.LinkSetUp(vlanLink); err != nil {
|
||||
return fmt.Errorf("failed to enable %s the macvlan parent link %v", vlanLink.Name, err)
|
||||
}
|
||||
logrus.Debugf("Added a vlan tagged netlink subinterface: %s with a vlan id: %d", parentName, vidInt)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("invalid subinterface vlan name %s, example formatting is eth0.10", parentName)
|
||||
}
|
||||
|
||||
// delVlanLink verifies only sub-interfaces with a vlan id get deleted
|
||||
func delVlanLink(linkName string) error {
|
||||
if strings.Contains(linkName, ".") {
|
||||
_, _, err := parseVlan(linkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// delete the vlan subinterface
|
||||
vlanLink, err := netlink.LinkByName(linkName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find interface %s on the Docker host : %v", linkName, err)
|
||||
}
|
||||
// verify a parent interface isn't being deleted
|
||||
if vlanLink.Attrs().ParentIndex == 0 {
|
||||
return fmt.Errorf("interface %s does not appear to be a slave device: %v", linkName, err)
|
||||
}
|
||||
// delete the macvlan slave device
|
||||
if err := netlink.LinkDel(vlanLink); err != nil {
|
||||
return fmt.Errorf("failed to delete %s link: %v", linkName, err)
|
||||
}
|
||||
logrus.Debugf("Deleted a vlan tagged netlink subinterface: %s", linkName)
|
||||
}
|
||||
// if the subinterface doesn't parse to iface.vlan_id leave the interface in
|
||||
// place since it could be a user specified name not created by the driver.
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseVlan parses and verifies a slave interface name: -o parent=eth0.10
|
||||
func parseVlan(linkName string) (string, int, error) {
|
||||
// parse -o parent=eth0.10
|
||||
splitName := strings.Split(linkName, ".")
|
||||
if len(splitName) != 2 {
|
||||
return "", 0, fmt.Errorf("required interface name format is: name.vlan_id, ex. eth0.10 for vlan 10, instead received %s", linkName)
|
||||
}
|
||||
parent, vidStr := splitName[0], splitName[1]
|
||||
// validate type and convert vlan id to int
|
||||
vidInt, err := strconv.Atoi(vidStr)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("unable to parse a valid vlan id from: %s (ex. eth0.10 for vlan 10)", vidStr)
|
||||
}
|
||||
// Check if the interface exists
|
||||
if !parentExists(parent) {
|
||||
return "", 0, fmt.Errorf("-o parent interface does was not found on the host: %s", parent)
|
||||
}
|
||||
|
||||
return parent, vidInt, nil
|
||||
}
|
||||
|
||||
// createDummyLink creates a dummy0 parent link
|
||||
func createDummyLink(dummyName, truncNetID string) error {
|
||||
// create a parent interface since one was not specified
|
||||
parent := &netlink.Dummy{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: dummyName,
|
||||
},
|
||||
}
|
||||
if err := netlink.LinkAdd(parent); err != nil {
|
||||
return err
|
||||
}
|
||||
parentDummyLink, err := netlink.LinkByName(dummyName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", macvlanType, dummyName, err)
|
||||
}
|
||||
// bring the new netlink iface up
|
||||
if err := netlink.LinkSetUp(parentDummyLink); err != nil {
|
||||
return fmt.Errorf("failed to enable %s the macvlan parent link: %v", dummyName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// delDummyLink deletes the link type dummy used when -o parent is not passed
|
||||
func delDummyLink(linkName string) error {
|
||||
// delete the vlan subinterface
|
||||
dummyLink, err := netlink.LinkByName(linkName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find link %s on the Docker host : %v", linkName, err)
|
||||
}
|
||||
// verify a parent interface is being deleted
|
||||
if dummyLink.Attrs().ParentIndex != 0 {
|
||||
return fmt.Errorf("link %s is not a parent dummy interface", linkName)
|
||||
}
|
||||
// delete the macvlan dummy device
|
||||
if err := netlink.LinkDel(dummyLink); err != nil {
|
||||
return fmt.Errorf("failed to delete the dummy %s link: %v", linkName, err)
|
||||
}
|
||||
logrus.Debugf("Deleted a dummy parent link: %s", linkName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDummyName returns the name of a dummy parent with truncated net ID and driver prefix
|
||||
func getDummyName(netID string) string {
|
||||
return fmt.Sprintf("%s%s", dummyPrefix, netID)
|
||||
}
|
113
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_state.go
vendored
Normal file
113
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_state.go
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
package macvlan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
func (d *driver) network(nid string) *network {
|
||||
d.Lock()
|
||||
n, ok := d.networks[nid]
|
||||
d.Unlock()
|
||||
if !ok {
|
||||
logrus.Errorf("network id %s not found", nid)
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (d *driver) addNetwork(n *network) {
|
||||
d.Lock()
|
||||
d.networks[n.id] = n
|
||||
d.Unlock()
|
||||
}
|
||||
|
||||
func (d *driver) deleteNetwork(nid string) {
|
||||
d.Lock()
|
||||
delete(d.networks, nid)
|
||||
d.Unlock()
|
||||
}
|
||||
|
||||
// getNetworks Safely returns a slice of existng networks
|
||||
func (d *driver) getNetworks() []*network {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
ls := make([]*network, 0, len(d.networks))
|
||||
for _, nw := range d.networks {
|
||||
ls = append(ls, nw)
|
||||
}
|
||||
|
||||
return ls
|
||||
}
|
||||
|
||||
func (n *network) endpoint(eid string) *endpoint {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return n.endpoints[eid]
|
||||
}
|
||||
|
||||
func (n *network) addEndpoint(ep *endpoint) {
|
||||
n.Lock()
|
||||
n.endpoints[ep.id] = ep
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (n *network) deleteEndpoint(eid string) {
|
||||
n.Lock()
|
||||
delete(n.endpoints, eid)
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (n *network) getEndpoint(eid string) (*endpoint, error) {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
if eid == "" {
|
||||
return nil, fmt.Errorf("endpoint id %s not found", eid)
|
||||
}
|
||||
if ep, ok := n.endpoints[eid]; ok {
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func validateID(nid, eid string) error {
|
||||
if nid == "" {
|
||||
return fmt.Errorf("invalid network id")
|
||||
}
|
||||
if eid == "" {
|
||||
return fmt.Errorf("invalid endpoint id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) sandbox() osl.Sandbox {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return n.sbox
|
||||
}
|
||||
|
||||
func (n *network) setSandbox(sbox osl.Sandbox) {
|
||||
n.Lock()
|
||||
n.sbox = sbox
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (d *driver) getNetwork(id string) (*network, error) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
if id == "" {
|
||||
return nil, types.BadRequestErrorf("invalid network id: %s", id)
|
||||
}
|
||||
if nw, ok := d.networks[id]; ok {
|
||||
return nw, nil
|
||||
}
|
||||
|
||||
return nil, types.NotFoundErrorf("network not found: %s", id)
|
||||
}
|
219
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go
vendored
Normal file
219
vendor/src/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go
vendored
Normal file
|
@ -0,0 +1,219 @@
|
|||
package macvlan
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const macvlanPrefix = "macvlan" // prefix used for persistent driver storage
|
||||
|
||||
// networkConfiguration for this driver's network specific configuration
|
||||
type configuration struct {
|
||||
ID string
|
||||
Mtu int
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
Internal bool
|
||||
Parent string
|
||||
MacvlanMode string
|
||||
CreatedSlaveLink bool
|
||||
Ipv4Subnets []*ipv4Subnet
|
||||
Ipv6Subnets []*ipv6Subnet
|
||||
}
|
||||
|
||||
type ipv4Subnet struct {
|
||||
SubnetIP string
|
||||
GwIP string
|
||||
}
|
||||
|
||||
type ipv6Subnet struct {
|
||||
SubnetIP string
|
||||
GwIP string
|
||||
}
|
||||
|
||||
// initStore drivers are responsible for caching their own persistent state
|
||||
func (d *driver) initStore(option map[string]interface{}) error {
|
||||
if data, ok := option[netlabel.LocalKVClient]; ok {
|
||||
var err error
|
||||
dsc, ok := data.(discoverapi.DatastoreConfigData)
|
||||
if !ok {
|
||||
return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
|
||||
}
|
||||
d.store, err = datastore.NewDataStoreFromConfig(dsc)
|
||||
if err != nil {
|
||||
return types.InternalErrorf("macvlan driver failed to initialize data store: %v", err)
|
||||
}
|
||||
|
||||
return d.populateNetworks()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populateNetworks is invoked at driver init to recreate persistently stored networks
|
||||
func (d *driver) populateNetworks() error {
|
||||
kvol, err := d.store.List(datastore.Key(macvlanPrefix), &configuration{})
|
||||
if err != nil && err != datastore.ErrKeyNotFound && err != boltdb.ErrBoltBucketNotFound {
|
||||
return fmt.Errorf("failed to get macvlan network configurations from store: %v", err)
|
||||
}
|
||||
// If empty it simply means no macvlan networks have been created yet
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
return nil
|
||||
}
|
||||
for _, kvo := range kvol {
|
||||
config := kvo.(*configuration)
|
||||
if err = d.createNetwork(config); err != nil {
|
||||
logrus.Warnf("Could not create macvlan network for id %s from persistent state", config.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// storeUpdate used to update persistent macvlan network records as they are created
|
||||
func (d *driver) storeUpdate(kvObject datastore.KVObject) error {
|
||||
if d.store == nil {
|
||||
logrus.Warnf("macvlan store not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
}
|
||||
if err := d.store.PutObjectAtomic(kvObject); err != nil {
|
||||
return fmt.Errorf("failed to update macvlan store for object type %T: %v", kvObject, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// storeDelete used to delete macvlan records from persistent cache as they are deleted
|
||||
func (d *driver) storeDelete(kvObject datastore.KVObject) error {
|
||||
if d.store == nil {
|
||||
logrus.Debugf("macvlan store not initialized. kv object %s is not deleted from store", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
}
|
||||
retry:
|
||||
if err := d.store.DeleteObjectAtomic(kvObject); err != nil {
|
||||
if err == datastore.ErrKeyModified {
|
||||
if err := d.store.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil {
|
||||
return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err)
|
||||
}
|
||||
goto retry
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *configuration) MarshalJSON() ([]byte, error) {
|
||||
nMap := make(map[string]interface{})
|
||||
nMap["ID"] = config.ID
|
||||
nMap["Mtu"] = config.Mtu
|
||||
nMap["Parent"] = config.Parent
|
||||
nMap["MacvlanMode"] = config.MacvlanMode
|
||||
nMap["Internal"] = config.Internal
|
||||
nMap["CreatedSubIface"] = config.CreatedSlaveLink
|
||||
if len(config.Ipv4Subnets) > 0 {
|
||||
iis, err := json.Marshal(config.Ipv4Subnets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nMap["Ipv4Subnets"] = string(iis)
|
||||
}
|
||||
if len(config.Ipv6Subnets) > 0 {
|
||||
iis, err := json.Marshal(config.Ipv6Subnets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nMap["Ipv6Subnets"] = string(iis)
|
||||
}
|
||||
|
||||
return json.Marshal(nMap)
|
||||
}
|
||||
|
||||
func (config *configuration) UnmarshalJSON(b []byte) error {
|
||||
var (
|
||||
err error
|
||||
nMap map[string]interface{}
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(b, &nMap); err != nil {
|
||||
return err
|
||||
}
|
||||
config.ID = nMap["ID"].(string)
|
||||
config.Mtu = int(nMap["Mtu"].(float64))
|
||||
config.Parent = nMap["Parent"].(string)
|
||||
config.MacvlanMode = nMap["MacvlanMode"].(string)
|
||||
config.Internal = nMap["Internal"].(bool)
|
||||
config.CreatedSlaveLink = nMap["CreatedSubIface"].(bool)
|
||||
if v, ok := nMap["Ipv4Subnets"]; ok {
|
||||
if err := json.Unmarshal([]byte(v.(string)), &config.Ipv4Subnets); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v, ok := nMap["Ipv6Subnets"]; ok {
|
||||
if err := json.Unmarshal([]byte(v.(string)), &config.Ipv6Subnets); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *configuration) Key() []string {
|
||||
return []string{macvlanPrefix, config.ID}
|
||||
}
|
||||
|
||||
func (config *configuration) KeyPrefix() []string {
|
||||
return []string{macvlanPrefix}
|
||||
}
|
||||
|
||||
func (config *configuration) Value() []byte {
|
||||
b, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (config *configuration) SetValue(value []byte) error {
|
||||
return json.Unmarshal(value, config)
|
||||
}
|
||||
|
||||
func (config *configuration) Index() uint64 {
|
||||
return config.dbIndex
|
||||
}
|
||||
|
||||
func (config *configuration) SetIndex(index uint64) {
|
||||
config.dbIndex = index
|
||||
config.dbExists = true
|
||||
}
|
||||
|
||||
func (config *configuration) Exists() bool {
|
||||
return config.dbExists
|
||||
}
|
||||
|
||||
func (config *configuration) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (config *configuration) New() datastore.KVObject {
|
||||
return &configuration{}
|
||||
}
|
||||
|
||||
func (config *configuration) CopyTo(o datastore.KVObject) error {
|
||||
dstNcfg := o.(*configuration)
|
||||
*dstNcfg = *config
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *configuration) DataScope() string {
|
||||
return datastore.LocalScope
|
||||
}
|
15
vendor/src/github.com/docker/libnetwork/drivers_experimental_linux.go
vendored
Normal file
15
vendor/src/github.com/docker/libnetwork/drivers_experimental_linux.go
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
// +build experimental
|
||||
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"github.com/docker/libnetwork/drivers/ipvlan"
|
||||
"github.com/docker/libnetwork/drivers/macvlan"
|
||||
)
|
||||
|
||||
func additionalDrivers() []initializer {
|
||||
return []initializer{
|
||||
{macvlan.Init, "macvlan"},
|
||||
{ipvlan.Init, "ipvlan"},
|
||||
}
|
||||
}
|
|
@ -9,11 +9,14 @@ import (
|
|||
)
|
||||
|
||||
func getInitializers() []initializer {
|
||||
return []initializer{
|
||||
in := []initializer{
|
||||
{bridge.Init, "bridge"},
|
||||
{host.Init, "host"},
|
||||
{null.Init, "null"},
|
||||
{remote.Init, "remote"},
|
||||
{overlay.Init, "overlay"},
|
||||
}
|
||||
|
||||
in = append(in, additionalDrivers()...)
|
||||
return in
|
||||
}
|
||||
|
|
7
vendor/src/github.com/docker/libnetwork/drivers_stub_linux.go
vendored
Normal file
7
vendor/src/github.com/docker/libnetwork/drivers_stub_linux.go
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !experimental
|
||||
|
||||
package libnetwork
|
||||
|
||||
func additionalDrivers() []initializer {
|
||||
return nil
|
||||
}
|
|
@ -44,11 +44,15 @@ func FirewalldInit() error {
|
|||
if connection, err = newConnection(); err != nil {
|
||||
return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err)
|
||||
}
|
||||
firewalldRunning = checkRunning()
|
||||
if !firewalldRunning {
|
||||
connection.sysconn.Close()
|
||||
connection = nil
|
||||
}
|
||||
if connection != nil {
|
||||
go signalHandler()
|
||||
}
|
||||
|
||||
firewalldRunning = checkRunning()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue