]> git.proxmox.com Git - mirror_frr.git/blob - ldpd/l2vpn.c
zebra, ldpd: fix display of pseudowire status
[mirror_frr.git] / ldpd / l2vpn.c
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
22 #include <zebra.h>
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 static __inline int l2vpn_compare(const struct l2vpn *, const struct l2vpn *);
31 static __inline int l2vpn_if_compare(const struct l2vpn_if *, const struct l2vpn_if *);
32 static __inline int l2vpn_pw_compare(const struct l2vpn_pw *, const struct l2vpn_pw *);
33
34 RB_GENERATE(l2vpn_head, l2vpn, entry, l2vpn_compare)
35 RB_GENERATE(l2vpn_if_head, l2vpn_if, entry, l2vpn_if_compare)
36 RB_GENERATE(l2vpn_pw_head, l2vpn_pw, entry, l2vpn_pw_compare)
37
38 static __inline int
39 l2vpn_compare(const struct l2vpn *a, const struct l2vpn *b)
40 {
41 return (strcmp(a->name, b->name));
42 }
43
44 struct l2vpn *
45 l2vpn_new(const char *name)
46 {
47 struct l2vpn *l2vpn;
48
49 if ((l2vpn = calloc(1, sizeof(*l2vpn))) == NULL)
50 fatal("l2vpn_new: calloc");
51
52 strlcpy(l2vpn->name, name, sizeof(l2vpn->name));
53
54 /* set default values */
55 l2vpn->mtu = DEFAULT_L2VPN_MTU;
56 l2vpn->pw_type = DEFAULT_PW_TYPE;
57
58 RB_INIT(l2vpn_if_head, &l2vpn->if_tree);
59 RB_INIT(l2vpn_pw_head, &l2vpn->pw_tree);
60 RB_INIT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
61
62 return (l2vpn);
63 }
64
65 struct l2vpn *
66 l2vpn_find(struct ldpd_conf *xconf, const char *name)
67 {
68 struct l2vpn l2vpn;
69 strlcpy(l2vpn.name, name, sizeof(l2vpn.name));
70 return (RB_FIND(l2vpn_head, &xconf->l2vpn_tree, &l2vpn));
71 }
72
73 void
74 l2vpn_del(struct l2vpn *l2vpn)
75 {
76 struct l2vpn_if *lif;
77 struct l2vpn_pw *pw;
78
79 while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
80 RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
81 free(lif);
82 }
83 while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
84 RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
85 free(pw);
86 }
87 while ((pw = RB_ROOT(l2vpn_pw_head,
88 &l2vpn->pw_inactive_tree)) != NULL) {
89 RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
90 free(pw);
91 }
92
93 free(l2vpn);
94 }
95
96 void
97 l2vpn_init(struct l2vpn *l2vpn)
98 {
99 struct l2vpn_pw *pw;
100
101 RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
102 l2vpn_pw_init(pw);
103 }
104
105 void
106 l2vpn_exit(struct l2vpn *l2vpn)
107 {
108 struct l2vpn_pw *pw;
109
110 RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
111 l2vpn_pw_exit(pw);
112 }
113
114 static __inline int
115 l2vpn_if_compare(const struct l2vpn_if *a, const struct l2vpn_if *b)
116 {
117 return (if_cmp_name_func((char *)a->ifname, (char *)b->ifname));
118 }
119
120 struct l2vpn_if *
121 l2vpn_if_new(struct l2vpn *l2vpn, const char *ifname)
122 {
123 struct l2vpn_if *lif;
124
125 if ((lif = calloc(1, sizeof(*lif))) == NULL)
126 fatal("l2vpn_if_new: calloc");
127
128 lif->l2vpn = l2vpn;
129 strlcpy(lif->ifname, ifname, sizeof(lif->ifname));
130
131 return (lif);
132 }
133
134 struct l2vpn_if *
135 l2vpn_if_find(struct l2vpn *l2vpn, const char *ifname)
136 {
137 struct l2vpn_if lif;
138 strlcpy(lif.ifname, ifname, sizeof(lif.ifname));
139 return (RB_FIND(l2vpn_if_head, &l2vpn->if_tree, &lif));
140 }
141
142 void
143 l2vpn_if_update_info(struct l2vpn_if *lif, struct kif *kif)
144 {
145 lif->ifindex = kif->ifindex;
146 lif->operative = kif->operative;
147 memcpy(lif->mac, kif->mac, sizeof(lif->mac));
148 }
149
150 void
151 l2vpn_if_update(struct l2vpn_if *lif)
152 {
153 struct l2vpn *l2vpn = lif->l2vpn;
154 struct l2vpn_pw *pw;
155 struct map fec;
156 struct nbr *nbr;
157
158 if (lif->operative)
159 return;
160
161 RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
162 nbr = nbr_find_ldpid(pw->lsr_id.s_addr);
163 if (nbr == NULL)
164 continue;
165
166 memset(&fec, 0, sizeof(fec));
167 fec.type = MAP_TYPE_PWID;
168 fec.fec.pwid.type = l2vpn->pw_type;
169 fec.fec.pwid.group_id = 0;
170 fec.flags |= F_MAP_PW_ID;
171 fec.fec.pwid.pwid = pw->pwid;
172
173 send_mac_withdrawal(nbr, &fec, lif->mac);
174 }
175 }
176
177 static __inline int
178 l2vpn_pw_compare(const struct l2vpn_pw *a, const struct l2vpn_pw *b)
179 {
180 return (if_cmp_name_func((char *)a->ifname, (char *)b->ifname));
181 }
182
183 struct l2vpn_pw *
184 l2vpn_pw_new(struct l2vpn *l2vpn, const char *ifname)
185 {
186 struct l2vpn_pw *pw;
187
188 if ((pw = calloc(1, sizeof(*pw))) == NULL)
189 fatal("l2vpn_pw_new: calloc");
190
191 pw->l2vpn = l2vpn;
192 strlcpy(pw->ifname, ifname, sizeof(pw->ifname));
193
194 return (pw);
195 }
196
197 struct l2vpn_pw *
198 l2vpn_pw_find(struct l2vpn *l2vpn, const char *ifname)
199 {
200 struct l2vpn_pw *pw;
201 struct l2vpn_pw s;
202
203 strlcpy(s.ifname, ifname, sizeof(s.ifname));
204 pw = RB_FIND(l2vpn_pw_head, &l2vpn->pw_tree, &s);
205 if (pw)
206 return (pw);
207 return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_inactive_tree, &s));
208 }
209
210 struct l2vpn_pw *
211 l2vpn_pw_find_active(struct l2vpn *l2vpn, const char *ifname)
212 {
213 struct l2vpn_pw s;
214
215 strlcpy(s.ifname, ifname, sizeof(s.ifname));
216 return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_tree, &s));
217 }
218
219 struct l2vpn_pw *
220 l2vpn_pw_find_inactive(struct l2vpn *l2vpn, const char *ifname)
221 {
222 struct l2vpn_pw s;
223
224 strlcpy(s.ifname, ifname, sizeof(s.ifname));
225 return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_inactive_tree, &s));
226 }
227
228 void
229 l2vpn_pw_update_info(struct l2vpn_pw *pw, struct kif *kif)
230 {
231 pw->ifindex = kif->ifindex;
232 }
233
234 void
235 l2vpn_pw_init(struct l2vpn_pw *pw)
236 {
237 struct fec fec;
238 struct zapi_pw zpw;
239
240 l2vpn_pw_reset(pw);
241
242 pw2zpw(pw, &zpw);
243 lde_imsg_compose_parent(IMSG_KPW_ADD, 0, &zpw, sizeof(zpw));
244
245 l2vpn_pw_fec(pw, &fec);
246 lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0,
247 0, (void *)pw);
248 lde_kernel_update(&fec);
249 }
250
251 void
252 l2vpn_pw_exit(struct l2vpn_pw *pw)
253 {
254 struct fec fec;
255 struct zapi_pw zpw;
256
257 l2vpn_pw_fec(pw, &fec);
258 lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0);
259 lde_kernel_update(&fec);
260
261 pw2zpw(pw, &zpw);
262 lde_imsg_compose_parent(IMSG_KPW_DELETE, 0, &zpw, sizeof(zpw));
263 }
264
265 static void
266 l2vpn_pw_fec(struct l2vpn_pw *pw, struct fec *fec)
267 {
268 memset(fec, 0, sizeof(*fec));
269 fec->type = FEC_TYPE_PWID;
270 fec->u.pwid.type = pw->l2vpn->pw_type;
271 fec->u.pwid.pwid = pw->pwid;
272 fec->u.pwid.lsr_id = pw->lsr_id;
273 }
274
275 void
276 l2vpn_pw_reset(struct l2vpn_pw *pw)
277 {
278 pw->remote_group = 0;
279 pw->remote_mtu = 0;
280 pw->local_status = PW_FORWARDING;
281 pw->remote_status = PW_NOT_FORWARDING;
282
283 if (pw->flags & F_PW_CWORD_CONF)
284 pw->flags |= F_PW_CWORD;
285 else
286 pw->flags &= ~F_PW_CWORD;
287
288 if (pw->flags & F_PW_STATUSTLV_CONF)
289 pw->flags |= F_PW_STATUSTLV;
290 else
291 pw->flags &= ~F_PW_STATUSTLV;
292 }
293
294 int
295 l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh)
296 {
297 /* check for a remote label */
298 if (fnh->remote_label == NO_LABEL)
299 return (0);
300
301 /* MTUs must match */
302 if (pw->l2vpn->mtu != pw->remote_mtu)
303 return (0);
304
305 /* check pw status if applicable */
306 if ((pw->flags & F_PW_STATUSTLV) &&
307 pw->remote_status != PW_FORWARDING)
308 return (0);
309
310 return (1);
311 }
312
313 int
314 l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map)
315 {
316 struct l2vpn_pw *pw;
317 struct status_tlv st;
318
319 /* NOTE: thanks martini & friends for all this mess */
320
321 pw = (struct l2vpn_pw *) fn->data;
322 if (pw == NULL)
323 /*
324 * pseudowire not configured, return and record
325 * the mapping later
326 */
327 return (0);
328
329 /* RFC4447 - Section 6.2: control word negotiation */
330 if (fec_find(&ln->sent_map, &fn->fec)) {
331 if ((map->flags & F_MAP_PW_CWORD) &&
332 !(pw->flags & F_PW_CWORD_CONF)) {
333 /* ignore the received label mapping */
334 return (1);
335 } else if (!(map->flags & F_MAP_PW_CWORD) &&
336 (pw->flags & F_PW_CWORD_CONF)) {
337 /* append a "Wrong C-bit" status code */
338 st.status_code = S_WRONG_CBIT;
339 st.msg_id = map->msg_id;
340 st.msg_type = htons(MSG_TYPE_LABELMAPPING);
341 lde_send_labelwithdraw(ln, fn, NULL, &st);
342
343 pw->flags &= ~F_PW_CWORD;
344 lde_send_labelmapping(ln, fn, 1);
345 }
346 } else if (map->flags & F_MAP_PW_CWORD) {
347 if (pw->flags & F_PW_CWORD_CONF)
348 pw->flags |= F_PW_CWORD;
349 else
350 /* act as if no label mapping had been received */
351 return (1);
352 } else
353 pw->flags &= ~F_PW_CWORD;
354
355 /* RFC4447 - Section 5.4.3: pseudowire status negotiation */
356 if (fec_find(&ln->recv_map, &fn->fec) == NULL &&
357 !(map->flags & F_MAP_PW_STATUS))
358 pw->flags &= ~F_PW_STATUSTLV;
359
360 return (0);
361 }
362
363 void
364 l2vpn_send_pw_status(struct lde_nbr *ln, uint32_t status, struct fec *fec)
365 {
366 struct notify_msg nm;
367
368 memset(&nm, 0, sizeof(nm));
369 nm.status_code = S_PW_STATUS;
370 nm.pw_status = status;
371 nm.flags |= F_NOTIF_PW_STATUS;
372 lde_fec2map(fec, &nm.fec);
373 nm.flags |= F_NOTIF_FEC;
374
375 lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm,
376 sizeof(nm));
377 }
378
379 void
380 l2vpn_send_pw_status_wcard(struct lde_nbr *ln, uint32_t status,
381 uint16_t pw_type, uint32_t group_id)
382 {
383 struct notify_msg nm;
384
385 memset(&nm, 0, sizeof(nm));
386 nm.status_code = S_PW_STATUS;
387 nm.pw_status = status;
388 nm.flags |= F_NOTIF_PW_STATUS;
389 nm.fec.type = MAP_TYPE_PWID;
390 nm.fec.fec.pwid.type = pw_type;
391 nm.fec.fec.pwid.group_id = group_id;
392 nm.flags |= F_NOTIF_FEC;
393
394 lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm,
395 sizeof(nm));
396 }
397
398 void
399 l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
400 {
401 struct fec fec;
402 struct fec_node *fn;
403 struct fec_nh *fnh;
404 struct l2vpn_pw *pw;
405
406 if (nm->fec.type == MAP_TYPE_TYPED_WCARD ||
407 !(nm->fec.flags & F_MAP_PW_ID)) {
408 l2vpn_recv_pw_status_wcard(ln, nm);
409 return;
410 }
411
412 lde_map2fec(&nm->fec, ln->id, &fec);
413 fn = (struct fec_node *)fec_find(&ft, &fec);
414 if (fn == NULL)
415 /* unknown fec */
416 return;
417
418 pw = (struct l2vpn_pw *) fn->data;
419 if (pw == NULL)
420 return;
421
422 fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0, 0);
423 if (fnh == NULL)
424 return;
425
426 /* remote status didn't change */
427 if (pw->remote_status == nm->pw_status)
428 return;
429 pw->remote_status = nm->pw_status;
430
431 if (l2vpn_pw_ok(pw, fnh))
432 lde_send_change_klabel(fn, fnh);
433 else
434 lde_send_delete_klabel(fn, fnh);
435 }
436
437 /* RFC4447 PWid group wildcard */
438 void
439 l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm)
440 {
441 struct fec *f;
442 struct fec_node *fn;
443 struct fec_nh *fnh;
444 struct l2vpn_pw *pw;
445 struct map *wcard = &nm->fec;
446
447 RB_FOREACH(f, fec_tree, &ft) {
448 fn = (struct fec_node *)f;
449 if (fn->fec.type != FEC_TYPE_PWID)
450 continue;
451
452 pw = (struct l2vpn_pw *) fn->data;
453 if (pw == NULL)
454 continue;
455
456 switch (wcard->type) {
457 case MAP_TYPE_TYPED_WCARD:
458 if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD &&
459 wcard->fec.twcard.u.pw_type != fn->fec.u.pwid.type)
460 continue;
461 break;
462 case MAP_TYPE_PWID:
463 if (wcard->fec.pwid.type != fn->fec.u.pwid.type)
464 continue;
465 if (wcard->fec.pwid.group_id != pw->remote_group)
466 continue;
467 break;
468 }
469
470 fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id,
471 0, 0);
472 if (fnh == NULL)
473 continue;
474
475 /* remote status didn't change */
476 if (pw->remote_status == nm->pw_status)
477 continue;
478 pw->remote_status = nm->pw_status;
479
480 if (l2vpn_pw_ok(pw, fnh))
481 lde_send_change_klabel(fn, fnh);
482 else
483 lde_send_delete_klabel(fn, fnh);
484 }
485 }
486
487 int
488 l2vpn_pw_status_update(struct zapi_pw_status *zpw)
489 {
490 struct l2vpn *l2vpn;
491 struct l2vpn_pw *pw = NULL;
492 struct lde_nbr *ln;
493 struct fec fec;
494 uint32_t local_status;
495
496 RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree) {
497 pw = l2vpn_pw_find(l2vpn, zpw->ifname);
498 if (pw)
499 break;
500 }
501 if (!pw) {
502 log_warnx("%s: pseudowire %s not found", __func__, zpw->ifname);
503 return (1);
504 }
505
506 if (zpw->status == PW_STATUS_UP)
507 local_status = PW_FORWARDING;
508 else
509 local_status = PW_NOT_FORWARDING;
510
511 /* local status didn't change */
512 if (pw->local_status == local_status)
513 return (0);
514 pw->local_status = local_status;
515
516 /* notify remote peer about the status update */
517 ln = lde_nbr_find_by_lsrid(pw->lsr_id);
518 if (ln == NULL)
519 return (0);
520 l2vpn_pw_fec(pw, &fec);
521 if (pw->flags & F_PW_STATUSTLV)
522 l2vpn_send_pw_status(ln, local_status, &fec);
523 else {
524 struct fec_node *fn;
525 fn = (struct fec_node *)fec_find(&ft, &fec);
526 if (fn) {
527 if (pw->local_status == PW_FORWARDING)
528 lde_send_labelmapping(ln, fn, 1);
529 else
530 lde_send_labelwithdraw(ln, fn, NULL, NULL);
531 }
532 }
533
534 return (0);
535 }
536
537 void
538 l2vpn_pw_ctl(pid_t pid)
539 {
540 struct l2vpn *l2vpn;
541 struct l2vpn_pw *pw;
542 static struct ctl_pw pwctl;
543
544 RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree)
545 RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
546 memset(&pwctl, 0, sizeof(pwctl));
547 strlcpy(pwctl.l2vpn_name, pw->l2vpn->name,
548 sizeof(pwctl.l2vpn_name));
549 strlcpy(pwctl.ifname, pw->ifname,
550 sizeof(pwctl.ifname));
551 pwctl.pwid = pw->pwid;
552 pwctl.lsr_id = pw->lsr_id;
553 if (pw->enabled &&
554 pw->local_status == PW_FORWARDING &&
555 pw->remote_status == PW_FORWARDING)
556 pwctl.status = 1;
557
558 lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0,
559 pid, &pwctl, sizeof(pwctl));
560 }
561 }
562
563 void
564 l2vpn_binding_ctl(pid_t pid)
565 {
566 struct fec *f;
567 struct fec_node *fn;
568 struct lde_map *me;
569 struct l2vpn_pw *pw;
570 static struct ctl_pw pwctl;
571
572 RB_FOREACH(f, fec_tree, &ft) {
573 if (f->type != FEC_TYPE_PWID)
574 continue;
575
576 fn = (struct fec_node *)f;
577 if (fn->local_label == NO_LABEL &&
578 RB_EMPTY(lde_map_head, &fn->downstream))
579 continue;
580
581 memset(&pwctl, 0, sizeof(pwctl));
582 pwctl.type = f->u.pwid.type;
583 pwctl.pwid = f->u.pwid.pwid;
584 pwctl.lsr_id = f->u.pwid.lsr_id;
585
586 pw = (struct l2vpn_pw *) fn->data;
587 if (pw) {
588 pwctl.local_label = fn->local_label;
589 pwctl.local_gid = 0;
590 pwctl.local_ifmtu = pw->l2vpn->mtu;
591 pwctl.local_cword = (pw->flags & F_PW_CWORD_CONF) ?
592 1 : 0;
593 } else
594 pwctl.local_label = NO_LABEL;
595
596 RB_FOREACH(me, lde_map_head, &fn->downstream)
597 if (f->u.pwid.lsr_id.s_addr == me->nexthop->id.s_addr)
598 break;
599
600 if (me) {
601 pwctl.remote_label = me->map.label;
602 pwctl.remote_gid = me->map.fec.pwid.group_id;
603 if (me->map.flags & F_MAP_PW_IFMTU)
604 pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu;
605 if (pw)
606 pwctl.remote_cword = (pw->flags & F_PW_CWORD) ?
607 1 : 0;
608
609 lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
610 0, pid, &pwctl, sizeof(pwctl));
611 } else if (pw) {
612 pwctl.remote_label = NO_LABEL;
613
614 lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
615 0, pid, &pwctl, sizeof(pwctl));
616 }
617 }
618 }
619
620 /* ldpe */
621
622 void
623 ldpe_l2vpn_init(struct l2vpn *l2vpn)
624 {
625 struct l2vpn_pw *pw;
626
627 RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
628 ldpe_l2vpn_pw_init(pw);
629 }
630
631 void
632 ldpe_l2vpn_exit(struct l2vpn *l2vpn)
633 {
634 struct l2vpn_pw *pw;
635
636 RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
637 ldpe_l2vpn_pw_exit(pw);
638 }
639
640 void
641 ldpe_l2vpn_pw_init(struct l2vpn_pw *pw)
642 {
643 struct tnbr *tnbr;
644
645 tnbr = tnbr_find(leconf, pw->af, &pw->addr);
646 if (tnbr == NULL) {
647 tnbr = tnbr_new(pw->af, &pw->addr);
648 tnbr_update(tnbr);
649 RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
650 }
651
652 tnbr->pw_count++;
653 }
654
655 void
656 ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw)
657 {
658 struct tnbr *tnbr;
659
660 tnbr = tnbr_find(leconf, pw->af, &pw->addr);
661 if (tnbr) {
662 tnbr->pw_count--;
663 tnbr_check(leconf, tnbr);
664 }
665 }