]>
Commit | Line | Data |
---|---|---|
8429abe0 RW |
1 | /* $OpenBSD$ */ |
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 | ||
eac6e3f0 | 22 | #include <zebra.h> |
8429abe0 RW |
23 | |
24 | #include "ldpd.h" | |
25 | #include "ldpe.h" | |
26 | #include "lde.h" | |
27 | #include "log.h" | |
28 | ||
29 | static void l2vpn_pw_fec(struct l2vpn_pw *, struct fec *); | |
30 | ||
31 | struct l2vpn * | |
32 | l2vpn_new(const char *name) | |
33 | { | |
34 | struct l2vpn *l2vpn; | |
35 | ||
36 | if ((l2vpn = calloc(1, sizeof(*l2vpn))) == NULL) | |
37 | fatal("l2vpn_new: calloc"); | |
38 | ||
39 | strlcpy(l2vpn->name, name, sizeof(l2vpn->name)); | |
40 | ||
41 | /* set default values */ | |
42 | l2vpn->mtu = DEFAULT_L2VPN_MTU; | |
43 | l2vpn->pw_type = DEFAULT_PW_TYPE; | |
44 | ||
45 | LIST_INIT(&l2vpn->if_list); | |
46 | LIST_INIT(&l2vpn->pw_list); | |
eac6e3f0 | 47 | LIST_INIT(&l2vpn->pw_inactive_list); |
8429abe0 RW |
48 | |
49 | return (l2vpn); | |
50 | } | |
51 | ||
52 | struct l2vpn * | |
53 | l2vpn_find(struct ldpd_conf *xconf, const char *name) | |
54 | { | |
55 | struct l2vpn *l2vpn; | |
56 | ||
57 | LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) | |
58 | if (strcmp(l2vpn->name, name) == 0) | |
59 | return (l2vpn); | |
60 | ||
61 | return (NULL); | |
62 | } | |
63 | ||
64 | void | |
65 | l2vpn_del(struct l2vpn *l2vpn) | |
66 | { | |
67 | struct l2vpn_if *lif; | |
68 | struct l2vpn_pw *pw; | |
69 | ||
70 | while ((lif = LIST_FIRST(&l2vpn->if_list)) != NULL) { | |
71 | LIST_REMOVE(lif, entry); | |
72 | free(lif); | |
73 | } | |
74 | while ((pw = LIST_FIRST(&l2vpn->pw_list)) != NULL) { | |
75 | LIST_REMOVE(pw, entry); | |
76 | free(pw); | |
77 | } | |
eac6e3f0 RW |
78 | while ((pw = LIST_FIRST(&l2vpn->pw_inactive_list)) != NULL) { |
79 | LIST_REMOVE(pw, entry); | |
80 | free(pw); | |
81 | } | |
8429abe0 RW |
82 | |
83 | free(l2vpn); | |
84 | } | |
85 | ||
86 | void | |
87 | l2vpn_init(struct l2vpn *l2vpn) | |
88 | { | |
89 | struct l2vpn_pw *pw; | |
90 | ||
91 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) | |
92 | l2vpn_pw_init(pw); | |
93 | } | |
94 | ||
95 | void | |
96 | l2vpn_exit(struct l2vpn *l2vpn) | |
97 | { | |
98 | struct l2vpn_pw *pw; | |
99 | ||
100 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) | |
101 | l2vpn_pw_exit(pw); | |
102 | } | |
103 | ||
104 | struct l2vpn_if * | |
105 | l2vpn_if_new(struct l2vpn *l2vpn, struct kif *kif) | |
106 | { | |
107 | struct l2vpn_if *lif; | |
108 | ||
109 | if ((lif = calloc(1, sizeof(*lif))) == NULL) | |
110 | fatal("l2vpn_if_new: calloc"); | |
111 | ||
112 | lif->l2vpn = l2vpn; | |
113 | strlcpy(lif->ifname, kif->ifname, sizeof(lif->ifname)); | |
114 | lif->ifindex = kif->ifindex; | |
115 | lif->flags = kif->flags; | |
8429abe0 RW |
116 | |
117 | return (lif); | |
118 | } | |
119 | ||
120 | struct l2vpn_if * | |
121 | l2vpn_if_find(struct l2vpn *l2vpn, unsigned int ifindex) | |
122 | { | |
123 | struct l2vpn_if *lif; | |
124 | ||
125 | LIST_FOREACH(lif, &l2vpn->if_list, entry) | |
126 | if (lif->ifindex == ifindex) | |
127 | return (lif); | |
128 | ||
129 | return (NULL); | |
130 | } | |
131 | ||
eac6e3f0 RW |
132 | struct l2vpn_if * |
133 | l2vpn_if_find_name(struct l2vpn *l2vpn, const char *ifname) | |
134 | { | |
135 | struct l2vpn_if *lif; | |
136 | ||
137 | LIST_FOREACH(lif, &l2vpn->if_list, entry) | |
138 | if (strcmp(lif->ifname, ifname) == 0) | |
139 | return (lif); | |
140 | ||
141 | return (NULL); | |
142 | } | |
143 | ||
144 | ||
8429abe0 RW |
145 | struct l2vpn_pw * |
146 | l2vpn_pw_new(struct l2vpn *l2vpn, struct kif *kif) | |
147 | { | |
148 | struct l2vpn_pw *pw; | |
149 | ||
150 | if ((pw = calloc(1, sizeof(*pw))) == NULL) | |
151 | fatal("l2vpn_pw_new: calloc"); | |
152 | ||
153 | pw->l2vpn = l2vpn; | |
154 | strlcpy(pw->ifname, kif->ifname, sizeof(pw->ifname)); | |
155 | pw->ifindex = kif->ifindex; | |
156 | ||
157 | return (pw); | |
158 | } | |
159 | ||
160 | struct l2vpn_pw * | |
161 | l2vpn_pw_find(struct l2vpn *l2vpn, unsigned int ifindex) | |
162 | { | |
163 | struct l2vpn_pw *pw; | |
164 | ||
165 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) | |
166 | if (pw->ifindex == ifindex) | |
167 | return (pw); | |
eac6e3f0 RW |
168 | LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) |
169 | if (pw->ifindex == ifindex) | |
170 | return (pw); | |
171 | ||
172 | return (NULL); | |
173 | } | |
174 | ||
175 | struct l2vpn_pw * | |
176 | l2vpn_pw_find_name(struct l2vpn *l2vpn, const char *ifname) | |
177 | { | |
178 | struct l2vpn_pw *pw; | |
179 | ||
180 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) | |
181 | if (strcmp(pw->ifname, ifname) == 0) | |
182 | return (pw); | |
183 | LIST_FOREACH(pw, &l2vpn->pw_inactive_list, entry) | |
184 | if (strcmp(pw->ifname, ifname) == 0) | |
185 | return (pw); | |
8429abe0 RW |
186 | |
187 | return (NULL); | |
188 | } | |
189 | ||
190 | void | |
191 | l2vpn_pw_init(struct l2vpn_pw *pw) | |
192 | { | |
193 | struct fec fec; | |
194 | ||
195 | l2vpn_pw_reset(pw); | |
196 | ||
197 | l2vpn_pw_fec(pw, &fec); | |
198 | lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, | |
199 | 0, (void *)pw); | |
200 | } | |
201 | ||
202 | void | |
203 | l2vpn_pw_exit(struct l2vpn_pw *pw) | |
204 | { | |
205 | struct fec fec; | |
206 | ||
207 | l2vpn_pw_fec(pw, &fec); | |
208 | lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0); | |
209 | } | |
210 | ||
211 | static void | |
212 | l2vpn_pw_fec(struct l2vpn_pw *pw, struct fec *fec) | |
213 | { | |
214 | memset(fec, 0, sizeof(*fec)); | |
215 | fec->type = FEC_TYPE_PWID; | |
216 | fec->u.pwid.type = pw->l2vpn->pw_type; | |
217 | fec->u.pwid.pwid = pw->pwid; | |
218 | fec->u.pwid.lsr_id = pw->lsr_id; | |
219 | } | |
220 | ||
221 | void | |
222 | l2vpn_pw_reset(struct l2vpn_pw *pw) | |
223 | { | |
224 | pw->remote_group = 0; | |
225 | pw->remote_mtu = 0; | |
226 | pw->remote_status = 0; | |
227 | ||
228 | if (pw->flags & F_PW_CWORD_CONF) | |
229 | pw->flags |= F_PW_CWORD; | |
230 | else | |
231 | pw->flags &= ~F_PW_CWORD; | |
232 | ||
233 | if (pw->flags & F_PW_STATUSTLV_CONF) | |
234 | pw->flags |= F_PW_STATUSTLV; | |
235 | else | |
236 | pw->flags &= ~F_PW_STATUSTLV; | |
237 | } | |
238 | ||
239 | int | |
240 | l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) | |
241 | { | |
242 | struct fec fec; | |
243 | struct fec_node *fn; | |
244 | ||
245 | /* check for a remote label */ | |
246 | if (fnh->remote_label == NO_LABEL) | |
247 | return (0); | |
248 | ||
249 | /* MTUs must match */ | |
250 | if (pw->l2vpn->mtu != pw->remote_mtu) | |
251 | return (0); | |
252 | ||
253 | /* check pw status if applicable */ | |
254 | if ((pw->flags & F_PW_STATUSTLV) && | |
255 | pw->remote_status != PW_FORWARDING) | |
256 | return (0); | |
257 | ||
258 | /* check for a working lsp to the nexthop */ | |
259 | memset(&fec, 0, sizeof(fec)); | |
260 | switch (pw->af) { | |
261 | case AF_INET: | |
262 | fec.type = FEC_TYPE_IPV4; | |
263 | fec.u.ipv4.prefix = pw->addr.v4; | |
264 | fec.u.ipv4.prefixlen = 32; | |
265 | break; | |
266 | case AF_INET6: | |
267 | fec.type = FEC_TYPE_IPV6; | |
268 | fec.u.ipv6.prefix = pw->addr.v6; | |
269 | fec.u.ipv6.prefixlen = 128; | |
270 | break; | |
271 | default: | |
272 | fatalx("l2vpn_pw_ok: unknown af"); | |
273 | } | |
274 | ||
275 | fn = (struct fec_node *)fec_find(&ft, &fec); | |
276 | if (fn == NULL || fn->local_label == NO_LABEL) | |
277 | return (0); | |
278 | /* | |
279 | * Need to ensure that there's a label binding for all nexthops. | |
280 | * Otherwise, ECMP for this route could render the pseudowire unusable. | |
281 | */ | |
282 | LIST_FOREACH(fnh, &fn->nexthops, entry) | |
283 | if (fnh->remote_label == NO_LABEL) | |
284 | return (0); | |
285 | ||
286 | return (1); | |
287 | } | |
288 | ||
289 | int | |
290 | l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map) | |
291 | { | |
292 | struct l2vpn_pw *pw; | |
293 | struct status_tlv st; | |
294 | ||
295 | /* NOTE: thanks martini & friends for all this mess */ | |
296 | ||
297 | pw = (struct l2vpn_pw *) fn->data; | |
298 | if (pw == NULL) | |
299 | /* | |
300 | * pseudowire not configured, return and record | |
301 | * the mapping later | |
302 | */ | |
303 | return (0); | |
304 | ||
305 | /* RFC4447 - Section 6.2: control word negotiation */ | |
306 | if (fec_find(&ln->sent_map, &fn->fec)) { | |
307 | if ((map->flags & F_MAP_PW_CWORD) && | |
308 | !(pw->flags & F_PW_CWORD_CONF)) { | |
309 | /* ignore the received label mapping */ | |
310 | return (1); | |
311 | } else if (!(map->flags & F_MAP_PW_CWORD) && | |
312 | (pw->flags & F_PW_CWORD_CONF)) { | |
313 | /* append a "Wrong C-bit" status code */ | |
314 | st.status_code = S_WRONG_CBIT; | |
315 | st.msg_id = map->msg_id; | |
316 | st.msg_type = htons(MSG_TYPE_LABELMAPPING); | |
317 | lde_send_labelwithdraw(ln, fn, NO_LABEL, &st); | |
318 | ||
319 | pw->flags &= ~F_PW_CWORD; | |
320 | lde_send_labelmapping(ln, fn, 1); | |
321 | } | |
322 | } else if (map->flags & F_MAP_PW_CWORD) { | |
323 | if (pw->flags & F_PW_CWORD_CONF) | |
324 | pw->flags |= F_PW_CWORD; | |
325 | else | |
326 | /* act as if no label mapping had been received */ | |
327 | return (1); | |
328 | } else | |
329 | pw->flags &= ~F_PW_CWORD; | |
330 | ||
331 | /* RFC4447 - Section 5.4.3: pseudowire status negotiation */ | |
332 | if (fec_find(&ln->recv_map, &fn->fec) == NULL && | |
333 | !(map->flags & F_MAP_PW_STATUS)) | |
334 | pw->flags &= ~F_PW_STATUSTLV; | |
335 | ||
336 | return (0); | |
337 | } | |
338 | ||
339 | void | |
340 | l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec) | |
341 | { | |
342 | struct notify_msg nm; | |
343 | ||
344 | memset(&nm, 0, sizeof(nm)); | |
345 | nm.status_code = S_PW_STATUS; | |
346 | nm.pw_status = status; | |
347 | nm.flags |= F_NOTIF_PW_STATUS; | |
348 | lde_fec2map(fec, &nm.fec); | |
349 | nm.flags |= F_NOTIF_FEC; | |
350 | ||
351 | lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0, | |
352 | &nm, sizeof(nm)); | |
353 | } | |
354 | ||
355 | void | |
356 | l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) | |
357 | { | |
358 | struct fec fec; | |
359 | struct fec_node *fn; | |
360 | struct fec_nh *fnh; | |
361 | struct l2vpn_pw *pw; | |
362 | ||
363 | /* TODO group wildcard */ | |
364 | if (!(nm->fec.flags & F_MAP_PW_ID)) | |
365 | return; | |
366 | ||
367 | lde_map2fec(&nm->fec, ln->id, &fec); | |
368 | fn = (struct fec_node *)fec_find(&ft, &fec); | |
369 | if (fn == NULL) | |
370 | /* unknown fec */ | |
371 | return; | |
372 | ||
373 | pw = (struct l2vpn_pw *) fn->data; | |
374 | if (pw == NULL) | |
375 | return; | |
376 | ||
377 | fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0); | |
378 | if (fnh == NULL) | |
379 | return; | |
380 | ||
381 | /* remote status didn't change */ | |
382 | if (pw->remote_status == nm->pw_status) | |
383 | return; | |
384 | ||
385 | pw->remote_status = nm->pw_status; | |
386 | ||
387 | if (l2vpn_pw_ok(pw, fnh)) | |
388 | lde_send_change_klabel(fn, fnh); | |
389 | else | |
390 | lde_send_delete_klabel(fn, fnh); | |
391 | } | |
392 | ||
393 | void | |
394 | l2vpn_sync_pws(int af, union ldpd_addr *addr) | |
395 | { | |
396 | struct l2vpn *l2vpn; | |
397 | struct l2vpn_pw *pw; | |
398 | struct fec fec; | |
399 | struct fec_node *fn; | |
400 | struct fec_nh *fnh; | |
401 | ||
402 | LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) { | |
403 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) { | |
404 | if (af != pw->af || ldp_addrcmp(af, &pw->addr, addr)) | |
405 | continue; | |
406 | ||
407 | l2vpn_pw_fec(pw, &fec); | |
408 | fn = (struct fec_node *)fec_find(&ft, &fec); | |
409 | if (fn == NULL) | |
410 | continue; | |
411 | fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *) | |
412 | &pw->lsr_id, 0); | |
413 | if (fnh == NULL) | |
414 | continue; | |
415 | ||
416 | if (l2vpn_pw_ok(pw, fnh)) | |
417 | lde_send_change_klabel(fn, fnh); | |
418 | else | |
419 | lde_send_delete_klabel(fn, fnh); | |
420 | } | |
421 | } | |
422 | } | |
423 | ||
424 | void | |
425 | l2vpn_pw_ctl(pid_t pid) | |
426 | { | |
427 | struct l2vpn *l2vpn; | |
428 | struct l2vpn_pw *pw; | |
429 | static struct ctl_pw pwctl; | |
430 | ||
431 | LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) | |
432 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) { | |
433 | memset(&pwctl, 0, sizeof(pwctl)); | |
eac6e3f0 RW |
434 | strlcpy(pwctl.l2vpn_name, pw->l2vpn->name, |
435 | sizeof(pwctl.l2vpn_name)); | |
8429abe0 RW |
436 | strlcpy(pwctl.ifname, pw->ifname, |
437 | sizeof(pwctl.ifname)); | |
438 | pwctl.pwid = pw->pwid; | |
439 | pwctl.lsr_id = pw->lsr_id; | |
440 | pwctl.status = pw->flags & F_PW_STATUS_UP; | |
441 | ||
442 | lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0, | |
443 | pid, &pwctl, sizeof(pwctl)); | |
444 | } | |
445 | } | |
446 | ||
447 | void | |
448 | l2vpn_binding_ctl(pid_t pid) | |
449 | { | |
450 | struct fec *f; | |
451 | struct fec_node *fn; | |
452 | struct lde_map *me; | |
453 | struct l2vpn_pw *pw; | |
454 | static struct ctl_pw pwctl; | |
455 | ||
456 | RB_FOREACH(f, fec_tree, &ft) { | |
457 | if (f->type != FEC_TYPE_PWID) | |
458 | continue; | |
459 | ||
460 | fn = (struct fec_node *)f; | |
461 | if (fn->local_label == NO_LABEL && | |
462 | LIST_EMPTY(&fn->downstream)) | |
463 | continue; | |
464 | ||
465 | memset(&pwctl, 0, sizeof(pwctl)); | |
466 | pwctl.type = f->u.pwid.type; | |
467 | pwctl.pwid = f->u.pwid.pwid; | |
468 | pwctl.lsr_id = f->u.pwid.lsr_id; | |
469 | ||
470 | pw = (struct l2vpn_pw *) fn->data; | |
471 | if (pw) { | |
472 | pwctl.local_label = fn->local_label; | |
473 | pwctl.local_gid = 0; | |
474 | pwctl.local_ifmtu = pw->l2vpn->mtu; | |
eac6e3f0 RW |
475 | pwctl.local_cword = (pw->flags & F_PW_CWORD_CONF) ? |
476 | 1 : 0; | |
8429abe0 RW |
477 | } else |
478 | pwctl.local_label = NO_LABEL; | |
479 | ||
480 | LIST_FOREACH(me, &fn->downstream, entry) | |
481 | if (f->u.pwid.lsr_id.s_addr == me->nexthop->id.s_addr) | |
482 | break; | |
483 | ||
484 | if (me) { | |
485 | pwctl.remote_label = me->map.label; | |
486 | pwctl.remote_gid = me->map.fec.pwid.group_id; | |
487 | if (me->map.flags & F_MAP_PW_IFMTU) | |
488 | pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu; | |
eac6e3f0 RW |
489 | if (pw) |
490 | pwctl.remote_cword = (pw->flags & F_PW_CWORD) ? | |
491 | 1 : 0; | |
8429abe0 RW |
492 | |
493 | lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, | |
494 | 0, pid, &pwctl, sizeof(pwctl)); | |
495 | } else if (pw) { | |
496 | pwctl.remote_label = NO_LABEL; | |
497 | ||
498 | lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, | |
499 | 0, pid, &pwctl, sizeof(pwctl)); | |
500 | } | |
501 | } | |
502 | } | |
503 | ||
504 | /* ldpe */ | |
505 | ||
506 | void | |
507 | ldpe_l2vpn_init(struct l2vpn *l2vpn) | |
508 | { | |
509 | struct l2vpn_pw *pw; | |
510 | ||
511 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) | |
512 | ldpe_l2vpn_pw_init(pw); | |
513 | } | |
514 | ||
515 | void | |
516 | ldpe_l2vpn_exit(struct l2vpn *l2vpn) | |
517 | { | |
518 | struct l2vpn_pw *pw; | |
519 | ||
520 | LIST_FOREACH(pw, &l2vpn->pw_list, entry) | |
521 | ldpe_l2vpn_pw_exit(pw); | |
522 | } | |
523 | ||
524 | void | |
525 | ldpe_l2vpn_pw_init(struct l2vpn_pw *pw) | |
526 | { | |
527 | struct tnbr *tnbr; | |
528 | ||
529 | tnbr = tnbr_find(leconf, pw->af, &pw->addr); | |
530 | if (tnbr == NULL) { | |
eac6e3f0 | 531 | tnbr = tnbr_new(pw->af, &pw->addr); |
8429abe0 RW |
532 | tnbr_update(tnbr); |
533 | LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry); | |
534 | } | |
535 | ||
536 | tnbr->pw_count++; | |
537 | } | |
538 | ||
539 | void | |
540 | ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw) | |
541 | { | |
542 | struct tnbr *tnbr; | |
543 | ||
544 | tnbr = tnbr_find(leconf, pw->af, &pw->addr); | |
545 | if (tnbr) { | |
546 | tnbr->pw_count--; | |
547 | tnbr_check(tnbr); | |
548 | } | |
549 | } |