l2vpn.c revision 1.17
1/*	$OpenBSD: l2vpn.c,v 1.17 2016/06/18 01:25:53 renato Exp $ */
2
3/*
4 * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
5 * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
6 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
7 * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22#include <sys/types.h>
23#include <stdlib.h>
24#include <string.h>
25#include <limits.h>
26
27#include "ldpd.h"
28#include "ldpe.h"
29#include "lde.h"
30#include "log.h"
31
32static void	 l2vpn_pw_fec(struct l2vpn_pw *, struct fec *);
33
34struct l2vpn *
35l2vpn_new(const char *name)
36{
37	struct l2vpn	*l2vpn;
38
39	if ((l2vpn = calloc(1, sizeof(*l2vpn))) == NULL)
40		fatal("l2vpn_new: calloc");
41
42	strlcpy(l2vpn->name, name, sizeof(l2vpn->name));
43
44	/* set default values */
45	l2vpn->mtu = DEFAULT_L2VPN_MTU;
46	l2vpn->pw_type = DEFAULT_PW_TYPE;
47
48	LIST_INIT(&l2vpn->if_list);
49	LIST_INIT(&l2vpn->pw_list);
50
51	return (l2vpn);
52}
53
54struct l2vpn *
55l2vpn_find(struct ldpd_conf *xconf, const char *name)
56{
57	struct l2vpn	*l2vpn;
58
59	LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry)
60		if (strcmp(l2vpn->name, name) == 0)
61			return (l2vpn);
62
63	return (NULL);
64}
65
66void
67l2vpn_del(struct l2vpn *l2vpn)
68{
69	struct l2vpn_if		*lif;
70	struct l2vpn_pw		*pw;
71
72	while ((lif = LIST_FIRST(&l2vpn->if_list)) != NULL) {
73		LIST_REMOVE(lif, entry);
74		free(lif);
75	}
76	while ((pw = LIST_FIRST(&l2vpn->pw_list)) != NULL) {
77		l2vpn_pw_exit(pw);
78		LIST_REMOVE(pw, entry);
79		free(pw);
80	}
81
82	free(l2vpn);
83}
84
85void
86l2vpn_init(struct l2vpn *l2vpn)
87{
88	struct l2vpn_pw	*pw;
89
90	LIST_FOREACH(pw, &l2vpn->pw_list, entry)
91		l2vpn_pw_init(pw);
92}
93
94struct l2vpn_if *
95l2vpn_if_new(struct l2vpn *l2vpn, struct kif *kif)
96{
97	struct l2vpn_if	*lif;
98
99	if ((lif = calloc(1, sizeof(*lif))) == NULL)
100		fatal("l2vpn_if_new: calloc");
101
102	lif->l2vpn = l2vpn;
103	strlcpy(lif->ifname, kif->ifname, sizeof(lif->ifname));
104	lif->ifindex = kif->ifindex;
105	lif->flags = kif->flags;
106	lif->link_state = kif->link_state;
107
108	return (lif);
109}
110
111struct l2vpn_if *
112l2vpn_if_find(struct l2vpn *l2vpn, unsigned int ifindex)
113{
114	struct l2vpn_if	*lif;
115
116	LIST_FOREACH(lif, &l2vpn->if_list, entry)
117		if (lif->ifindex == ifindex)
118			return (lif);
119
120	return (NULL);
121}
122
123struct l2vpn_pw *
124l2vpn_pw_new(struct l2vpn *l2vpn, struct kif *kif)
125{
126	struct l2vpn_pw	*pw;
127
128	if ((pw = calloc(1, sizeof(*pw))) == NULL)
129		fatal("l2vpn_pw_new: calloc");
130
131	pw->l2vpn = l2vpn;
132	strlcpy(pw->ifname, kif->ifname, sizeof(pw->ifname));
133	pw->ifindex = kif->ifindex;
134
135	return (pw);
136}
137
138struct l2vpn_pw *
139l2vpn_pw_find(struct l2vpn *l2vpn, unsigned int ifindex)
140{
141	struct l2vpn_pw	*pw;
142
143	LIST_FOREACH(pw, &l2vpn->pw_list, entry)
144		if (pw->ifindex == ifindex)
145			return (pw);
146
147	return (NULL);
148}
149
150void
151l2vpn_pw_init(struct l2vpn_pw *pw)
152{
153	struct fec	 fec;
154
155	l2vpn_pw_reset(pw);
156
157	l2vpn_pw_fec(pw, &fec);
158	lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0,
159	    0, (void *)pw);
160}
161
162void
163l2vpn_pw_exit(struct l2vpn_pw *pw)
164{
165	struct fec	 fec;
166
167	l2vpn_pw_fec(pw, &fec);
168	lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0);
169}
170
171static void
172l2vpn_pw_fec(struct l2vpn_pw *pw, struct fec *fec)
173{
174	memset(fec, 0, sizeof(*fec));
175	fec->type = FEC_TYPE_PWID;
176	fec->u.pwid.type = pw->l2vpn->pw_type;
177	fec->u.pwid.pwid = pw->pwid;
178	fec->u.pwid.lsr_id = pw->lsr_id;
179}
180
181void
182l2vpn_pw_reset(struct l2vpn_pw *pw)
183{
184	pw->remote_group = 0;
185	pw->remote_mtu = 0;
186	pw->remote_status = 0;
187
188	if (pw->flags & F_PW_CWORD_CONF)
189		pw->flags |= F_PW_CWORD;
190	else
191		pw->flags &= ~F_PW_CWORD;
192
193	if (pw->flags & F_PW_STATUSTLV_CONF)
194		pw->flags |= F_PW_STATUSTLV;
195	else
196		pw->flags &= ~F_PW_STATUSTLV;
197}
198
199int
200l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh)
201{
202	struct fec		 fec;
203	struct fec_node		*fn;
204
205	/* check for a remote label */
206	if (fnh->remote_label == NO_LABEL)
207		return (0);
208
209	/* MTUs must match */
210	if (pw->l2vpn->mtu != pw->remote_mtu)
211		return (0);
212
213	/* check pw status if applicable */
214	if ((pw->flags & F_PW_STATUSTLV) &&
215	    pw->remote_status != PW_FORWARDING)
216		return (0);
217
218	/* check for a working lsp to the nexthop */
219	memset(&fec, 0, sizeof(fec));
220	switch (pw->af) {
221	case AF_INET:
222		fec.type = FEC_TYPE_IPV4;
223		fec.u.ipv4.prefix = pw->addr.v4;
224		fec.u.ipv4.prefixlen = 32;
225		break;
226	case AF_INET6:
227		fec.type = FEC_TYPE_IPV6;
228		fec.u.ipv6.prefix = pw->addr.v6;
229		fec.u.ipv6.prefixlen = 128;
230		break;
231	default:
232		fatalx("l2vpn_pw_ok: unknown af");
233	}
234
235	fn = (struct fec_node *)fec_find(&ft, &fec);
236	if (fn == NULL || fn->local_label == NO_LABEL)
237		return (0);
238	/*
239	 * Need to ensure that there's a label binding for all nexthops.
240	 * Otherwise, ECMP for this route could render the pseudowire unusable.
241	 */
242	LIST_FOREACH(fnh, &fn->nexthops, entry)
243		if (fnh->remote_label == NO_LABEL)
244			return (0);
245
246	return (1);
247}
248
249int
250l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map)
251{
252	struct l2vpn_pw		*pw;
253
254	/* NOTE: thanks martini & friends for all this mess */
255
256	pw = (struct l2vpn_pw *) fn->data;
257	if (pw == NULL)
258		/*
259		 * pseudowire not configured, return and record
260		 * the mapping later
261		 */
262		return (0);
263
264	/* RFC4447 - Section 6.2: control word negotiation */
265	if (fec_find(&ln->sent_map, &fn->fec)) {
266		if ((map->flags & F_MAP_PW_CWORD) &&
267		    !(pw->flags & F_PW_CWORD_CONF)) {
268			/* ignore the received label mapping */
269			return (1);
270		} else if (!(map->flags & F_MAP_PW_CWORD) &&
271		    (pw->flags & F_PW_CWORD_CONF)) {
272			/* TODO append a "Wrong C-bit" status code */
273			lde_send_labelwithdraw(ln, fn, NO_LABEL);
274
275			pw->flags &= ~F_PW_CWORD;
276			lde_send_labelmapping(ln, fn, 1);
277		}
278	} else if (map->flags & F_MAP_PW_CWORD) {
279		if (pw->flags & F_PW_CWORD_CONF)
280			pw->flags |= F_PW_CWORD;
281		else
282			/* act as if no label mapping had been received */
283			return (1);
284	} else
285		pw->flags &= ~F_PW_CWORD;
286
287	/* RFC4447 - Section 5.4.3: pseudowire status negotiation */
288	if (fec_find(&ln->recv_map, &fn->fec) == NULL &&
289	    !(map->flags & F_MAP_PW_STATUS))
290		pw->flags &= ~F_PW_STATUSTLV;
291
292	return (0);
293}
294
295void
296l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec)
297{
298	struct notify_msg	 nm;
299
300	memset(&nm, 0, sizeof(nm));
301	nm.status = S_PW_STATUS;
302
303	nm.pw_status = status;
304	nm.flags |= F_NOTIF_PW_STATUS;
305
306	lde_fec2map(fec, &nm.fec);
307	nm.flags |= F_NOTIF_FEC;
308
309	lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0,
310	    &nm, sizeof(nm));
311}
312
313void
314l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
315{
316	struct fec		 fec;
317	struct fec_node		*fn;
318	struct fec_nh		*fnh;
319	struct l2vpn_pw		*pw;
320
321	/* TODO group wildcard */
322	if (!(nm->fec.flags & F_MAP_PW_ID))
323		return;
324
325	lde_map2fec(&nm->fec, ln->id, &fec);
326	fn = (struct fec_node *)fec_find(&ft, &fec);
327	if (fn == NULL)
328		/* unknown fec */
329		return;
330
331	pw = (struct l2vpn_pw *) fn->data;
332	if (pw == NULL)
333		return;
334
335	fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0);
336	if (fnh == NULL)
337		return;
338
339	/* remote status didn't change */
340	if (pw->remote_status == nm->pw_status)
341		return;
342
343	pw->remote_status = nm->pw_status;
344
345	if (l2vpn_pw_ok(pw, fnh))
346		lde_send_change_klabel(fn, fnh);
347	else
348		lde_send_delete_klabel(fn, fnh);
349}
350
351void
352l2vpn_sync_pws(int af, union ldpd_addr *addr)
353{
354	struct l2vpn		*l2vpn;
355	struct l2vpn_pw		*pw;
356	struct fec		 fec;
357	struct fec_node		*fn;
358	struct fec_nh		*fnh;
359
360	LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) {
361		LIST_FOREACH(pw, &l2vpn->pw_list, entry) {
362			if (af != pw->af || ldp_addrcmp(af, &pw->addr, addr))
363				continue;
364
365			l2vpn_pw_fec(pw, &fec);
366			fn = (struct fec_node *)fec_find(&ft, &fec);
367			if (fn == NULL)
368				continue;
369			fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)
370			    &pw->lsr_id, 0);
371			if (fnh == NULL)
372				continue;
373
374			if (l2vpn_pw_ok(pw, fnh))
375				lde_send_change_klabel(fn, fnh);
376			else
377				lde_send_delete_klabel(fn, fnh);
378		}
379	}
380}
381
382void
383l2vpn_pw_ctl(pid_t pid)
384{
385	struct l2vpn		*l2vpn;
386	struct l2vpn_pw		*pw;
387	static struct ctl_pw	 pwctl;
388
389	LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry)
390		LIST_FOREACH(pw, &l2vpn->pw_list, entry) {
391			memset(&pwctl, 0, sizeof(pwctl));
392			strlcpy(pwctl.ifname, pw->ifname,
393			    sizeof(pwctl.ifname));
394			pwctl.pwid = pw->pwid;
395			pwctl.lsr_id = pw->lsr_id;
396			pwctl.status = pw->flags & F_PW_STATUS_UP;
397
398			lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0,
399			    pid, &pwctl, sizeof(pwctl));
400		}
401}
402
403void
404l2vpn_binding_ctl(pid_t pid)
405{
406	struct fec		*f;
407	struct fec_node		*fn;
408	struct lde_map		*me;
409	struct l2vpn_pw		*pw;
410	static struct ctl_pw	 pwctl;
411
412	RB_FOREACH(f, fec_tree, &ft) {
413		if (f->type != FEC_TYPE_PWID)
414			continue;
415
416		fn = (struct fec_node *)f;
417		if (fn->local_label == NO_LABEL &&
418		    LIST_EMPTY(&fn->downstream))
419			continue;
420
421		memset(&pwctl, 0, sizeof(pwctl));
422		pwctl.type = f->u.pwid.type;
423		pwctl.pwid = f->u.pwid.pwid;
424		pwctl.lsr_id = f->u.pwid.lsr_id;
425
426		pw = (struct l2vpn_pw *) fn->data;
427		if (pw) {
428			pwctl.local_label = fn->local_label;
429			pwctl.local_gid = 0;
430			pwctl.local_ifmtu = pw->l2vpn->mtu;
431		} else
432			pwctl.local_label = NO_LABEL;
433
434		LIST_FOREACH(me, &fn->downstream, entry)
435			if (f->u.pwid.lsr_id.s_addr == me->nexthop->id.s_addr)
436				break;
437
438		if (me) {
439			pwctl.remote_label = me->map.label;
440			pwctl.remote_gid = me->map.fec.pwid.group_id;
441			if (me->map.flags & F_MAP_PW_IFMTU)
442				pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu;
443
444			lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
445			    0, pid, &pwctl, sizeof(pwctl));
446		} else if (pw) {
447			pwctl.remote_label = NO_LABEL;
448
449			lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
450			    0, pid, &pwctl, sizeof(pwctl));
451		}
452	}
453}
454
455/* ldpe */
456
457void
458ldpe_l2vpn_init(struct l2vpn *l2vpn)
459{
460	struct l2vpn_pw		*pw;
461
462	LIST_FOREACH(pw, &l2vpn->pw_list, entry)
463		ldpe_l2vpn_pw_init(pw);
464}
465
466void
467ldpe_l2vpn_exit(struct l2vpn *l2vpn)
468{
469	struct l2vpn_pw		*pw;
470
471	LIST_FOREACH(pw, &l2vpn->pw_list, entry)
472		ldpe_l2vpn_pw_exit(pw);
473}
474
475void
476ldpe_l2vpn_pw_init(struct l2vpn_pw *pw)
477{
478	struct tnbr		*tnbr;
479
480	tnbr = tnbr_find(leconf, pw->af, &pw->addr);
481	if (tnbr == NULL) {
482		tnbr = tnbr_new(leconf, pw->af, &pw->addr);
483		tnbr_update(tnbr);
484		LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry);
485	}
486
487	tnbr->pw_count++;
488}
489
490void
491ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw)
492{
493	struct tnbr		*tnbr;
494
495	tnbr = tnbr_find(leconf, pw->af, &pw->addr);
496	if (tnbr) {
497		tnbr->pw_count--;
498		tnbr_check(tnbr);
499	}
500}
501