1package macvlan
2
3import (
4	"fmt"
5	"net"
6
7	"github.com/docker/libnetwork/driverapi"
8	"github.com/docker/libnetwork/netutils"
9	"github.com/docker/libnetwork/ns"
10	"github.com/docker/libnetwork/osl"
11	"github.com/sirupsen/logrus"
12)
13
14// Join method is invoked when a Sandbox is attached to an endpoint.
15func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
16	defer osl.InitOSContext()()
17	n, err := d.getNetwork(nid)
18	if err != nil {
19		return err
20	}
21	endpoint := n.endpoint(eid)
22	if endpoint == nil {
23		return fmt.Errorf("could not find endpoint with id %s", eid)
24	}
25	// generate a name for the iface that will be renamed to eth0 in the sbox
26	containerIfName, err := netutils.GenerateIfaceName(ns.NlHandle(), vethPrefix, vethLen)
27	if err != nil {
28		return fmt.Errorf("error generating an interface name: %s", err)
29	}
30	// create the netlink macvlan interface
31	vethName, err := createMacVlan(containerIfName, n.config.Parent, n.config.MacvlanMode)
32	if err != nil {
33		return err
34	}
35	// bind the generated iface name to the endpoint
36	endpoint.srcName = vethName
37	ep := n.endpoint(eid)
38	if ep == nil {
39		return fmt.Errorf("could not find endpoint with id %s", eid)
40	}
41	// parse and match the endpoint address with the available v4 subnets
42	if len(n.config.Ipv4Subnets) > 0 {
43		s := n.getSubnetforIPv4(ep.addr)
44		if s == nil {
45			return fmt.Errorf("could not find a valid ipv4 subnet for endpoint %s", eid)
46		}
47		v4gw, _, err := net.ParseCIDR(s.GwIP)
48		if err != nil {
49			return fmt.Errorf("gateway %s is not a valid ipv4 address: %v", s.GwIP, err)
50		}
51		err = jinfo.SetGateway(v4gw)
52		if err != nil {
53			return err
54		}
55		logrus.Debugf("Macvlan Endpoint Joined with IPv4_Addr: %s, Gateway: %s, MacVlan_Mode: %s, Parent: %s",
56			ep.addr.IP.String(), v4gw.String(), n.config.MacvlanMode, n.config.Parent)
57	}
58	// parse and match the endpoint address with the available v6 subnets
59	if len(n.config.Ipv6Subnets) > 0 {
60		s := n.getSubnetforIPv6(ep.addrv6)
61		if s == nil {
62			return fmt.Errorf("could not find a valid ipv6 subnet for endpoint %s", eid)
63		}
64		v6gw, _, err := net.ParseCIDR(s.GwIP)
65		if err != nil {
66			return fmt.Errorf("gateway %s is not a valid ipv6 address: %v", s.GwIP, err)
67		}
68		err = jinfo.SetGatewayIPv6(v6gw)
69		if err != nil {
70			return err
71		}
72		logrus.Debugf("Macvlan Endpoint Joined with IPv6_Addr: %s Gateway: %s MacVlan_Mode: %s, Parent: %s",
73			ep.addrv6.IP.String(), v6gw.String(), n.config.MacvlanMode, n.config.Parent)
74	}
75	iNames := jinfo.InterfaceName()
76	err = iNames.SetNames(vethName, containerVethPrefix)
77	if err != nil {
78		return err
79	}
80	if err := d.storeUpdate(ep); err != nil {
81		return fmt.Errorf("failed to save macvlan endpoint %.7s to store: %v", ep.id, err)
82	}
83	return nil
84}
85
86// Leave method is invoked when a Sandbox detaches from an endpoint.
87func (d *driver) Leave(nid, eid string) error {
88	defer osl.InitOSContext()()
89	network, err := d.getNetwork(nid)
90	if err != nil {
91		return err
92	}
93	endpoint, err := network.getEndpoint(eid)
94	if err != nil {
95		return err
96	}
97	if endpoint == nil {
98		return fmt.Errorf("could not find endpoint with id %s", eid)
99	}
100
101	return nil
102}
103
104// getSubnetforIP returns the ipv4 subnet to which the given IP belongs
105func (n *network) getSubnetforIPv4(ip *net.IPNet) *ipv4Subnet {
106	for _, s := range n.config.Ipv4Subnets {
107		_, snet, err := net.ParseCIDR(s.SubnetIP)
108		if err != nil {
109			return nil
110		}
111		// first check if the mask lengths are the same
112		i, _ := snet.Mask.Size()
113		j, _ := ip.Mask.Size()
114		if i != j {
115			continue
116		}
117		if snet.Contains(ip.IP) {
118			return s
119		}
120	}
121
122	return nil
123}
124
125// getSubnetforIPv6 returns the ipv6 subnet to which the given IP belongs
126func (n *network) getSubnetforIPv6(ip *net.IPNet) *ipv6Subnet {
127	for _, s := range n.config.Ipv6Subnets {
128		_, snet, err := net.ParseCIDR(s.SubnetIP)
129		if err != nil {
130			return nil
131		}
132		// first check if the mask lengths are the same
133		i, _ := snet.Mask.Size()
134		j, _ := ip.Mask.Size()
135		if i != j {
136			continue
137		}
138		if snet.Contains(ip.IP) {
139			return s
140		}
141	}
142
143	return nil
144}
145