1package overlay
2
3import (
4	"fmt"
5	"net"
6	"syscall"
7
8	"github.com/sirupsen/logrus"
9	"github.com/docker/libnetwork/driverapi"
10	"github.com/docker/libnetwork/ns"
11	"github.com/docker/libnetwork/types"
12	"github.com/gogo/protobuf/proto"
13)
14
15// Join method is invoked when a Sandbox is attached to an endpoint.
16func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
17	if err := validateID(nid, eid); err != nil {
18		return err
19	}
20
21	n := d.network(nid)
22	if n == nil {
23		return fmt.Errorf("could not find network with id %s", nid)
24	}
25
26	ep := n.endpoint(eid)
27	if ep == nil {
28		return fmt.Errorf("could not find endpoint with id %s", eid)
29	}
30
31	if n.secure && len(d.keys) == 0 {
32		return fmt.Errorf("cannot join secure network: encryption keys not present")
33	}
34
35	nlh := ns.NlHandle()
36
37	if n.secure && !nlh.SupportsNetlinkFamily(syscall.NETLINK_XFRM) {
38		return fmt.Errorf("cannot join secure network: required modules to install IPSEC rules are missing on host")
39	}
40
41	s := n.getSubnetforIP(ep.addr)
42	if s == nil {
43		return fmt.Errorf("could not find subnet for endpoint %s", eid)
44	}
45
46	if err := n.obtainVxlanID(s); err != nil {
47		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
48	}
49
50	if err := n.joinSandbox(false); err != nil {
51		return fmt.Errorf("network sandbox join failed: %v", err)
52	}
53
54	if err := n.joinSubnetSandbox(s, false); err != nil {
55		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
56	}
57
58	// joinSubnetSandbox gets called when an endpoint comes up on a new subnet in the
59	// overlay network. Hence the Endpoint count should be updated outside joinSubnetSandbox
60	n.incEndpointCount()
61
62	sbox := n.sandbox()
63
64	overlayIfName, containerIfName, err := createVethPair()
65	if err != nil {
66		return err
67	}
68
69	ep.ifName = containerIfName
70
71	if err := d.writeEndpointToStore(ep); err != nil {
72		return fmt.Errorf("failed to update overlay endpoint %s to local data store: %v", ep.id[0:7], err)
73	}
74
75	// Set the container interface and its peer MTU to 1450 to allow
76	// for 50 bytes vxlan encap (inner eth header(14) + outer IP(20) +
77	// outer UDP(8) + vxlan header(8))
78	mtu := n.maxMTU()
79
80	veth, err := nlh.LinkByName(overlayIfName)
81	if err != nil {
82		return fmt.Errorf("cound not find link by name %s: %v", overlayIfName, err)
83	}
84	err = nlh.LinkSetMTU(veth, mtu)
85	if err != nil {
86		return err
87	}
88
89	if err := sbox.AddInterface(overlayIfName, "veth",
90		sbox.InterfaceOptions().Master(s.brName)); err != nil {
91		return fmt.Errorf("could not add veth pair inside the network sandbox: %v", err)
92	}
93
94	veth, err = nlh.LinkByName(containerIfName)
95	if err != nil {
96		return fmt.Errorf("could not find link by name %s: %v", containerIfName, err)
97	}
98	err = nlh.LinkSetMTU(veth, mtu)
99	if err != nil {
100		return err
101	}
102
103	if err := nlh.LinkSetHardwareAddr(veth, ep.mac); err != nil {
104		return fmt.Errorf("could not set mac address (%v) to the container interface: %v", ep.mac, err)
105	}
106
107	for _, sub := range n.subnets {
108		if sub == s {
109			continue
110		}
111		if err := jinfo.AddStaticRoute(sub.subnetIP, types.NEXTHOP, s.gwIP.IP); err != nil {
112			logrus.Errorf("Adding subnet %s static route in network %q failed\n", s.subnetIP, n.id)
113		}
114	}
115
116	if iNames := jinfo.InterfaceName(); iNames != nil {
117		err = iNames.SetNames(containerIfName, "eth")
118		if err != nil {
119			return err
120		}
121	}
122
123	d.peerDbAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac,
124		net.ParseIP(d.advertiseAddress), true)
125
126	if err := d.checkEncryption(nid, nil, n.vxlanID(s), true, true); err != nil {
127		logrus.Warn(err)
128	}
129
130	buf, err := proto.Marshal(&PeerRecord{
131		EndpointIP:       ep.addr.String(),
132		EndpointMAC:      ep.mac.String(),
133		TunnelEndpointIP: d.advertiseAddress,
134	})
135	if err != nil {
136		return err
137	}
138
139	if err := jinfo.AddTableEntry(ovPeerTable, eid, buf); err != nil {
140		logrus.Errorf("overlay: Failed adding table entry to joininfo: %v", err)
141	}
142
143	d.pushLocalEndpointEvent("join", nid, eid)
144
145	return nil
146}
147
148func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
149	if tableName != ovPeerTable {
150		logrus.Errorf("Unexpected table notification for table %s received", tableName)
151		return
152	}
153
154	eid := key
155
156	var peer PeerRecord
157	if err := proto.Unmarshal(value, &peer); err != nil {
158		logrus.Errorf("Failed to unmarshal peer record: %v", err)
159		return
160	}
161
162	// Ignore local peers. We already know about them and they
163	// should not be added to vxlan fdb.
164	if peer.TunnelEndpointIP == d.advertiseAddress {
165		return
166	}
167
168	addr, err := types.ParseCIDR(peer.EndpointIP)
169	if err != nil {
170		logrus.Errorf("Invalid peer IP %s received in event notify", peer.EndpointIP)
171		return
172	}
173
174	mac, err := net.ParseMAC(peer.EndpointMAC)
175	if err != nil {
176		logrus.Errorf("Invalid mac %s received in event notify", peer.EndpointMAC)
177		return
178	}
179
180	vtep := net.ParseIP(peer.TunnelEndpointIP)
181	if vtep == nil {
182		logrus.Errorf("Invalid VTEP %s received in event notify", peer.TunnelEndpointIP)
183		return
184	}
185
186	if etype == driverapi.Delete {
187		d.peerDelete(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
188		return
189	}
190
191	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
192}
193
194// Leave method is invoked when a Sandbox detaches from an endpoint.
195func (d *driver) Leave(nid, eid string) error {
196	if err := validateID(nid, eid); err != nil {
197		return err
198	}
199
200	n := d.network(nid)
201	if n == nil {
202		return fmt.Errorf("could not find network with id %s", nid)
203	}
204
205	ep := n.endpoint(eid)
206
207	if ep == nil {
208		return types.InternalMaskableErrorf("could not find endpoint with id %s", eid)
209	}
210
211	if d.notifyCh != nil {
212		d.notifyCh <- ovNotify{
213			action: "leave",
214			nw:     n,
215			ep:     ep,
216		}
217	}
218
219	n.leaveSandbox()
220
221	if err := d.checkEncryption(nid, nil, 0, true, false); err != nil {
222		logrus.Warn(err)
223	}
224
225	return nil
226}
227