diff options
-rw-r--r-- | usr.sbin/ldpd/address.c | 6 | ||||
-rw-r--r-- | usr.sbin/ldpd/adjacency.c | 8 | ||||
-rw-r--r-- | usr.sbin/ldpd/control.c | 4 | ||||
-rw-r--r-- | usr.sbin/ldpd/hello.c | 4 | ||||
-rw-r--r-- | usr.sbin/ldpd/kroute.c | 115 | ||||
-rw-r--r-- | usr.sbin/ldpd/l2vpn.c | 498 | ||||
-rw-r--r-- | usr.sbin/ldpd/labelmapping.c | 338 | ||||
-rw-r--r-- | usr.sbin/ldpd/lde.c | 303 | ||||
-rw-r--r-- | usr.sbin/ldpd/lde.h | 56 | ||||
-rw-r--r-- | usr.sbin/ldpd/lde_lib.c | 211 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldp.h | 64 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpd.c | 190 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpd.conf.5 | 89 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpd.h | 111 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpe.c | 37 | ||||
-rw-r--r-- | usr.sbin/ldpd/ldpe.h | 13 | ||||
-rw-r--r-- | usr.sbin/ldpd/log.c | 91 | ||||
-rw-r--r-- | usr.sbin/ldpd/log.h | 7 | ||||
-rw-r--r-- | usr.sbin/ldpd/notification.c | 160 |
19 files changed, 2028 insertions, 277 deletions
diff --git a/usr.sbin/ldpd/address.c b/usr.sbin/ldpd/address.c index 4f998e0e55d..50bcd1756a6 100644 --- a/usr.sbin/ldpd/address.c +++ b/usr.sbin/ldpd/address.c @@ -1,4 +1,4 @@ -/* $OpenBSD: address.c,v 1.16 2015/07/21 04:39:28 renato Exp $ */ +/* $OpenBSD: address.c,v 1.17 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -111,7 +111,7 @@ recv_address(struct nbr *nbr, char *buf, u_int16_t len) } /* For now we only support IPv4 */ - if (alt.family != htons(ADDR_IPV4)) { + if (alt.family != htons(AF_IPV4)) { send_notification_nbr(nbr, S_UNSUP_ADDR, addr.msgid, addr.type); return (-1); } @@ -148,7 +148,7 @@ gen_address_list_tlv(struct ibuf *buf, struct if_addr *if_addr, alt.type = TLV_TYPE_ADDRLIST; alt.length = htons(size); /* XXX: just ipv4 for now */ - alt.family = htons(ADDR_IPV4); + alt.family = htons(AF_IPV4); ibuf_add(buf, &alt, sizeof(alt)); diff --git a/usr.sbin/ldpd/adjacency.c b/usr.sbin/ldpd/adjacency.c index bce3151323e..da6b429897f 100644 --- a/usr.sbin/ldpd/adjacency.c +++ b/usr.sbin/ldpd/adjacency.c @@ -1,4 +1,4 @@ -/* $OpenBSD: adjacency.c,v 1.6 2015/07/21 04:43:28 renato Exp $ */ +/* $OpenBSD: adjacency.c,v 1.7 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -148,7 +148,8 @@ adj_itimer(int fd, short event, void *arg) LIST_REMOVE(adj, iface_entry); break; case HELLO_TARGETED: - if (!(adj->source.target->flags & F_TNBR_CONFIGURED)) { + if (!(adj->source.target->flags & F_TNBR_CONFIGURED) && + adj->source.target->pw_count == 0) { /* remove dynamic targeted neighbor */ LIST_REMOVE(adj->source.target, entry); tnbr_del(adj->source.target); @@ -210,7 +211,8 @@ tnbr_del(struct tnbr *tnbr) struct tnbr * tnbr_check(struct tnbr *tnbr) { - if (!(tnbr->flags & (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC))) { + if (!(tnbr->flags & (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) && + tnbr->pw_count == 0) { LIST_REMOVE(tnbr, entry); tnbr_del(tnbr); return (NULL); diff --git a/usr.sbin/ldpd/control.c b/usr.sbin/ldpd/control.c index 34ca615f0f2..d6c0e4a7025 100644 --- a/usr.sbin/ldpd/control.c +++ b/usr.sbin/ldpd/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.15 2015/02/09 11:54:24 claudio Exp $ */ +/* $OpenBSD: control.c,v 1.16 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -251,6 +251,8 @@ control_dispatch_imsg(int fd, short event, void *bula) ldpe_adj_ctl(c); break; case IMSG_CTL_SHOW_LIB: + case IMSG_CTL_SHOW_L2VPN_PW: + case IMSG_CTL_SHOW_L2VPN_BINDING: c->iev.ibuf.pid = imsg.hdr.pid; ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid, imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); diff --git a/usr.sbin/ldpd/hello.c b/usr.sbin/ldpd/hello.c index 891042b0fde..1ef5b19fde3 100644 --- a/usr.sbin/ldpd/hello.c +++ b/usr.sbin/ldpd/hello.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hello.c,v 1.27 2015/07/21 04:43:28 renato Exp $ */ +/* $OpenBSD: hello.c,v 1.28 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -66,7 +66,7 @@ send_hello(enum hello_type type, struct iface *iface, struct tnbr *tnbr) dst.sin_addr.s_addr = tnbr->addr.s_addr; holdtime = tnbr->hello_holdtime; flags = TARGETED_HELLO; - if (tnbr->flags & F_TNBR_CONFIGURED) + if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count) flags |= REQUEST_TARG_HELLO; fd = tnbr->discovery_fd; break; diff --git a/usr.sbin/ldpd/kroute.c b/usr.sbin/ldpd/kroute.c index d29500611dc..2f94690fb97 100644 --- a/usr.sbin/ldpd/kroute.c +++ b/usr.sbin/ldpd/kroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kroute.c,v 1.45 2015/07/21 04:43:28 renato Exp $ */ +/* $OpenBSD: kroute.c,v 1.46 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -20,6 +20,7 @@ #include <sys/types.h> #include <sys/socket.h> +#include <sys/ioctl.h> #include <sys/sysctl.h> #include <sys/tree.h> #include <sys/uio.h> @@ -47,6 +48,7 @@ struct { pid_t pid; int fib_sync; int fd; + int ioctl_fd; struct event ev; } kr_state; @@ -79,6 +81,7 @@ struct kif_node { RB_ENTRY(kif_node) entry; TAILQ_HEAD(, kif_addr) addrs; struct kif k; + struct kpw *kpw; }; void kr_redist_remove(struct kroute *); @@ -208,6 +211,12 @@ kr_init(int fs) kr_dispatch_msg, NULL); event_add(&kr_state.ev, NULL); + if ((kr_state.ioctl_fd = socket(AF_INET, + SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1) { + log_warn("kr_init: ioctl socket"); + return (-1); + } + return (0); } @@ -294,6 +303,7 @@ kr_fib_couple(void) struct kroute_prefix *kp; struct kroute_priority *kprio; struct kroute_node *kn; + struct kif_node *kif; if (kr_state.fib_sync == 1) /* already coupled */ return; @@ -319,6 +329,10 @@ kr_fib_couple(void) } } + RB_FOREACH(kif, kif_tree, &kit) + if (kif->kpw) + kmpw_install(kif->k.ifname, kif->kpw); + log_info("kernel routing table coupled"); } @@ -329,6 +343,7 @@ kr_fib_decouple(void) struct kroute_priority *kprio; struct kroute_node *kn; u_int32_t rl; + struct kif_node *kif; if (kr_state.fib_sync == 0) /* already decoupled */ return; @@ -356,6 +371,10 @@ kr_fib_decouple(void) } } + RB_FOREACH(kif, kif_tree, &kit) + if (kif->kpw) + kmpw_uninstall(kif->k.ifname, kif->kpw); + kr_state.fib_sync = 0; log_info("kernel routing table decoupled"); } @@ -1427,3 +1446,97 @@ rtmsg_process(char *buf, size_t len) return (offset); } + +void +kmpw_set(struct kpw *kpw) +{ + struct kif_node *kif; + + kif = kif_find(kpw->ifindex); + if (kif == NULL) { + log_warn("kmpw_set: failed to find mpw by index (%u)", + kpw->ifindex); + return; + } + + if (kif->kpw == NULL) + kif->kpw = malloc(sizeof(*kif->kpw)); + memcpy(kif->kpw, kpw, sizeof(*kif->kpw)); + + kmpw_install(kif->k.ifname, kpw); +} + +void +kmpw_unset(struct kpw *kpw) +{ + struct kif_node *kif; + + kif = kif_find(kpw->ifindex); + if (kif == NULL) { + log_warn("kmpw_unset: failed to find mpw by index (%u)", + kpw->ifindex); + return; + } + + if (kif->kpw == NULL) { + log_warn("kmpw_unset: %s is not set", kif->k.ifname); + return; + } + + free(kif->kpw); + kif->kpw = NULL; + kmpw_uninstall(kif->k.ifname, kpw); +} + +void +kmpw_install(const char *ifname, struct kpw *kpw) +{ + struct sockaddr_in *sin; + struct ifreq ifr; + struct ifmpwreq imr; + + memset(&imr, 0, sizeof(imr)); + switch (kpw->pw_type) { + case PW_TYPE_ETHERNET: + imr.imr_type = IMR_TYPE_ETHERNET; + break; + case PW_TYPE_ETHERNET_TAGGED: + imr.imr_type = IMR_TYPE_ETHERNET_TAGGED; + break; + + default: + log_warn("kmpw_install: unhandled pseudowire type (%#X)", + kpw->pw_type); + } + + if (kpw->flags & F_PW_CONTROLWORD) + imr.imr_flags |= IMR_FLAG_CONTROLWORD; + + sin = (struct sockaddr_in *) &imr.imr_nexthop; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = kpw->nexthop.s_addr; + sin->sin_len = sizeof(struct sockaddr_in); + + imr.imr_lshim.shim_label = kpw->local_label; + imr.imr_rshim.shim_label = kpw->remote_label; + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = (caddr_t) &imr; + if (ioctl(kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr)) + log_warn("ioctl SETMPWCFG"); +} + +void +kmpw_uninstall(const char *ifname, struct kpw *kpw) +{ + struct ifreq ifr; + struct ifmpwreq imr; + + memset(&imr, 0, sizeof(imr)); + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = (caddr_t) &imr; + if (ioctl(kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr)) + log_warn("ioctl SETMPWCFG"); +} diff --git a/usr.sbin/ldpd/l2vpn.c b/usr.sbin/ldpd/l2vpn.c new file mode 100644 index 00000000000..b93523c7779 --- /dev/null +++ b/usr.sbin/ldpd/l2vpn.c @@ -0,0 +1,498 @@ +/* $OpenBSD: l2vpn.c,v 1.1 2015/07/21 04:52:29 renato Exp $ */ + +/* + * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ldpd.h" +#include "lde.h" +#include "ldpe.h" +#include "control.h" +#include "log.h" + +RB_PROTOTYPE(fec_tree, fec, entry, fec_compare) +extern struct fec_tree ft; + +extern struct ldpd_conf *ldeconf; +extern struct ldpd_conf *leconf; + +struct l2vpn * +l2vpn_new(const char *name) +{ + struct l2vpn *l2vpn; + + if ((l2vpn = calloc(1, sizeof(*l2vpn))) == NULL) + err(1, "l2vpn_new: calloc"); + + strlcpy(l2vpn->name, name, sizeof(l2vpn->name)); + + /* set default values */ + l2vpn->mtu = DEFAULT_L2VPN_MTU; + l2vpn->pw_type = PW_TYPE_ETHERNET; + + return (l2vpn); +} + +struct l2vpn * +l2vpn_find(struct ldpd_conf *xconf, char *name) +{ + struct l2vpn *l2vpn; + + LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) + if (strcmp(l2vpn->name, name) == 0) + return (l2vpn); + + return (NULL); +} + +void +l2vpn_del(struct l2vpn *l2vpn) +{ + struct l2vpn_if *lif; + struct l2vpn_pw *pw; + + while ((lif = LIST_FIRST(&l2vpn->if_list)) != NULL) { + LIST_REMOVE(lif, entry); + l2vpn_if_del(lif); + } + while ((pw = LIST_FIRST(&l2vpn->pw_list)) != NULL) { + LIST_REMOVE(pw, entry); + l2vpn_pw_del(pw); + } + + free(l2vpn); +} + +void +l2vpn_init(struct l2vpn *l2vpn) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + l2vpn_pw_init(pw); +} + +struct l2vpn_if * +l2vpn_if_new(struct l2vpn *l2vpn, struct kif *kif) +{ + struct l2vpn_if *lif; + + if ((lif = calloc(1, sizeof(*lif))) == NULL) + err(1, "l2vpn_if_new: calloc"); + + lif->l2vpn = l2vpn; + strlcpy(lif->ifname, kif->ifname, sizeof(lif->ifname)); + lif->ifindex = kif->ifindex; + lif->flags = kif->flags; + lif->link_state = kif->link_state; + + return (lif); +} + +struct l2vpn_if * +l2vpn_if_find(struct l2vpn *l2vpn, unsigned int ifindex) +{ + struct l2vpn_if *lif; + + LIST_FOREACH(lif, &l2vpn->if_list, entry) + if (lif->ifindex == ifindex) + return (lif); + + return (NULL); +} + +void +l2vpn_if_del(struct l2vpn_if *lif) +{ + free(lif); +} + +struct l2vpn_pw * +l2vpn_pw_new(struct l2vpn *l2vpn, struct kif *kif) +{ + struct l2vpn_pw *pw; + + if ((pw = calloc(1, sizeof(*pw))) == NULL) + err(1, "l2vpn_pw_new: calloc"); + + pw->l2vpn = l2vpn; + strlcpy(pw->ifname, kif->ifname, sizeof(pw->ifname)); + pw->ifindex = kif->ifindex; + + return (pw); +} + +struct l2vpn_pw * +l2vpn_pw_find(struct l2vpn *l2vpn, unsigned int ifindex) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + if (pw->ifindex == ifindex) + return (pw); + + return (NULL); +} + +void +l2vpn_pw_del(struct l2vpn_pw *pw) +{ + struct fec fec; + + if (pw->pwid == 0 || pw->addr.s_addr == INADDR_ANY) + return; + + l2vpn_pw_fec(pw, &fec); + lde_kernel_remove(&fec, pw->addr); + free(pw); +} + +void +l2vpn_pw_init(struct l2vpn_pw *pw) +{ + struct fec fec; + + if (pw->pwid == 0 || pw->addr.s_addr == INADDR_ANY) + return; + + l2vpn_pw_fec(pw, &fec); + lde_kernel_insert(&fec, pw->addr, 0, (void *)pw); +} + +void +l2vpn_pw_fec(struct l2vpn_pw *pw, struct fec *fec) +{ + bzero(fec, sizeof(*fec)); + fec->type = FEC_TYPE_PWID; + fec->u.pwid.type = pw->l2vpn->pw_type; + fec->u.pwid.pwid = pw->pwid; + fec->u.pwid.nexthop.s_addr = pw->addr.s_addr; +} + +void +l2vpn_pw_reset(struct l2vpn_pw *pw) +{ + pw->remote_group = 0; + pw->remote_mtu = 0; + if (!(pw->flags & F_PW_CONTROLWORD_CONF)) + pw->flags &= ~F_PW_CONTROLWORD; + if (!(pw->flags & F_PW_STATUSTLV_CONF)) + pw->flags &= ~F_PW_STATUSTLV; +} + +int +l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) +{ + struct fec fec; + struct fec_node *fn; + + /* check for a remote label */ + if (fnh->remote_label == NO_LABEL) + return (0); + + /* MTUs must match */ + if (pw->l2vpn->mtu != pw->remote_mtu) + return (0); + + /* check pw status if applicable */ + if ((pw->flags & F_PW_STATUSTLV) && + pw->remote_status != PW_FORWARDING) + return (0); + + /* check for a working lsp to the nexthop */ + bzero(&fec, sizeof(fec)); + fec.type = FEC_TYPE_IPV4; + fec.u.ipv4.prefix.s_addr = pw->addr.s_addr; + fec.u.ipv4.prefixlen = 32; + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL) + return (0); + LIST_FOREACH(fnh, &fn->nexthops, entry) + if (fnh->remote_label == NO_LABEL) + return (0); + + return (1); +} + +int +l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map) +{ + struct fec_nh *fnh; + struct l2vpn_pw *pw; + + /* NOTE: thanks martini & friends for all this mess */ + + fnh = fec_nh_find(fn, ln->id); + if (fnh == NULL) + /* + * pseudowire not configured, return and record + * the mapping later + */ + return (0); + pw = (struct l2vpn_pw *) fnh->data; + + l2vpn_pw_reset(pw); + + /* RFC4447 - Section 6.2: control word negotiation */ + if (fec_find(&ln->sent_map, &fn->fec)) { + if ((map->flags & F_MAP_PW_CWORD) && + !(pw->flags & F_PW_CONTROLWORD_CONF)) { + /* ignore the received label mapping */ + return (1); + } else if (!(map->flags & F_MAP_PW_CWORD) && + (pw->flags & F_PW_CONTROLWORD_CONF)) { + /* TODO append a "Wrong C-bit" status code */ + lde_send_labelwithdraw(ln, fn); + + pw->flags &= ~F_PW_CONTROLWORD; + lde_send_labelmapping(ln, fn, 1); + } + } else if (map->flags & F_MAP_PW_CWORD) { + if (pw->flags & F_PW_CONTROLWORD_CONF) + pw->flags |= F_PW_CONTROLWORD; + else + /* act as if no label mapping had been received */ + return (1); + } else + pw->flags &= ~F_PW_CONTROLWORD; + + /* RFC4447 - Section 5.4.3: pseudowire status negotiation */ + if (fec_find(&ln->recv_map, &fn->fec) == NULL && + !(map->flags & F_MAP_PW_STATUS)) + pw->flags &= ~F_PW_STATUSTLV; + + return (0); +} + +void +l2vpn_send_pw_status(u_int32_t peerid, u_int32_t status, struct fec *fec) +{ + struct notify_msg nm; + + bzero(&nm, sizeof(nm)); + nm.status = S_PW_STATUS; + + nm.pw_status = status; + nm.flags |= F_NOTIF_PW_STATUS; + + lde_fec2map(fec, &nm.fec); + nm.flags |= F_NOTIF_FEC; + + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0, + &nm, sizeof(nm)); +} + +void +l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) +{ + struct fec fec; + struct fec_node *fn; + struct fec_nh *fnh; + struct l2vpn_pw *pw; + + /* TODO group wildcard */ + if (!(nm->fec.flags & F_MAP_PW_ID)) + return; + + lde_map2fec(&nm->fec, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL) + /* unknown fec */ + return; + + fnh = fec_nh_find(fn, ln->id); + if (fnh == NULL) + return; + pw = (struct l2vpn_pw *) fnh->data; + + /* remote status didn't change */ + if (pw->remote_status == nm->pw_status) + return; + + pw->remote_status = nm->pw_status; + + if (l2vpn_pw_ok(pw, fnh)) + lde_send_change_klabel(fn, fnh); + else + lde_send_delete_klabel(fn, fnh); +} + +void +l2vpn_sync_pws(struct in_addr addr) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + struct fec fec; + struct fec_node *fn; + struct fec_nh *fnh; + + LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) { + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + if (pw->addr.s_addr == addr.s_addr) { + l2vpn_pw_fec(pw, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn == NULL) + continue; + fnh = fec_nh_find(fn, pw->addr); + if (fnh == NULL) + continue; + + if (l2vpn_pw_ok(pw, fnh)) + lde_send_change_klabel(fn, fnh); + else + lde_send_delete_klabel(fn, fnh); + } + } + } +} + +void +l2vpn_pw_ctl(pid_t pid) +{ + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + static struct ctl_pw pwctl; + + LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + bzero(&pwctl, sizeof(pwctl)); + strlcpy(pwctl.ifname, pw->ifname, + sizeof(pwctl.ifname)); + pwctl.pwid = pw->pwid; + pwctl.nexthop.s_addr = pw->addr.s_addr; + pwctl.status = pw->flags & F_PW_STATUS_UP; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0, + pid, &pwctl, sizeof(pwctl)); + } +} + +void +l2vpn_binding_ctl(pid_t pid) +{ + struct fec *f; + struct fec_node *fn; + struct lde_map *me; + struct fec_nh *fnh; + struct l2vpn_pw *pw; + static struct ctl_pw pwctl; + + RB_FOREACH(f, fec_tree, &ft) { + if (f->type != FEC_TYPE_PWID) + continue; + + fn = (struct fec_node *)f; + if (fn->local_label == NO_LABEL && + LIST_EMPTY(&fn->downstream)) + continue; + + fnh = fec_nh_find(fn, f->u.pwid.nexthop); + if (fnh != NULL) + pw = (struct l2vpn_pw *) fnh->data; + else + pw = NULL; + + bzero(&pwctl, sizeof(pwctl)); + pwctl.type = f->u.pwid.type; + pwctl.pwid = f->u.pwid.pwid; + pwctl.nexthop = f->u.pwid.nexthop; + + if (pw) { + pwctl.local_label = fn->local_label; + pwctl.local_gid = 0; + pwctl.local_ifmtu = pw->l2vpn->mtu; + } else + pwctl.local_label = NO_LABEL; + + LIST_FOREACH(me, &fn->downstream, entry) + if (f->u.pwid.nexthop.s_addr == me->nexthop->id.s_addr) + break; + + if (me) { + pwctl.remote_label = me->map.label; + pwctl.remote_gid = me->map.fec.pwid.group_id; + if (me->map.flags & F_MAP_PW_IFMTU) + pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, + 0, pid, &pwctl, sizeof(pwctl)); + } else if (pw) { + pwctl.remote_label = NO_LABEL; + + lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, + 0, pid, &pwctl, sizeof(pwctl)); + } + } +} + +/* ldpe */ + +void +ldpe_l2vpn_init(struct l2vpn *l2vpn) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + ldpe_l2vpn_pw_init(pw); +} + +void +ldpe_l2vpn_exit(struct l2vpn *l2vpn) +{ + struct l2vpn_pw *pw; + + LIST_FOREACH(pw, &l2vpn->pw_list, entry) + ldpe_l2vpn_pw_exit(pw); +} + +void +ldpe_l2vpn_pw_init(struct l2vpn_pw *pw) +{ + struct tnbr *tnbr; + + if (pw->pwid == 0 || pw->addr.s_addr == INADDR_ANY) + return; + + tnbr = tnbr_find(leconf, pw->addr); + if (tnbr->discovery_fd == 0) + tnbr_init(leconf, tnbr); +} + +void +ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw) +{ + struct tnbr *tnbr; + + if (pw->pwid == 0 || pw->addr.s_addr == INADDR_ANY) + return; + + tnbr = tnbr_find(leconf, pw->addr); + if (tnbr) { + tnbr->pw_count--; + tnbr_check(tnbr); + } +} diff --git a/usr.sbin/ldpd/labelmapping.c b/usr.sbin/ldpd/labelmapping.c index df62ce14e44..2652092c594 100644 --- a/usr.sbin/ldpd/labelmapping.c +++ b/usr.sbin/ldpd/labelmapping.c @@ -1,4 +1,4 @@ -/* $OpenBSD: labelmapping.c,v 1.32 2015/07/19 20:54:16 renato Exp $ */ +/* $OpenBSD: labelmapping.c,v 1.33 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -40,13 +40,9 @@ void gen_label_tlv(struct ibuf *, u_int32_t); void gen_reqid_tlv(struct ibuf *, u_int32_t); -void gen_fec_tlv(struct ibuf *, struct in_addr, u_int8_t); -void gen_wcard_fec_tlv(struct ibuf *); int tlv_decode_label(struct nbr *, struct ldp_msg *, char *, u_int16_t, u_int32_t *); -int tlv_decode_fec_elm(struct nbr *, struct ldp_msg *, char *, u_int16_t, - u_int8_t *, u_int32_t *, u_int8_t *); static void enqueue_pdu(struct nbr *nbr, struct ibuf *buf, u_int16_t size) @@ -82,11 +78,27 @@ send_labelmessage(struct nbr *nbr, u_int16_t type, struct mapping_head *mh) /* calculate size */ tlv_size = LDP_MSG_LEN + TLV_HDR_LEN; - if (me->map.flags & F_MAP_WILDCARD) + + switch (me->map.type) { + case FEC_WILDCARD: tlv_size += FEC_ELM_WCARD_LEN; - else + break; + case FEC_PREFIX: tlv_size += FEC_ELM_PREFIX_MIN_LEN + - PREFIX_SIZE(me->map.prefixlen); + PREFIX_SIZE(me->map.fec.ipv4.prefixlen); + break; + case FEC_PWID: + tlv_size += FEC_PWID_ELM_MIN_LEN; + + if (me->map.flags & F_MAP_PW_ID) + tlv_size += sizeof(u_int32_t); + if (me->map.flags & F_MAP_PW_IFMTU) + tlv_size += FEC_SUBTLV_IFMTU_LEN; + if (me->map.flags & F_MAP_PW_STATUS) + tlv_size += PW_STATUS_TLV_LEN; + break; + } + if (me->map.label != NO_LABEL) tlv_size += LABEL_TLV_LEN; if (me->map.flags & F_MAP_REQ_ID) @@ -103,14 +115,13 @@ send_labelmessage(struct nbr *nbr, u_int16_t type, struct mapping_head *mh) /* append message and tlvs */ gen_msg_tlv(buf, type, tlv_size); - if (me->map.flags & F_MAP_WILDCARD) - gen_wcard_fec_tlv(buf); - else - gen_fec_tlv(buf, me->map.prefix, me->map.prefixlen); + gen_fec_tlv(buf, &me->map); if (me->map.label != NO_LABEL) gen_label_tlv(buf, me->map.label); if (me->map.flags & F_MAP_REQ_ID) gen_reqid_tlv(buf, me->map.requestid); + if (me->map.flags & F_MAP_PW_STATUS) + gen_pw_status_tlv(buf, me->map.pw_status); TAILQ_REMOVE(mh, me, entry); free(me); @@ -128,10 +139,9 @@ recv_labelmessage(struct nbr *nbr, char *buf, u_int16_t len, u_int16_t type) struct ldp_msg lm; struct tlv ft; u_int32_t label = NO_LABEL, reqid = 0; + u_int32_t pw_status = 0; u_int8_t flags = 0; - int feclen, lbllen, tlen; - u_int8_t addr_type; struct mapping_entry *me; struct mapping_head mh; @@ -168,23 +178,29 @@ recv_labelmessage(struct nbr *nbr, char *buf, u_int16_t len, u_int16_t type) TAILQ_INSERT_HEAD(&mh, me, entry); if ((tlen = tlv_decode_fec_elm(nbr, &lm, buf, feclen, - &addr_type, &me->map.prefix.s_addr, &me->map.prefixlen)) == -1) + &me->map)) == -1) goto err; + if (me->map.type == FEC_PWID && + type == MSG_TYPE_LABELMAPPING && + !(me->map.flags & F_MAP_PW_ID)) { + send_notification_nbr(nbr, S_MISS_MSG, lm.msgid, + lm.type); + return (-1); + } /* * The Wildcard FEC Element can be used only in the * Label Withdraw and Label Release messages. */ - if (addr_type == FEC_WILDCARD) { + if (me->map.type == FEC_WILDCARD) { switch (type) { - case MSG_TYPE_LABELWITHDRAW: - case MSG_TYPE_LABELRELEASE: - me->map.flags |= F_MAP_WILDCARD; - break; - default: + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELREQUEST: + case MSG_TYPE_LABELABORTREQ: session_shutdown(nbr, S_BAD_TLV_VAL, lm.msgid, lm.type); goto err; + default: break; } } @@ -218,7 +234,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, u_int16_t len, u_int16_t type) /* Optional Parameters */ while (len > 0) { struct tlv tlv; - u_int32_t reqbuf, labelbuf; + u_int32_t reqbuf, labelbuf, statusbuf; if (len < sizeof(tlv)) { session_shutdown(nbr, S_BAD_TLV_LEN, lm.msgid, @@ -227,10 +243,15 @@ recv_labelmessage(struct nbr *nbr, char *buf, u_int16_t len, u_int16_t type) } bcopy(buf, &tlv, sizeof(tlv)); + if (ntohs(tlv.length) > len - TLV_HDR_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, lm.msgid, + lm.type); + goto err; + } buf += TLV_HDR_LEN; len -= TLV_HDR_LEN; - switch (ntohs(tlv.type)) { + switch (ntohs(tlv.type) & ~UNKNOWN_FLAG) { case TLV_TYPE_LABELREQUEST: switch (type) { case MSG_TYPE_LABELMAPPING: @@ -287,6 +308,24 @@ recv_labelmessage(struct nbr *nbr, char *buf, u_int16_t len, u_int16_t type) break; } break; + case TLV_TYPE_PW_STATUS: + switch (type) { + case MSG_TYPE_LABELMAPPING: + if (ntohs(tlv.length) != 4) { + session_shutdown(nbr, S_BAD_TLV_LEN, + lm.msgid, lm.type); + goto err; + } + + flags |= F_MAP_PW_STATUS; + memcpy(&statusbuf, buf, sizeof(statusbuf)); + pw_status = ntohl(statusbuf); + break; + default: + /* ignore */ + break; + } + break; default: if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) { send_notification_nbr(nbr, S_UNKNOWN_TLV, @@ -307,32 +346,34 @@ recv_labelmessage(struct nbr *nbr, char *buf, u_int16_t len, u_int16_t type) me->map.label = label; if (me->map.flags & F_MAP_REQ_ID) me->map.requestid = reqid; + if (me->map.flags & F_MAP_PW_STATUS) + me->map.pw_status = pw_status; switch (type) { case MSG_TYPE_LABELMAPPING: log_debug("label mapping from nbr %s, FEC %s, " "label %u", inet_ntoa(nbr->id), - log_fec(&me->map), me->map.label); + log_map(&me->map), me->map.label); imsg_type = IMSG_LABEL_MAPPING; break; case MSG_TYPE_LABELREQUEST: log_debug("label request from nbr %s, FEC %s", - inet_ntoa(nbr->id), log_fec(&me->map)); + inet_ntoa(nbr->id), log_map(&me->map)); imsg_type = IMSG_LABEL_REQUEST; break; case MSG_TYPE_LABELWITHDRAW: log_debug("label withdraw from nbr %s, FEC %s", - inet_ntoa(nbr->id), log_fec(&me->map)); + inet_ntoa(nbr->id), log_map(&me->map)); imsg_type = IMSG_LABEL_WITHDRAW; break; case MSG_TYPE_LABELRELEASE: log_debug("label release from nbr %s, FEC %s", - inet_ntoa(nbr->id), log_fec(&me->map)); + inet_ntoa(nbr->id), log_map(&me->map)); imsg_type = IMSG_LABEL_RELEASE; break; case MSG_TYPE_LABELABORTREQ: log_debug("label abort from nbr %s, FEC %s", - inet_ntoa(nbr->id), log_fec(&me->map)); + inet_ntoa(nbr->id), log_map(&me->map)); imsg_type = IMSG_LABEL_ABORT; break; default: @@ -426,54 +467,99 @@ gen_reqid_tlv(struct ibuf *buf, u_int32_t reqid) } void -gen_fec_tlv(struct ibuf *buf, struct in_addr prefix, u_int8_t prefixlen) +gen_pw_status_tlv(struct ibuf *buf, u_int32_t status) { - struct tlv ft; - u_int8_t type; - u_int16_t family; - u_int8_t len; + struct pw_status_tlv st; - len = PREFIX_SIZE(prefixlen); - ft.type = htons(TLV_TYPE_FEC); - ft.length = htons(sizeof(type) + sizeof(family) + sizeof(prefixlen) + - len); + st.type = htons(TLV_TYPE_PW_STATUS); + st.length = htons(sizeof(status)); + st.value = htonl(status); - ibuf_add(buf, &ft, sizeof(ft)); - - type = FEC_PREFIX; - family = htons(FEC_IPV4); - - ibuf_add(buf, &type, sizeof(type)); - ibuf_add(buf, &family, sizeof(family)); - ibuf_add(buf, &prefixlen, sizeof(prefixlen)); - if (len) - ibuf_add(buf, &prefix, len); + ibuf_add(buf, &st, sizeof(st)); } void -gen_wcard_fec_tlv(struct ibuf *buf) +gen_fec_tlv(struct ibuf *buf, struct map *map) { struct tlv ft; - u_int8_t type; + u_int16_t family, len, pw_type, ifmtu; + u_int8_t pw_len = 0; + u_int32_t group_id, pwid; ft.type = htons(TLV_TYPE_FEC); - ft.length = htons(sizeof(type)); - ibuf_add(buf, &ft, sizeof(ft)); - type = FEC_WILDCARD; - ibuf_add(buf, &type, sizeof(type)); + switch (map->type) { + case FEC_WILDCARD: + ft.length = htons(sizeof(u_int8_t)); + ibuf_add(buf, &ft, sizeof(ft)); + ibuf_add(buf, &map->type, sizeof(map->type)); + break; + case FEC_PREFIX: + len = PREFIX_SIZE(map->fec.ipv4.prefixlen); + ft.length = htons(sizeof(map->type) + sizeof(family) + + sizeof(map->fec.ipv4.prefixlen) + len); + ibuf_add(buf, &ft, sizeof(ft)); + + ibuf_add(buf, &map->type, sizeof(map->type)); + family = htons(AF_IPV4); + ibuf_add(buf, &family, sizeof(family)); + ibuf_add(buf, &map->fec.ipv4.prefixlen, + sizeof(map->fec.ipv4.prefixlen)); + if (len) + ibuf_add(buf, &map->fec.ipv4.prefix, len); + break; + case FEC_PWID: + if (map->flags & F_MAP_PW_ID) + pw_len += sizeof(u_int32_t); + if (map->flags & F_MAP_PW_IFMTU) + pw_len += FEC_SUBTLV_IFMTU_LEN; + + len = FEC_PWID_ELM_MIN_LEN + pw_len; + + ft.length = htons(len); + ibuf_add(buf, &ft, sizeof(ft)); + + ibuf_add(buf, &map->type, sizeof(u_int8_t)); + pw_type = map->fec.pwid.type; + if (map->flags & F_MAP_PW_CWORD) + pw_type |= CONTROL_WORD_FLAG; + pw_type = htons(pw_type); + ibuf_add(buf, &pw_type, sizeof(u_int16_t)); + ibuf_add(buf, &pw_len, sizeof(u_int8_t)); + group_id = htonl(map->fec.pwid.group_id); + ibuf_add(buf, &group_id, sizeof(u_int32_t)); + if (map->flags & F_MAP_PW_ID) { + pwid = htonl(map->fec.pwid.pwid); + ibuf_add(buf, &pwid, sizeof(u_int32_t)); + } + if (map->flags & F_MAP_PW_IFMTU) { + struct subtlv stlv; + + stlv.type = SUBTLV_IFMTU; + stlv.length = FEC_SUBTLV_IFMTU_LEN; + ibuf_add(buf, &stlv, sizeof(u_int16_t)); + + ifmtu = htons(map->fec.pwid.ifmtu); + ibuf_add(buf, &ifmtu, sizeof(u_int16_t)); + } + break; + default: + break; + } } int tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *lm, char *buf, - u_int16_t len, u_int8_t *type, u_int32_t *prefix, u_int8_t *prefixlen) + u_int16_t len, struct map *map) { u_int16_t family, off = 0; + u_int8_t pw_len; - *type = *buf; + map->type = *buf; off += sizeof(u_int8_t); - if (*type == FEC_WILDCARD) { + switch (map->type) { + case FEC_WILDCARD: if (len == FEC_ELM_WCARD_LEN) return (off); else { @@ -481,36 +567,128 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *lm, char *buf, lm->type); return (-1); } - } + break; + case FEC_PREFIX: + if (len < FEC_ELM_PREFIX_MIN_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, + lm->type); + return (-1); + } - if (*type != FEC_PREFIX) { - send_notification_nbr(nbr, S_UNKNOWN_FEC, lm->msgid, lm->type); - return (-1); - } + /* Address Family */ + bcopy(buf + off, &family, sizeof(family)); + off += sizeof(family); - if (len < FEC_ELM_PREFIX_MIN_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, lm->type); - return (-1); - } + if (family != htons(AF_IPV4)) { + send_notification_nbr(nbr, S_UNSUP_ADDR, lm->msgid, + lm->type); + return (-1); + } - bcopy(buf + off, &family, sizeof(family)); - off += sizeof(family); + /* PreLen */ + map->fec.ipv4.prefixlen = buf[off]; + off += sizeof(u_int8_t); - if (family != htons(FEC_IPV4)) { - send_notification_nbr(nbr, S_UNSUP_ADDR, lm->msgid, lm->type); - return (-1); - } + if (len < off + PREFIX_SIZE(map->fec.ipv4.prefixlen)) { + session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, + lm->type); + return (-1); + } - *prefixlen = buf[off]; - off += sizeof(u_int8_t); + /* Prefix */ + map->fec.ipv4.prefix.s_addr = 0; + bcopy(buf + off, &map->fec.ipv4.prefix, + PREFIX_SIZE(map->fec.ipv4.prefixlen)); - if (len < off + PREFIX_SIZE(*prefixlen)) { - session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, lm->type); - return (-1); - } + return (off + PREFIX_SIZE(map->fec.ipv4.prefixlen)); + break; + case FEC_PWID: + if (len < FEC_PWID_ELM_MIN_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, + lm->type); + return (-1); + } + + /* PW type */ + bcopy(buf + off, &map->fec.pwid.type, sizeof(u_int16_t)); + map->fec.pwid.type = ntohs(map->fec.pwid.type); + if (map->fec.pwid.type & CONTROL_WORD_FLAG) { + map->flags |= F_MAP_PW_CWORD; + map->fec.pwid.type &= ~CONTROL_WORD_FLAG; + } + off += sizeof(u_int16_t); + + /* PW info Length */ + pw_len = buf[off]; + off += sizeof(u_int8_t); + + if (len != FEC_PWID_ELM_MIN_LEN + pw_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, + lm->type); + return (-1); + } - *prefix = 0; - bcopy(buf + off, prefix, PREFIX_SIZE(*prefixlen)); + /* Group ID */ + bcopy(buf + off, &map->fec.pwid.group_id, sizeof(u_int32_t)); + map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id); + off += sizeof(u_int32_t); - return (off + PREFIX_SIZE(*prefixlen)); + /* PW ID */ + if (pw_len == 0) + return (off); + + if (pw_len < sizeof(u_int32_t)) { + session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, + lm->type); + return (-1); + } + + bcopy(buf + off, &map->fec.pwid.pwid, sizeof(u_int32_t)); + map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid); + map->flags |= F_MAP_PW_ID; + off += sizeof(u_int32_t); + pw_len -= sizeof(u_int32_t); + + /* Optional Interface Parameter Sub-TLVs */ + while (pw_len > 0) { + struct subtlv stlv; + + if (pw_len < sizeof(stlv)) { + session_shutdown(nbr, S_BAD_TLV_LEN, + lm->msgid, lm->type); + return (-1); + } + + bcopy(buf + off, &stlv, sizeof(stlv)); + off += SUBTLV_HDR_LEN; + pw_len -= SUBTLV_HDR_LEN; + + switch (stlv.type) { + case SUBTLV_IFMTU: + if (stlv.length != FEC_SUBTLV_IFMTU_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, + lm->msgid, lm->type); + return (-1); + } + bcopy(buf + off, &map->fec.pwid.ifmtu, + sizeof(u_int16_t)); + map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu); + map->flags |= F_MAP_PW_IFMTU; + break; + default: + /* ignore */ + break; + } + off += stlv.length - SUBTLV_HDR_LEN; + pw_len -= stlv.length - SUBTLV_HDR_LEN; + } + + return (off); + break; + default: + send_notification_nbr(nbr, S_UNKNOWN_FEC, lm->msgid, lm->type); + break; + } + + return (-1); } diff --git a/usr.sbin/ldpd/lde.c b/usr.sbin/ldpd/lde.c index 82ff83c444d..5d3f698b856 100644 --- a/usr.sbin/ldpd/lde.c +++ b/usr.sbin/ldpd/lde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lde.c,v 1.37 2015/07/21 04:48:42 renato Exp $ */ +/* $OpenBSD: lde.c,v 1.38 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> @@ -83,6 +83,7 @@ lde(struct ldpd_conf *xconf, int pipe_parent2lde[2], int pipe_ldpe2lde[2], struct timeval now; struct passwd *pw; pid_t pid; + struct l2vpn *l2vpn; switch (pid = fork()) { case -1: @@ -150,6 +151,10 @@ lde(struct ldpd_conf *xconf, int pipe_parent2lde[2], int pipe_ldpe2lde[2], gettimeofday(&now, NULL); ldeconf->uptime = now.tv_sec; + /* initialize l2vpns */ + LIST_FOREACH(l2vpn, &ldeconf->l2vpn_list, entry) + l2vpn_init(l2vpn); + event_dispatch(); lde_shutdown(); @@ -200,6 +205,7 @@ lde_dispatch_imsg(int fd, short event, void *bula) struct lde_nbr *nbr; struct map map; struct in_addr addr; + struct notify_msg nm; ssize_t n; int shut = 0, verbose; @@ -257,13 +263,13 @@ lde_dispatch_imsg(int fd, short event, void *bula) lde_check_request(&map, nbr); break; case IMSG_LABEL_RELEASE: - if (map.flags & F_MAP_WILDCARD) + if (map.type == FEC_WILDCARD) lde_check_release_wcard(&map, nbr); else lde_check_release(&map, nbr); break; case IMSG_LABEL_WITHDRAW: - if (map.flags & F_MAP_WILDCARD) + if (map.type == FEC_WILDCARD) lde_check_withdraw_wcard(&map, nbr); else lde_check_withdraw(&map, nbr); @@ -311,6 +317,26 @@ lde_dispatch_imsg(int fd, short event, void *bula) } break; + case IMSG_NOTIFICATION: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm)) + fatalx("invalid size of OE request"); + memcpy(&nm, imsg.data, sizeof(nm)); + + nbr = lde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("lde_dispatch_imsg: cannot find " + "lde neighbor"); + return; + } + + switch (nm.status) { + case S_PW_STATUS: + l2vpn_recv_pw_status(nbr, &nm); + break; + default: + break; + } + break; case IMSG_NEIGHBOR_UP: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(addr)) fatalx("invalid size of OE request"); @@ -330,6 +356,18 @@ lde_dispatch_imsg(int fd, short event, void *bula) lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0); break; + case IMSG_CTL_SHOW_L2VPN_PW: + l2vpn_pw_ctl(imsg.hdr.pid); + + lde_imsg_compose_ldpe(IMSG_CTL_END, 0, + imsg.hdr.pid, NULL, 0); + break; + case IMSG_CTL_SHOW_L2VPN_BINDING: + l2vpn_binding_ctl(imsg.hdr.pid); + + lde_imsg_compose_ldpe(IMSG_CTL_END, 0, + imsg.hdr.pid, NULL, 0); + break; case IMSG_CTL_LOG_VERBOSE: /* already checked by ldpe */ memcpy(&verbose, imsg.data, sizeof(verbose)); @@ -358,12 +396,16 @@ lde_dispatch_parent(int fd, short event, void *bula) struct iface *niface; struct tnbr *ntnbr; struct nbr_params *nnbrp; + static struct l2vpn *nl2vpn; + struct l2vpn_if *nlif; + struct l2vpn_pw *npw; struct imsg imsg; struct kroute kr; struct imsgev *iev = bula; struct imsgbuf *ibuf = &iev->ibuf; ssize_t n; int shut = 0; + struct fec fec; if (event & EV_READ) { if ((n = imsg_read(ibuf)) == -1) @@ -393,7 +435,11 @@ lde_dispatch_parent(int fd, short event, void *bula) } memcpy(&kr, imsg.data, sizeof(kr)); - lde_kernel_insert(&kr); + fec.type = FEC_TYPE_IPV4; + fec.u.ipv4.prefix.s_addr = kr.prefix.s_addr; + fec.u.ipv4.prefixlen = kr.prefixlen; + lde_kernel_insert(&fec, kr.nexthop, + kr.flags & F_CONNECTED, NULL); break; case IMSG_NETWORK_DEL: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) { @@ -403,7 +449,10 @@ lde_dispatch_parent(int fd, short event, void *bula) } memcpy(&kr, imsg.data, sizeof(kr)); - lde_kernel_remove(&kr); + fec.type = FEC_TYPE_IPV4; + fec.u.ipv4.prefix.s_addr = kr.prefix.s_addr; + fec.u.ipv4.prefixlen = kr.prefixlen; + lde_kernel_remove(&fec, kr.nexthop); break; case IMSG_RECONF_CONF: if ((nconf = malloc(sizeof(struct ldpd_conf))) == @@ -415,6 +464,7 @@ lde_dispatch_parent(int fd, short event, void *bula) LIST_INIT(&nconf->addr_list); LIST_INIT(&nconf->tnbr_list); LIST_INIT(&nconf->nbrp_list); + LIST_INIT(&nconf->l2vpn_list); break; case IMSG_RECONF_IFACE: if ((niface = malloc(sizeof(struct iface))) == NULL) @@ -440,6 +490,32 @@ lde_dispatch_parent(int fd, short event, void *bula) LIST_INSERT_HEAD(&nconf->nbrp_list, nnbrp, entry); break; + case IMSG_RECONF_L2VPN: + if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL) + fatal(NULL); + memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn)); + + LIST_INIT(&nl2vpn->if_list); + LIST_INIT(&nl2vpn->pw_list); + + LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry); + break; + case IMSG_RECONF_L2VPN_IF: + if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL) + fatal(NULL); + memcpy(nlif, imsg.data, sizeof(struct l2vpn_if)); + + nlif->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->if_list, nlif, entry); + break; + case IMSG_RECONF_L2VPN_PW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + npw->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry); + break; case IMSG_RECONF_END: merge_config(ldeconf, nconf); nconf = NULL; @@ -474,40 +550,140 @@ void lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) { struct kroute kr; - - bzero(&kr, sizeof(kr)); - kr.prefix.s_addr = fn->fec.prefix.s_addr; - kr.prefixlen = fn->fec.prefixlen; - kr.local_label = fn->local_label; - - kr.nexthop.s_addr = fnh->nexthop.s_addr; - kr.remote_label = fnh->remote_label; - - lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr)); + struct kpw kpw; + struct l2vpn_pw *pw; + + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = fn->fec.u.ipv4.prefix.s_addr; + kr.prefixlen = fn->fec.u.ipv4.prefixlen; + kr.local_label = fn->local_label; + kr.nexthop.s_addr = fnh->nexthop.s_addr; + kr.remote_label = fnh->remote_label; + + lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, + sizeof(kr)); + + if (fnh->remote_label != NO_LABEL && + fn->fec.u.ipv4.prefixlen == 32) + l2vpn_sync_pws(fn->fec.u.ipv4.prefix); + break; + case FEC_TYPE_PWID: + if (fn->local_label == NO_LABEL || + fnh->remote_label == NO_LABEL) + return; + + pw = (struct l2vpn_pw *) fnh->data; + if (pw->flags & F_PW_STATUS_UP) + return; + pw->flags |= F_PW_STATUS_UP; + + bzero(&kpw, sizeof(kpw)); + kpw.ifindex = pw->ifindex; + kpw.pw_type = fn->fec.u.pwid.type; + kpw.nexthop.s_addr = fnh->nexthop.s_addr; + kpw.local_label = fn->local_label; + kpw.remote_label = fnh->remote_label; + kpw.flags = pw->flags; + + lde_imsg_compose_parent(IMSG_KPWLABEL_CHANGE, 0, &kpw, + sizeof(kpw)); + break; + } } void lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) { struct kroute kr; + struct kpw kpw; + struct l2vpn_pw *pw; + + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = fn->fec.u.ipv4.prefix.s_addr; + kr.prefixlen = fn->fec.u.ipv4.prefixlen; + kr.local_label = fn->local_label; + kr.nexthop.s_addr = fnh->nexthop.s_addr; + kr.remote_label = fnh->remote_label; + + lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, + sizeof(kr)); + + if (fn->fec.u.ipv4.prefixlen == 32) + l2vpn_sync_pws(fn->fec.u.ipv4.prefix); + break; + case FEC_TYPE_PWID: + pw = (struct l2vpn_pw *) fnh->data; + if (!(pw->flags & F_PW_STATUS_UP)) + return; + pw->flags &= ~F_PW_STATUS_UP; + + bzero(&kpw, sizeof(kpw)); + kpw.ifindex = pw->ifindex; + kpw.pw_type = fn->fec.u.pwid.type; + kpw.nexthop.s_addr = fnh->nexthop.s_addr; + kpw.local_label = fn->local_label; + kpw.remote_label = fnh->remote_label; + kpw.flags = pw->flags; + + lde_imsg_compose_parent(IMSG_KPWLABEL_DELETE, 0, &kpw, + sizeof(kpw)); + break; + } +} + +void +lde_fec2map(struct fec *fec, struct map *map) +{ + bzero(map, sizeof(*map)); - bzero(&kr, sizeof(kr)); - kr.prefix.s_addr = fn->fec.prefix.s_addr; - kr.prefixlen = fn->fec.prefixlen; - kr.local_label = fn->local_label; + switch (fec->type) { + case FEC_TYPE_IPV4: + map->type = FEC_PREFIX; + map->fec.ipv4.prefix = fec->u.ipv4.prefix; + map->fec.ipv4.prefixlen = fec->u.ipv4.prefixlen; + break; + case FEC_TYPE_PWID: + map->type = FEC_PWID; + map->fec.pwid.type = fec->u.pwid.type; + map->fec.pwid.group_id = 0; + map->flags |= F_MAP_PW_ID; + map->fec.pwid.pwid = fec->u.pwid.pwid; + break; + } +} - kr.nexthop.s_addr = fnh->nexthop.s_addr; - kr.remote_label = fnh->remote_label; +void +lde_map2fec(struct map *map, struct in_addr nbrid, struct fec *fec) +{ + bzero(fec, sizeof(*fec)); - lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr)); + switch (map->type) { + case FEC_PREFIX: + fec->type = FEC_TYPE_IPV4; + fec->u.ipv4.prefix.s_addr = map->fec.ipv4.prefix.s_addr; + fec->u.ipv4.prefixlen = map->fec.ipv4.prefixlen; + break; + case FEC_PWID: + fec->type = FEC_TYPE_PWID; + fec->u.pwid.type = map->fec.pwid.type; + fec->u.pwid.pwid = map->fec.pwid.pwid; + fec->u.pwid.nexthop.s_addr = nbrid.s_addr; + break; + } } void -lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn) +lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) { struct lde_req *lre; struct lde_map *me; struct map map; + struct fec_nh *fnh; + struct l2vpn_pw *pw; /* * This function skips SL.1 - 3 and SL.9 - 14 because the label @@ -516,9 +692,26 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn) */ bzero(&map, sizeof(map)); + lde_fec2map(&fn->fec, &map); + + if (fn->fec.type == FEC_TYPE_PWID) { + fnh = fec_nh_find(fn, ln->id); + if (fnh == NULL) + /* not the other end of the pseudowire */ + return; + + pw = (struct l2vpn_pw *) fnh->data; + map.flags |= F_MAP_PW_IFMTU; + map.fec.pwid.ifmtu = pw->l2vpn->mtu; + if (pw->flags & F_PW_CONTROLWORD) + map.flags |= F_MAP_PW_CWORD; + if (pw->flags & F_PW_STATUSTLV) { + map.flags |= F_MAP_PW_STATUS; + /* VPLS are always up */ + map.pw_status = PW_FORWARDING; + } + } map.label = fn->local_label; - map.prefix = fn->fec.prefix; - map.prefixlen = fn->fec.prefixlen; /* SL.6: is there a pending request for this mapping? */ lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec); @@ -534,6 +727,9 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn) /* SL.4: send label mapping */ lde_imsg_compose_ldpe(IMSG_MAPPING_ADD, ln->peerid, 0, &map, sizeof(map)); + if (single) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, + NULL, 0); /* SL.5: record sent label mapping */ me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec); @@ -548,15 +744,29 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn) struct lde_wdraw *lw; struct map map; struct fec *f; + struct fec_nh *fnh = NULL; + struct l2vpn_pw *pw; + + if (fn->fec.type == FEC_TYPE_PWID) { + fnh = fec_nh_find(fn, ln->id); + if (fnh == NULL) + /* not the other end of the pseudowire */ + return; + } bzero(&map, sizeof(map)); if (fn) { + lde_fec2map(&fn->fec, &map); map.label = fn->local_label; - map.prefix = fn->fec.prefix; - map.prefixlen = fn->fec.prefixlen; + + if (fn->fec.type == FEC_TYPE_PWID) { + pw = (struct l2vpn_pw *) fnh->data; + if (pw->flags & F_PW_CONTROLWORD) + map.flags |= F_MAP_PW_CWORD; + } } else { + map.type = FEC_WILDCARD; map.label = NO_LABEL; - map.flags = F_MAP_WILDCARD; } /* SWd.1: send label withdraw. */ @@ -586,14 +796,28 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn) void lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, u_int32_t label) { - struct map map; + struct map map; + struct fec_nh *fnh = NULL; + struct l2vpn_pw *pw; + + if (fn->fec.type == FEC_TYPE_PWID) { + fnh = fec_nh_find(fn, ln->id); + if (fnh == NULL) + /* not the other end of the pseudowire */ + return; + } bzero(&map, sizeof(map)); if (fn) { - map.prefix = fn->fec.prefix; - map.prefixlen = fn->fec.prefixlen; + lde_fec2map(&fn->fec, &map); + + if (fn->fec.type == FEC_TYPE_PWID) { + pw = (struct l2vpn_pw *) fnh->data; + if (pw->flags & F_PW_CONTROLWORD) + map.flags |= F_MAP_PW_CWORD; + } } else - map.flags = F_MAP_WILDCARD; + map.type = FEC_WILDCARD; map.label = label; lde_imsg_compose_ldpe(IMSG_RELEASE_ADD, ln->peerid, 0, @@ -725,14 +949,14 @@ lde_map_add(struct lde_nbr *ln, struct fec_node *fn, int sent) if (sent) { LIST_INSERT_HEAD(&fn->upstream, me, entry); if (fec_insert(&ln->sent_map, &me->fec)) - log_warnx("failed to add %s/%u to sent map", - inet_ntoa(me->fec.prefix), me->fec.prefixlen); + log_warnx("failed to add %s to sent map", + log_fec(&me->fec)); /* XXX on failure more cleanup is needed */ } else { LIST_INSERT_HEAD(&fn->downstream, me, entry); if (fec_insert(&ln->recv_map, &me->fec)) - log_warnx("failed to add %s/%u to recv map", - inet_ntoa(me->fec.prefix), me->fec.prefixlen); + log_warnx("failed to add %s to recv map", + log_fec(&me->fec)); } return (me); @@ -772,8 +996,7 @@ lde_req_add(struct lde_nbr *ln, struct fec *fec, int sent) if (fec_insert(t, &lre->fec)) { log_warnx("failed to add %s/%u to %s req", - inet_ntoa(lre->fec.prefix), lre->fec.prefixlen, - sent ? "sent" : "recv"); + log_fec(&lre->fec), sent ? "sent" : "recv"); free(lre); return (NULL); } @@ -805,8 +1028,8 @@ lde_wdraw_add(struct lde_nbr *ln, struct fec_node *fn) lw->fec = fn->fec; if (fec_insert(&ln->sent_wdraw, &lw->fec)) - log_warnx("failed to add %s/%u to sent wdraw", - inet_ntoa(lw->fec.prefix), lw->fec.prefixlen); + log_warnx("failed to add %s to sent wdraw", + log_fec(&lw->fec)); return (lw); } diff --git a/usr.sbin/ldpd/lde.h b/usr.sbin/ldpd/lde.h index d1165c09c39..72edfa2418f 100644 --- a/usr.sbin/ldpd/lde.h +++ b/usr.sbin/ldpd/lde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: lde.h,v 1.28 2015/07/21 04:48:42 renato Exp $ */ +/* $OpenBSD: lde.h,v 1.29 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> @@ -28,10 +28,25 @@ RB_HEAD(fec_tree, fec); +enum fec_type { + FEC_TYPE_IPV4, + FEC_TYPE_PWID +}; + struct fec { RB_ENTRY(fec) entry; - struct in_addr prefix; - u_int8_t prefixlen; + enum fec_type type; + union { + struct { + struct in_addr prefix; + u_int8_t prefixlen; + } ipv4; + struct { + u_int16_t type; + u_int32_t pwid; + struct in_addr nexthop; + } pwid; + } u; }; RB_PROTOTYPE(fec_tree, fec, entry, fec_compare) extern struct fec_tree ft; @@ -82,6 +97,7 @@ struct fec_nh { struct in_addr nexthop; u_int32_t remote_label; + void *data; /* fec specific data */ }; struct fec_node { @@ -92,7 +108,6 @@ struct fec_node { LIST_HEAD(, lde_map) upstream; /* sent mappings */ u_int32_t local_label; - u_int8_t flags; }; /* lde.c */ @@ -100,10 +115,12 @@ pid_t lde(struct ldpd_conf *, int [2], int [2], int [2]); int lde_imsg_compose_parent(int, pid_t, void *, u_int16_t); int lde_imsg_compose_ldpe(int, u_int32_t, pid_t, void *, u_int16_t); u_int32_t lde_assign_label(void); +void lde_fec2map(struct fec *, struct map *); +void lde_map2fec(struct map *, struct in_addr, struct fec *); void lde_send_change_klabel(struct fec_node *, struct fec_nh *); void lde_send_delete_klabel(struct fec_node *, struct fec_nh *); -void lde_send_labelmapping(struct lde_nbr *, struct fec_node *); +void lde_send_labelmapping(struct lde_nbr *, struct fec_node *, int); void lde_send_labelwithdraw(struct lde_nbr *, struct fec_node *); void lde_send_labelrelease(struct lde_nbr *, struct fec_node *, u_int32_t); void lde_send_notification(u_int32_t, u_int32_t, u_int32_t, u_int32_t); @@ -124,7 +141,6 @@ int lde_address_del(struct lde_nbr *, struct in_addr *); void fec_init(struct fec_tree *); int fec_insert(struct fec_tree *, struct fec *); int fec_remove(struct fec_tree *, struct fec *); -struct fec *fec_find_prefix(struct fec_tree *, in_addr_t, u_int8_t); struct fec *fec_find(struct fec_tree *, struct fec *); void fec_clear(struct fec_tree *, void (*)(void *)); @@ -132,8 +148,9 @@ void rt_dump(pid_t); void fec_snap(struct lde_nbr *); void fec_tree_clear(void); -void lde_kernel_insert(struct kroute *); -void lde_kernel_remove(struct kroute *); +struct fec_nh *fec_nh_find(struct fec_node *, struct in_addr); +void lde_kernel_insert(struct fec *, struct in_addr, int, void *); +void lde_kernel_remove(struct fec *, struct in_addr); void lde_check_mapping(struct map *, struct lde_nbr *); void lde_check_request(struct map *, struct lde_nbr *); void lde_check_release(struct map *, struct lde_nbr *); @@ -142,4 +159,27 @@ void lde_check_withdraw(struct map *, struct lde_nbr *); void lde_check_withdraw_wcard(struct map *, struct lde_nbr *); void lde_label_list_free(struct lde_nbr *); +/* l2vpn.c */ +struct l2vpn *l2vpn_new(const char *); +struct l2vpn *l2vpn_find(struct ldpd_conf *, char *); +void l2vpn_del(struct l2vpn *); +void l2vpn_init(struct l2vpn *); +struct l2vpn_if *l2vpn_if_new(struct l2vpn *, struct kif *); +struct l2vpn_if *l2vpn_if_find(struct l2vpn *, unsigned int); +void l2vpn_if_del(struct l2vpn_if *l); +struct l2vpn_pw *l2vpn_pw_new(struct l2vpn *, struct kif *); +struct l2vpn_pw *l2vpn_pw_find(struct l2vpn *, unsigned int); +void l2vpn_pw_del(struct l2vpn_pw *); +void l2vpn_pw_init(struct l2vpn_pw *); +void l2vpn_pw_fec(struct l2vpn_pw *, struct fec *); +void l2vpn_pw_reset(struct l2vpn_pw *); +int l2vpn_pw_ok(struct l2vpn_pw *, struct fec_nh *); +int l2vpn_pw_negotiate(struct lde_nbr *, struct fec_node *, + struct map *); +void l2vpn_send_pw_status(u_int32_t, u_int32_t, struct fec *); +void l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *); +void l2vpn_sync_pws(struct in_addr); +void l2vpn_pw_ctl(pid_t); +void l2vpn_binding_ctl(pid_t); + #endif /* _LDE_H_ */ diff --git a/usr.sbin/ldpd/lde_lib.c b/usr.sbin/ldpd/lde_lib.c index b21c1d92477..d95da676cf7 100644 --- a/usr.sbin/ldpd/lde_lib.c +++ b/usr.sbin/ldpd/lde_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lde_lib.c,v 1.39 2015/07/21 04:48:42 renato Exp $ */ +/* $OpenBSD: lde_lib.c,v 1.40 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -41,8 +41,7 @@ static int fec_compare(struct fec *, struct fec *); void fec_free(void *); -struct fec_node *fec_add(struct in_addr, u_int8_t); -struct fec_nh *fec_nh_find(struct fec_node *, struct in_addr); +struct fec_node *fec_add(struct fec *fec); struct fec_nh *fec_nh_add(struct fec_node *, struct in_addr); void fec_nh_del(struct fec_nh *); int lde_nbr_is_nexthop(struct fec_node *, struct lde_nbr *); @@ -66,27 +65,45 @@ fec_init(struct fec_tree *fh) static int fec_compare(struct fec *a, struct fec *b) { - if (ntohl(a->prefix.s_addr) < ntohl(b->prefix.s_addr)) + if (a->type < b->type) return (-1); - if (ntohl(a->prefix.s_addr) > ntohl(b->prefix.s_addr)) + if (a->type > b->type) return (1); - if (a->prefixlen < b->prefixlen) - return (-1); - if (a->prefixlen > b->prefixlen) - return (1); - - return (0); -} -struct fec * -fec_find_prefix(struct fec_tree *fh, in_addr_t prefix, u_int8_t prefixlen) -{ - struct fec s; - - s.prefix.s_addr = prefix; - s.prefixlen = prefixlen; + switch (a->type) { + case FEC_TYPE_IPV4: + if (ntohl(a->u.ipv4.prefix.s_addr) < + ntohl(b->u.ipv4.prefix.s_addr)) + return (-1); + if (ntohl(a->u.ipv4.prefix.s_addr) > + ntohl(b->u.ipv4.prefix.s_addr)) + return (1); + if (a->u.ipv4.prefixlen < b->u.ipv4.prefixlen) + return (-1); + if (a->u.ipv4.prefixlen > b->u.ipv4.prefixlen) + return (1); + return (0); + break; + case FEC_TYPE_PWID: + if (a->u.pwid.type < b->u.pwid.type) + return (-1); + if (a->u.pwid.type > b->u.pwid.type) + return (1); + if (a->u.pwid.pwid < b->u.pwid.pwid) + return (-1); + if (a->u.pwid.pwid > b->u.pwid.pwid) + return (1); + if (ntohl(a->u.pwid.nexthop.s_addr) < + ntohl(b->u.pwid.nexthop.s_addr)) + return (-1); + if (ntohl(a->u.pwid.nexthop.s_addr) > + ntohl(b->u.pwid.nexthop.s_addr)) + return (1); + return (0); + break; + } - return (fec_find(fh, &s)); + return (-1); } struct fec * @@ -107,8 +124,7 @@ int fec_remove(struct fec_tree *fh, struct fec *f) { if (RB_REMOVE(fec_tree, fh, f) == NULL) { - log_warnx("fec_remove failed for %s/%u", - inet_ntoa(f->prefix), f->prefixlen); + log_warnx("fec_remove failed for %s", log_fec(f)); return (-1); } return (0); @@ -148,13 +164,15 @@ rt_dump(pid_t pid) RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + if (fn->local_label == NO_LABEL && LIST_EMPTY(&fn->downstream)) continue; - rtctl.prefix = fn->fec.prefix; - rtctl.prefixlen = fn->fec.prefixlen; - rtctl.flags = fn->flags; + rtctl.prefix = fn->fec.u.ipv4.prefix; + rtctl.prefixlen = fn->fec.u.ipv4.prefixlen; rtctl.local_label = fn->local_label; LIST_FOREACH(me, &fn->downstream, entry) { @@ -188,7 +206,7 @@ fec_snap(struct lde_nbr *ln) if (fn->local_label == NO_LABEL) continue; - lde_send_labelmapping(ln, fn); + lde_send_labelmapping(ln, fn, 0); count++; } if (count > 0) @@ -205,11 +223,11 @@ fec_free(void *arg) while ((fnh = LIST_FIRST(&fn->nexthops))) fec_nh_del(fnh); if (!LIST_EMPTY(&fn->downstream)) - log_warnx("fec_free: fec %s/%u downstream list not empty", - inet_ntoa(fn->fec.prefix), fn->fec.prefixlen); + log_warnx("fec_free: fec %s downstream list not empty", + log_fec(&fn->fec)); if (!LIST_EMPTY(&fn->upstream)) - log_warnx("fec_free: fec %s/%u upstream list not empty", - inet_ntoa(fn->fec.prefix), fn->fec.prefixlen); + log_warnx("fec_free: fec %s upstream list not empty", + log_fec(&fn->fec)); free(fn); } @@ -221,7 +239,7 @@ fec_tree_clear(void) } struct fec_node * -fec_add(struct in_addr prefix, u_int8_t prefixlen) +fec_add(struct fec *fec) { struct fec_node *fn; @@ -229,16 +247,15 @@ fec_add(struct in_addr prefix, u_int8_t prefixlen) if (fn == NULL) fatal("fec_add"); - fn->fec.prefix.s_addr = prefix.s_addr; - fn->fec.prefixlen = prefixlen; + memcpy(&fn->fec, fec, sizeof(fn->fec)); fn->local_label = NO_LABEL; LIST_INIT(&fn->upstream); LIST_INIT(&fn->downstream); LIST_INIT(&fn->nexthops); if (fec_insert(&ft, &fn->fec)) - log_warnx("failed to add %s/%u to ft tree", - inet_ntoa(fn->fec.prefix), fn->fec.prefixlen); + log_warnx("failed to add %s to ft tree", + log_fec(&fn->fec)); return (fn); } @@ -278,35 +295,33 @@ fec_nh_del(struct fec_nh *fnh) } void -lde_kernel_insert(struct kroute *kr) +lde_kernel_insert(struct fec *fec, struct in_addr nexthop, int connected, + void *data) { struct fec_node *fn; struct fec_nh *fnh; struct lde_map *me; struct lde_nbr *ln; - char buf[16]; - log_debug("kernel add route %s/%u nexthop %s", - inet_ntoa(kr->prefix), kr->prefixlen, - inet_ntop(AF_INET, &kr->nexthop, buf, sizeof(buf))); + log_debug("lde add fec %s nexthop %s", + log_fec(fec), inet_ntoa(nexthop)); - fn = (struct fec_node *)fec_find_prefix(&ft, kr->prefix.s_addr, - kr->prefixlen); + fn = (struct fec_node *)fec_find(&ft, fec); if (fn == NULL) - fn = fec_add(kr->prefix, kr->prefixlen); + fn = fec_add(fec); - if (fec_nh_find(fn, kr->nexthop) != NULL) + if (fec_nh_find(fn, nexthop) != NULL) return; if (LIST_EMPTY(&fn->nexthops)) { if (fn->local_label == NO_LABEL) { - if (kr->flags & F_CONNECTED) + if (connected) fn->local_label = MPLS_LABEL_IMPLNULL; else fn->local_label = lde_assign_label(); } else { /* Handle local label changes */ - if ((kr->flags & F_CONNECTED) && + if (connected && fn->local_label != MPLS_LABEL_IMPLNULL) { /* explicit withdraw of the previous label */ RB_FOREACH(ln, nbr_tree, &lde_nbrs) @@ -314,7 +329,7 @@ lde_kernel_insert(struct kroute *kr) fn->local_label = MPLS_LABEL_IMPLNULL; } - if (!(kr->flags & F_CONNECTED) && + if (!connected && fn->local_label == MPLS_LABEL_IMPLNULL) { /* explicit withdraw of the previous label */ RB_FOREACH(ln, nbr_tree, &lde_nbrs) @@ -324,16 +339,13 @@ lde_kernel_insert(struct kroute *kr) } /* FEC.1: perform lsr label distribution procedure */ - RB_FOREACH(ln, nbr_tree, &lde_nbrs) { - lde_send_labelmapping(ln, fn); - lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, - ln->peerid, 0, NULL, 0); - } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 1); } - fnh = fec_nh_add(fn, kr->nexthop); + fnh = fec_nh_add(fn, nexthop); + fnh->data = data; lde_send_change_klabel(fn, fnh); - ln = lde_find_address(fnh->nexthop); if (ln) { /* FEC.2 */ @@ -345,28 +357,26 @@ lde_kernel_insert(struct kroute *kr) } void -lde_kernel_remove(struct kroute *kr) +lde_kernel_remove(struct fec *fec, struct in_addr nexthop) { struct fec_node *fn; struct fec_nh *fnh; struct lde_nbr *ln; - char buf[16]; - log_debug("kernel remove route %s/%u nexthop %s", - inet_ntoa(kr->prefix), kr->prefixlen, - inet_ntop(AF_INET, &kr->nexthop, buf, sizeof(buf))); + log_debug("lde remove fec %s nexthop %s", + log_fec(fec), inet_ntoa(nexthop)); - fn = (struct fec_node *)fec_find_prefix(&ft, kr->prefix.s_addr, - kr->prefixlen); + fn = (struct fec_node *)fec_find(&ft, fec); if (fn == NULL) /* route lost */ return; - fnh = fec_nh_find(fn, kr->nexthop); + fnh = fec_nh_find(fn, nexthop); if (fnh == NULL) /* route lost */ return; + lde_send_delete_klabel(fn, fnh); fec_nh_del(fnh); if (LIST_EMPTY(&fn->nexthops)) RB_FOREACH(ln, nbr_tree, &lde_nbrs) @@ -376,17 +386,19 @@ lde_kernel_remove(struct kroute *kr) void lde_check_mapping(struct map *map, struct lde_nbr *ln) { + struct fec fec; struct fec_node *fn; struct fec_nh *fnh; struct lde_req *lre; struct lde_nbr_address *addr; struct lde_map *me; + struct l2vpn_pw *pw; int msgsource = 0; - fn = (struct fec_node *)fec_find_prefix(&ft, map->prefix.s_addr, - map->prefixlen); + lde_map2fec(map, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); if (fn == NULL) - fn = fec_add(map->prefix, map->prefixlen); + fn = fec_add(&fec); /* LMp.1: first check if we have a pending request running */ lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec); @@ -394,6 +406,10 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) /* LMp.2: delete record of outstanding label request */ lde_req_del(ln, lre, 1); + /* RFC 4447 control word and status tlv negotiation */ + if (map->type == FEC_PWID && l2vpn_pw_negotiate(ln, fn, map)) + return; + /* * LMp.3 - LMp.8: Loop detection LMp.3 - unecessary for frame-mode * mpls networks @@ -422,27 +438,38 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) * support multipath */ LIST_FOREACH(fnh, &fn->nexthops, entry) { - if (lde_address_find(ln, &fnh->nexthop)) { - msgsource = 1; + if (lde_address_find(ln, &fnh->nexthop) == NULL) + continue; - /* LMp.15: install FEC in FIB */ - fnh->remote_label = map->label; + msgsource = 1; + + /* LMp.15: install FEC in FIB */ + fnh->remote_label = map->label; + switch (map->type) { + case FEC_PREFIX: lde_send_change_klabel(fn, fnh); + break; + case FEC_PWID: + pw = (struct l2vpn_pw *) fnh->data; + pw->remote_group = map->fec.pwid.group_id; + if (map->flags & F_MAP_PW_IFMTU) + pw->remote_mtu = map->fec.pwid.ifmtu; + if (map->flags & F_MAP_PW_STATUS) + pw->remote_status = map->pw_status; + if (l2vpn_pw_ok(pw, fnh)) + lde_send_change_klabel(fn, fnh); + break; } } - if (msgsource == 0) { - /* LMp.13: perform lsr label release procedure */ - if (me == NULL) - me = lde_map_add(ln, fn, 0); - memcpy(&me->map, map, sizeof(*map)); - return; - } - - /* LMp.16: Record the mapping from this peer */ + /* LMp.13 & LMp.16: Record the mapping from this peer */ if (me == NULL) me = lde_map_add(ln, fn, 0); memcpy(&me->map, map, sizeof(*map)); + if (msgsource == 0) + /* LMp.13: just return since we use liberal lbl retention */ + return; + /* * LMp.17 - LMp.27 are unnecessary since we don't need to implement * loop detection. LMp.28 - LMp.30 are unnecessary because we are @@ -453,6 +480,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) void lde_check_request(struct map *map, struct lde_nbr *ln) { + struct fec fec; struct lde_req *lre; struct fec_node *fn; struct fec_nh *fnh; @@ -460,8 +488,8 @@ lde_check_request(struct map *map, struct lde_nbr *ln) /* TODO LRq.1: loop detection */ /* LRq.2: is there a next hop for fec? */ - fn = (struct fec_node *)fec_find_prefix(&ft, map->prefix.s_addr, - map->prefixlen); + lde_map2fec(map, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); if (fn == NULL || LIST_EMPTY(&fn->nexthops)) { lde_send_notification(ln->peerid, S_NO_ROUTE, map->messageid, MSG_TYPE_LABELREQUEST); @@ -489,8 +517,7 @@ lde_check_request(struct map *map, struct lde_nbr *ln) lre->msgid = map->messageid; /* LRq.9: perform LSR label distribution */ - lde_send_labelmapping(ln, fn); - lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0); + lde_send_labelmapping(ln, fn, 1); /* * LRq.10: do nothing (Request Never) since we use liberal @@ -502,12 +529,17 @@ lde_check_request(struct map *map, struct lde_nbr *ln) void lde_check_release(struct map *map, struct lde_nbr *ln) { + struct fec fec; struct fec_node *fn; struct lde_wdraw *lw; struct lde_map *me; - fn = (struct fec_node *)fec_find_prefix(&ft, map->prefix.s_addr, - map->prefixlen); + /* TODO group wildcard */ + if (!(map->flags & F_MAP_PW_ID)) + return; + + lde_map2fec(map, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); /* LRl.1: does FEC match a known FEC? */ if (fn == NULL) return; @@ -567,14 +599,19 @@ lde_check_release_wcard(struct map *map, struct lde_nbr *ln) void lde_check_withdraw(struct map *map, struct lde_nbr *ln) { + struct fec fec; struct fec_node *fn; struct fec_nh *fnh; struct lde_map *me; - fn = (struct fec_node *)fec_find_prefix(&ft, map->prefix.s_addr, - map->prefixlen); + /* TODO group wildcard */ + if (!(map->flags & F_MAP_PW_ID)) + return; + + lde_map2fec(map, ln->id, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); if (fn == NULL) - fn = fec_add(map->prefix, map->prefixlen); + fn = fec_add(&fec); /* LWd.1: remove label from forwarding/switching use */ LIST_FOREACH(fnh, &fn->nexthops, entry) { diff --git a/usr.sbin/ldpd/ldp.h b/usr.sbin/ldpd/ldp.h index e1a4480d8d4..a0830380124 100644 --- a/usr.sbin/ldpd/ldp.h +++ b/usr.sbin/ldpd/ldp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ldp.h,v 1.18 2015/07/19 20:54:17 renato Exp $ */ +/* $OpenBSD: ldp.h,v 1.19 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -49,6 +49,13 @@ #define INIT_DELAY_TMR 15 #define MAX_DELAY_TMR 120 +#define MIN_PWID_ID 1 +#define MAX_PWID_ID 0xffffffff + +#define DEFAULT_L2VPN_MTU 1500 +#define MIN_L2VPN_MTU 512 +#define MAX_L2VPN_MTU 0xffff + /* LDP message types */ #define MSG_TYPE_NOTIFICATION 0x0001 #define MSG_TYPE_HELLO 0x0100 @@ -82,6 +89,10 @@ #define TLV_TYPE_ATMSESSIONPAR 0x0501 #define TLV_TYPE_FRSESSION 0x0502 #define TLV_TYPE_LABELREQUEST 0x0600 +/* RFC 4447 */ +#define TLV_TYPE_PW_STATUS 0x096A +#define TLV_TYPE_PW_IF_PARAM 0x096B +#define TLV_TYPE_PW_GROUP_ID 0x096C /* LDP header */ struct ldp_hdr { @@ -158,6 +169,15 @@ struct hello_prms_opt4_tlv { #define S_UNSUP_ADDR 0x00000017 #define S_KEEPALIVE_BAD 0x80000018 #define S_INTERN_ERR 0x80000019 +/* RFC 4447 */ +#define S_ILLEGAL_CBIT 0x00000024 +#define S_WRONG_CBIT 0x00000025 +#define S_INCPT_BITRATE 0x00000026 +#define S_CEP_MISCONF 0x00000027 +#define S_PW_STATUS 0x00000028 +#define S_UNASSIGN_TAI 0x00000029 +#define S_MISCONF_ERR 0x0000002A +#define S_WITHDRAW_MTHD 0x0000002B struct sess_prms_tlv { u_int16_t type; @@ -185,6 +205,9 @@ struct status_tlv { #define STATUS_TLV_LEN 10 #define STATUS_FATAL 0x80000000 +#define AF_IPV4 0x1 +#define AF_IPV6 0x2 + struct address_list_tlv { u_int16_t type; u_int16_t length; @@ -192,14 +215,30 @@ struct address_list_tlv { /* address entries */ } __packed; -#define ADDR_IPV4 0x1 -#define ADDR_IPV6 0x2 - #define FEC_ELM_WCARD_LEN 1 #define FEC_ELM_PREFIX_MIN_LEN 4 +#define FEC_PWID_ELM_MIN_LEN 8 #define FEC_WILDCARD 0x01 #define FEC_PREFIX 0x02 -#define FEC_IPV4 0x0001 +#define FEC_PWID 0x80 +#define FEC_GENPWID 0x81 + +#define CONTROL_WORD_FLAG 0x8000 +#define PW_TYPE_ETHERNET_TAGGED 0x0004 +#define PW_TYPE_ETHERNET 0x0005 + +/* RFC 4447 Sub-TLV record */ +struct subtlv { + u_int8_t type; + u_int8_t length; +}; +#define SUBTLV_HDR_LEN 2 + +#define SUBTLV_IFMTU 0x01 +#define SUBTLV_VLANID 0x06 + +#define FEC_SUBTLV_IFMTU_LEN 4 +#define FEC_SUBTLV_VLANID_LEN 4 struct label_tlv { u_int16_t type; @@ -217,6 +256,21 @@ struct reqid_tlv { #define REQID_TLV_LEN 8 +struct pw_status_tlv { + u_int16_t type; + u_int16_t length; + u_int32_t value; +}; + +#define PW_STATUS_TLV_LEN 8 + +#define PW_FORWARDING 0 +#define PW_NOT_FORWARDING (1 << 0) +#define PW_LOCAL_RX_FAULT (1 << 1) +#define PW_LOCAL_TX_FAULT (1 << 2) +#define PW_PSN_RX_FAULT (1 << 3) +#define PW_PSN_TX_FAULT (1 << 4) + #define NO_LABEL UINT_MAX #endif /* !_LDP_H_ */ diff --git a/usr.sbin/ldpd/ldpd.c b/usr.sbin/ldpd/ldpd.c index f0c72fd9f29..0f6dccce512 100644 --- a/usr.sbin/ldpd/ldpd.c +++ b/usr.sbin/ldpd/ldpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ldpd.c,v 1.24 2015/07/21 04:45:21 renato Exp $ */ +/* $OpenBSD: ldpd.c,v 1.25 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> @@ -56,6 +56,7 @@ void main_dispatch_lde(int, short, void *); int ldp_reload(void); int ldp_sendboth(enum imsg_type, void *, u_int16_t); +void merge_l2vpns(struct ldpd_conf *, struct l2vpn *, struct l2vpn *); int pipe_parent2ldpe[2]; int pipe_parent2lde[2]; @@ -399,6 +400,7 @@ main_dispatch_lde(int fd, short event, void *bula) struct imsg imsg; ssize_t n; int shut = 0; + struct kpw *kpw; if (event & EV_READ) { if ((n = imsg_read(ibuf)) == -1) @@ -437,6 +439,22 @@ main_dispatch_lde(int fd, short event, void *bula) log_warn("main_dispatch_lde: error deleting " "route"); break; + case IMSG_KPWLABEL_CHANGE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct kpw)) + fatalx("invalid size of IMSG_KPWLABEL_CHANGE"); + + kpw = imsg.data; + kmpw_set(kpw); + break; + case IMSG_KPWLABEL_DELETE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct kpw)) + fatalx("invalid size of IMSG_KPWLABEL_DELETE"); + + kpw = imsg.data; + kmpw_unset(kpw); + break; default: log_debug("main_dispatch_lde: error handling imsg %d", imsg.hdr.type); @@ -528,6 +546,9 @@ ldp_reload(void) struct iface *iface; struct tnbr *tnbr; struct nbr_params *nbrp; + struct l2vpn *l2vpn; + struct l2vpn_if *lif; + struct l2vpn_pw *pw; struct ldpd_conf *xconf; if ((xconf = parse_config(conffile, ldpd_conf->opts)) == NULL) @@ -554,6 +575,23 @@ ldp_reload(void) return (-1); } + LIST_FOREACH(l2vpn, &xconf->l2vpn_list, entry) { + if (ldp_sendboth(IMSG_RECONF_L2VPN, l2vpn, + sizeof(*l2vpn)) == -1) + return (-1); + + LIST_FOREACH(lif, &l2vpn->if_list, entry) { + if (ldp_sendboth(IMSG_RECONF_L2VPN_IF, lif, + sizeof(*lif)) == -1) + return (-1); + } + LIST_FOREACH(pw, &l2vpn->pw_list, entry) { + if (ldp_sendboth(IMSG_RECONF_L2VPN_PW, pw, + sizeof(*pw)) == -1) + return (-1); + } + } + if (ldp_sendboth(IMSG_RECONF_END, NULL, 0) == -1) return (-1); @@ -578,6 +616,7 @@ merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) struct iface *iface, *itmp, *xi; struct tnbr *tnbr, *ttmp, *xt; struct nbr_params *nbrp, *ntmp, *xn; + struct l2vpn *l2vpn, *ltmp, *xl; struct nbr *nbr; /* change of rtr_id needs a restart */ @@ -706,10 +745,158 @@ merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) } } + /* merge l2vpns */ + LIST_FOREACH_SAFE(l2vpn, &conf->l2vpn_list, entry, ltmp) { + /* find deleted l2vpns */ + if ((xl = l2vpn_find(xconf, l2vpn->name)) == NULL) { + LIST_REMOVE(l2vpn, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_del(l2vpn); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_exit(l2vpn); + free(l2vpn); + break; + case PROC_MAIN: + free(l2vpn); + break; + } + } + } + LIST_FOREACH_SAFE(xl, &xconf->l2vpn_list, entry, ltmp) { + /* find new l2vpns */ + if ((l2vpn = l2vpn_find(conf, xl->name)) == NULL) { + LIST_REMOVE(xl, entry); + LIST_INSERT_HEAD(&conf->l2vpn_list, xl, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_init(xl); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_init(xl); + break; + case PROC_MAIN: + break; + } + continue; + } + + /* update existing l2vpns */ + merge_l2vpns(conf, l2vpn, xl); + } + free(xconf); } void +merge_l2vpns(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) +{ + struct l2vpn_if *lif, *ftmp, *xf; + struct l2vpn_pw *pw, *ptmp, *xp; + + /* merge intefaces */ + LIST_FOREACH_SAFE(lif, &l2vpn->if_list, entry, ftmp) { + /* find deleted interfaces */ + if ((xf = l2vpn_if_find(xl, lif->ifindex)) == NULL) { + LIST_REMOVE(lif, entry); + free(lif); + } + } + LIST_FOREACH_SAFE(xf, &xl->if_list, entry, ftmp) { + /* find new interfaces */ + if ((lif = l2vpn_if_find(l2vpn, xf->ifindex)) == NULL) { + LIST_REMOVE(xf, entry); + LIST_INSERT_HEAD(&l2vpn->if_list, xf, entry); + lif->l2vpn = l2vpn; + continue; + } + + /* update existing interfaces */ + lif->l2vpn = l2vpn; + } + + /* merge pseudowires */ + LIST_FOREACH_SAFE(pw, &l2vpn->pw_list, entry, ptmp) { + /* find deleted pseudowires */ + if ((xp = l2vpn_pw_find(xl, pw->ifindex)) == NULL) { + LIST_REMOVE(pw, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_pw_del(pw); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_pw_exit(pw); + free(pw); + break; + case PROC_MAIN: + free(pw); + break; + } + } + } + LIST_FOREACH_SAFE(xp, &xl->pw_list, entry, ptmp) { + /* find new pseudowires */ + if ((pw = l2vpn_pw_find(l2vpn, xp->ifindex)) == NULL) { + LIST_REMOVE(xp, entry); + LIST_INSERT_HEAD(&l2vpn->pw_list, xp, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_pw_init(xp); + break; + case PROC_LDP_ENGINE: + ldpe_l2vpn_pw_init(xp); + break; + case PROC_MAIN: + break; + } + continue; + } + + /* changes that require a full reset of the pseudowire */ + if (l2vpn->pw_type != xl->pw_type || + l2vpn->mtu != xl->mtu || + pw->addr.s_addr != xp->addr.s_addr || + pw->pwid != xp->pwid || + ((pw->flags & + (F_PW_STATUSTLV_CONF|F_PW_CONTROLWORD_CONF)) != + (xp->flags & + (F_PW_STATUSTLV_CONF|F_PW_CONTROLWORD_CONF)))) { + LIST_REMOVE(pw, entry); + LIST_REMOVE(xp, entry); + LIST_INSERT_HEAD(&l2vpn->pw_list, xp, entry); + + switch (ldpd_process) { + case PROC_LDE_ENGINE: + l2vpn_pw_del(pw); + l2vpn_pw_init(xp); + break; + case PROC_LDP_ENGINE: + if (pw->addr.s_addr != xp->addr.s_addr) { + ldpe_l2vpn_pw_exit(pw); + ldpe_l2vpn_pw_init(xp); + } + free(pw); + break; + case PROC_MAIN: + free(pw); + break; + } + } + + /* update existing pseudowires */ + pw->l2vpn = xp->l2vpn; + } + + l2vpn->mtu = xl->mtu; + l2vpn->br_ifindex = xl->br_ifindex; +} + +void config_clear(struct ldpd_conf *conf) { struct ldpd_conf *xconf; @@ -720,6 +907,7 @@ config_clear(struct ldpd_conf *conf) LIST_INIT(&xconf->iface_list); LIST_INIT(&xconf->tnbr_list); LIST_INIT(&xconf->nbrp_list); + LIST_INIT(&xconf->l2vpn_list); merge_config(conf, xconf); free(conf); diff --git a/usr.sbin/ldpd/ldpd.conf.5 b/usr.sbin/ldpd/ldpd.conf.5 index 34c37717a9f..02a0f7ef096 100644 --- a/usr.sbin/ldpd/ldpd.conf.5 +++ b/usr.sbin/ldpd/ldpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ldpd.conf.5,v 1.17 2015/07/19 22:36:30 jmc Exp $ +.\" $OpenBSD: ldpd.conf.5,v 1.18 2015/07/21 04:52:29 renato Exp $ .\" .\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> .\" Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org> @@ -18,7 +18,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 19 2015 $ +.Dd $Mdocdate: July 21 2015 $ .Dt LDPD.CONF 5 .Os .Sh NAME @@ -31,7 +31,7 @@ daemon implements the Label Distribution Protocol as described in RFC 5036. .Sh SECTIONS The .Nm -config file is divided into five main sections. +config file is divided into six main sections. .Bl -tag -width xxxx .It Sy Macros User-defined variables may be defined and used later, simplifying the @@ -45,6 +45,8 @@ Interface-specific parameters. Targeted neighbor specific parameters. .It Sy Neighbors Configuration Neighbor-specific parameters. +.It Sy Layer 2 VPNs Configuration +Layer 2 VPNs parameters as per RFC 4447. .El .Sh MACROS Much like @@ -159,6 +161,87 @@ Neighbor-specific parameters are listed below. .It Ic password Ar secret Enable TCP MD5 signatures per RFC 5036. .El +.Sh LAYER 2 VPNS +.Xr ldpd 8 +implements the signaling of pseudowires which can be used to +implement either the VPWS solution (also known as PWE3) or the VPLS +solution. Currently only the VPLS solution is supported. +.Bd -literal -offset indent +l2vpn name type vpls { + bridge bridge0 + interface em1 + pseudowire mpw1 { + pw-id 100 + neighbor 192.168.1.10 + } + pseudowire mpw2 { + pw-id 200 + neighbor 10.0.1.5 + } +} +.Ed +.Pp +Layer 2 VPN specific parameters are listed below. +.Bl -tag -width Ds +.It Xo +.Ic type +.Pq Ic ethernet Ns | Ns Ic ethernet-tagged +.Xc +Specify the type of the configured pseudowires. The type must be the +same at both endpoints. The default is +.Ic ethernet . +.Pp +.It Ic mtu Ar number +Set the MTU advertised in the pseudowires. Local and remote MTUs must +match for a pseudowire to be set up. The default value is 1500. +.Pp +.It Ic bridge Ar interface +Set the bridge interface the VPLS is associated with. This parameter +is optional and is only used to remove MAC addresses received from MAC +address withdrawal messages. Only one bridge interface can be set. +.Pp +.It Ic interface Ar interface +Configure a non pseudowire interface pertaining to the VPLS. This +parameter is optional and is only used to send MAC address withdrawal +messages when the specified interface is shutdown. Multiple interfaces +can be configured. +.Sh PSEUDOWIRES +Each +.Xr mpw 4 +pseudowire interface can have several parameters configured individually, +otherwise they are inherited. A pseudowire interface is specified by +its name. +.Bd -literal -offset indent +pseudowire mpw5 { + pw-id 5000 + neighbor 172.16.1.50 +} +.Ed +.Pp +Pseudowire-specific parameters are listed below. +.Bl -tag -width Ds +.Pp +.It Ic neighbor Ar address +Specify the endpoint of the pseudowire on the remote PE router. A targeted +neighbor will automatically be created for this address. +.Pp +.It Ic pw-id Ar number +Set the PW ID used to identify the pseudowire. The PW ID must be the +same at both endpoints. Valid range is 1\-4294967295. +.It Xo +.Ic status-tlv +.Pq Ic yes Ns | Ns Ic no +.Xc +Specify whether the use of the Status TLV is preferred or not +preferred. The default is +.Ic yes . +.It Xo +.Ic control-word +.Pq Ic yes Ns | Ns Ic no +.Xc +Specify whether the use of the control word is preferred or not +preferred. The default is +.Ic yes . .Sh FILES .Bl -tag -width "/etc/ldpd.conf" -compact .It Pa /etc/ldpd.conf diff --git a/usr.sbin/ldpd/ldpd.h b/usr.sbin/ldpd/ldpd.h index 87fd28128bb..8b0b5ef2324 100644 --- a/usr.sbin/ldpd/ldpd.h +++ b/usr.sbin/ldpd/ldpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ldpd.h,v 1.53 2015/07/21 04:45:21 renato Exp $ */ +/* $OpenBSD: ldpd.h,v 1.54 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -38,6 +38,7 @@ #define LDPD_USER "_ldpd" #define TCP_MD5_KEY_LEN 80 +#define L2VPN_NAME_LEN 32 #define NBR_IDSELF 1 #define NBR_CNTSTART (NBR_IDSELF + 1) @@ -77,6 +78,8 @@ enum imsg_type { IMSG_CTL_SHOW_DISCOVERY, IMSG_CTL_SHOW_NBR, IMSG_CTL_SHOW_LIB, + IMSG_CTL_SHOW_L2VPN_PW, + IMSG_CTL_SHOW_L2VPN_BINDING, IMSG_CTL_FIB_COUPLE, IMSG_CTL_FIB_DECOUPLE, IMSG_CTL_KROUTE, @@ -86,6 +89,8 @@ enum imsg_type { IMSG_CTL_LOG_VERBOSE, IMSG_KLABEL_CHANGE, IMSG_KLABEL_DELETE, + IMSG_KPWLABEL_CHANGE, + IMSG_KPWLABEL_DELETE, IMSG_IFSTATUS, IMSG_NEWADDR, IMSG_DELADDR, @@ -105,6 +110,7 @@ enum imsg_type { IMSG_WITHDRAW_ADD_END, IMSG_ADDRESS_ADD, IMSG_ADDRESS_DEL, + IMSG_NOTIFICATION, IMSG_NOTIFICATION_SEND, IMSG_NEIGHBOR_UP, IMSG_NEIGHBOR_DOWN, @@ -114,6 +120,9 @@ enum imsg_type { IMSG_RECONF_IFACE, IMSG_RECONF_TNBR, IMSG_RECONF_NBRP, + IMSG_RECONF_L2VPN, + IMSG_RECONF_L2VPN_IF, + IMSG_RECONF_L2VPN_PW, IMSG_RECONF_END }; @@ -164,21 +173,41 @@ enum nbr_action { TAILQ_HEAD(mapping_head, mapping_entry); struct map { - struct in_addr prefix; - u_int8_t prefixlen; - u_int32_t label; + u_int8_t type; u_int32_t messageid; + union map_fec { + struct { + struct in_addr prefix; + u_int8_t prefixlen; + } ipv4; + struct { + u_int16_t type; + u_int32_t pwid; + u_int32_t group_id; + u_int16_t ifmtu; + } pwid; + } fec; + u_int32_t label; u_int32_t requestid; + u_int32_t pw_status; u_int8_t flags; }; -#define F_MAP_WILDCARD 0x01 /* wildcard FEC */ -#define F_MAP_REQ_ID 0x02 /* optional request message id present */ +#define F_MAP_REQ_ID 0x01 /* optional request message id present */ +#define F_MAP_PW_CWORD 0x02 /* pseudowire control word */ +#define F_MAP_PW_ID 0x04 /* pseudowire connection id */ +#define F_MAP_PW_IFMTU 0x08 /* pseudowire interface parameter */ +#define F_MAP_PW_STATUS 0x10 /* pseudowire status */ struct notify_msg { u_int32_t messageid; u_int32_t status; u_int32_t type; + u_int32_t pw_status; + struct map fec; + u_int8_t flags; }; +#define F_NOTIF_PW_STATUS 0x01 /* pseudowire status tlv present */ +#define F_NOTIF_FEC 0x02 /* fec tlv present */ struct if_addr { LIST_ENTRY(if_addr) entry; @@ -218,6 +247,7 @@ struct tnbr { u_int16_t hello_holdtime; u_int16_t hello_interval; + u_int16_t pw_count; u_int8_t flags; }; #define F_TNBR_CONFIGURED 0x01 @@ -239,6 +269,47 @@ struct nbr_params { } auth; }; +struct l2vpn_if { + LIST_ENTRY(l2vpn_if) entry; + struct l2vpn *l2vpn; + char ifname[IF_NAMESIZE]; + unsigned int ifindex; + u_int16_t flags; + u_int8_t link_state; +}; + +struct l2vpn_pw { + LIST_ENTRY(l2vpn_pw) entry; + struct l2vpn *l2vpn; + struct in_addr addr; + u_int32_t pwid; + char ifname[IF_NAMESIZE]; + unsigned int ifindex; + u_int32_t remote_group; + u_int16_t remote_mtu; + u_int32_t remote_status; + u_int8_t flags; +}; +#define F_PW_STATUSTLV_CONF 0x01 /* status tlv configured */ +#define F_PW_STATUSTLV 0x02 /* status tlv negotiated */ +#define F_PW_CONTROLWORD_CONF 0x04 /* control word configured */ +#define F_PW_CONTROLWORD 0x08 /* control word negotiated */ +#define F_PW_STATUS_UP 0x10 /* pseudowire is operational */ + +struct l2vpn { + LIST_ENTRY(l2vpn) entry; + char name[L2VPN_NAME_LEN]; + int type; + int pw_type; + int mtu; + char br_ifname[IF_NAMESIZE]; + unsigned int br_ifindex; + LIST_HEAD(, l2vpn_if) if_list; + LIST_HEAD(, l2vpn_pw) pw_list; +}; +#define L2VPN_TYPE_VPWS 1 +#define L2VPN_TYPE_VPLS 2 + /* ldp_conf */ enum { PROC_MAIN, @@ -257,6 +328,7 @@ struct ldpd_conf { struct if_addr_head addr_list; LIST_HEAD(, tnbr) tnbr_list; LIST_HEAD(, nbr_params) nbrp_list; + LIST_HEAD(, l2vpn) l2vpn_list; u_int32_t opts; #define LDPD_OPT_VERBOSE 0x00000001 @@ -284,6 +356,15 @@ struct kroute { u_int8_t priority; }; +struct kpw { + u_short ifindex; + int pw_type; + struct in_addr nexthop; + u_int32_t local_label; + u_int32_t remote_label; + u_int8_t flags; +}; + struct kaddr { u_short ifindex; struct in_addr addr; @@ -341,6 +422,20 @@ struct ctl_rt { u_int8_t in_use; }; +struct ctl_pw { + u_int16_t type; + char ifname[IF_NAMESIZE]; + u_int32_t pwid; + struct in_addr nexthop; + u_int32_t local_label; + u_int32_t local_gid; + u_int16_t local_ifmtu; + u_int32_t remote_label; + u_int32_t remote_gid; + u_int16_t remote_ifmtu; + u_int32_t status; +}; + /* parse.y */ struct ldpd_conf *parse_config(char *, int); int cmdline_symset(char *); @@ -360,6 +455,10 @@ void kr_ifinfo(char *, pid_t); struct kif *kif_findname(char *); u_int8_t mask2prefixlen(in_addr_t); in_addr_t prefixlen2mask(u_int8_t); +void kmpw_set(struct kpw *); +void kmpw_unset(struct kpw *); +void kmpw_install(const char *, struct kpw *); +void kmpw_uninstall(const char *, struct kpw *); /* log.h */ const char *nbr_state_name(int); diff --git a/usr.sbin/ldpd/ldpe.c b/usr.sbin/ldpd/ldpe.c index 2a1a81ae2f3..5e82620eae0 100644 --- a/usr.sbin/ldpd/ldpe.c +++ b/usr.sbin/ldpd/ldpe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ldpe.c,v 1.35 2015/07/21 04:45:21 renato Exp $ */ +/* $OpenBSD: ldpe.c,v 1.36 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> @@ -333,6 +333,9 @@ ldpe_dispatch_main(int fd, short event, void *bula) struct iface *niface; struct tnbr *ntnbr; struct nbr_params *nnbrp; + static struct l2vpn *nl2vpn; + struct l2vpn_if *nlif; + struct l2vpn_pw *npw; struct imsg imsg; struct imsgev *iev = bula; struct imsgbuf *ibuf = &iev->ibuf; @@ -441,6 +444,7 @@ ldpe_dispatch_main(int fd, short event, void *bula) LIST_INIT(&nconf->addr_list); LIST_INIT(&nconf->tnbr_list); LIST_INIT(&nconf->nbrp_list); + LIST_INIT(&nconf->l2vpn_list); break; case IMSG_RECONF_IFACE: if ((niface = malloc(sizeof(struct iface))) == NULL) @@ -466,6 +470,32 @@ ldpe_dispatch_main(int fd, short event, void *bula) LIST_INSERT_HEAD(&nconf->nbrp_list, nnbrp, entry); break; + case IMSG_RECONF_L2VPN: + if ((nl2vpn = malloc(sizeof(struct l2vpn))) == NULL) + fatal(NULL); + memcpy(nl2vpn, imsg.data, sizeof(struct l2vpn)); + + LIST_INIT(&nl2vpn->if_list); + LIST_INIT(&nl2vpn->pw_list); + + LIST_INSERT_HEAD(&nconf->l2vpn_list, nl2vpn, entry); + break; + case IMSG_RECONF_L2VPN_IF: + if ((nlif = malloc(sizeof(struct l2vpn_if))) == NULL) + fatal(NULL); + memcpy(nlif, imsg.data, sizeof(struct l2vpn_if)); + + nlif->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->if_list, nlif, entry); + break; + case IMSG_RECONF_L2VPN_PW: + if ((npw = malloc(sizeof(struct l2vpn_pw))) == NULL) + fatal(NULL); + memcpy(npw, imsg.data, sizeof(struct l2vpn_pw)); + + npw->l2vpn = nl2vpn; + LIST_INSERT_HEAD(&nl2vpn->pw_list, npw, entry); + break; case IMSG_RECONF_END: merge_config(leconf, nconf); nconf = NULL; @@ -602,11 +632,12 @@ ldpe_dispatch_lde(int fd, short event, void *bula) if (nbr->state != NBR_STA_OPER) return; - send_notification_nbr(nbr, nm.status, - htonl(nm.messageid), htonl(nm.type)); + send_notification_full(nbr->tcp, &nm); break; case IMSG_CTL_END: case IMSG_CTL_SHOW_LIB: + case IMSG_CTL_SHOW_L2VPN_PW: + case IMSG_CTL_SHOW_L2VPN_BINDING: control_imsg_relay(&imsg); break; default: diff --git a/usr.sbin/ldpd/ldpe.h b/usr.sbin/ldpd/ldpe.h index d69b973f11b..3aceadf3d91 100644 --- a/usr.sbin/ldpd/ldpe.h +++ b/usr.sbin/ldpd/ldpe.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ldpe.h,v 1.39 2015/07/21 04:43:28 renato Exp $ */ +/* $OpenBSD: ldpe.h,v 1.40 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> @@ -127,6 +127,7 @@ int recv_keepalive(struct nbr *, char *, u_int16_t); void send_notification_nbr(struct nbr *, u_int32_t, u_int32_t, u_int32_t); void send_notification(u_int32_t, struct tcp_conn *, u_int32_t, u_int32_t); +void send_notification_full(struct tcp_conn *, struct notify_msg *); int recv_notification(struct nbr *, char *, u_int16_t); /* address.c */ @@ -138,6 +139,10 @@ void send_address_withdraw(struct nbr *, struct if_addr *); #define PREFIX_SIZE(x) (((x) + 7) / 8) void send_labelmessage(struct nbr *, u_int16_t, struct mapping_head *); int recv_labelmessage(struct nbr *, char *, u_int16_t, u_int16_t); +void gen_fec_tlv(struct ibuf *, struct map *); +void gen_pw_status_tlv(struct ibuf *, u_int32_t); +int tlv_decode_fec_elm(struct nbr *, struct ldp_msg *, char *, + u_int16_t, struct map *); /* ldpe.c */ pid_t ldpe(struct ldpd_conf *, int[2], int[2], int[2]); @@ -248,4 +253,10 @@ int pfkey_establish(struct nbr *, struct nbr_params *); int pfkey_remove(struct nbr *); int pfkey_init(struct ldpd_sysdep *); +/* l2vpn.c */ +void ldpe_l2vpn_init(struct l2vpn *); +void ldpe_l2vpn_exit(struct l2vpn *); +void ldpe_l2vpn_pw_init(struct l2vpn_pw *); +void ldpe_l2vpn_pw_exit(struct l2vpn_pw *); + #endif /* _LDPE_H_ */ diff --git a/usr.sbin/ldpd/log.c b/usr.sbin/ldpd/log.c index 8a4e1c538b2..cf28c293012 100644 --- a/usr.sbin/ldpd/log.c +++ b/usr.sbin/ldpd/log.c @@ -1,4 +1,4 @@ -/* $OpenBSD: log.c,v 1.14 2015/07/19 20:54:17 renato Exp $ */ +/* $OpenBSD: log.c,v 1.15 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -30,6 +30,7 @@ #include <unistd.h> #include "ldpd.h" +#include "lde.h" #include "log.h" static const char * const procnames[] = { @@ -276,6 +277,22 @@ notification_name(u_int32_t status) return ("Bad KeepAlive Time"); case S_INTERN_ERR: return ("Internal Error"); + case S_ILLEGAL_CBIT: + return ("Illegal C-Bit"); + case S_WRONG_CBIT: + return ("Wrong C-Bit"); + case S_INCPT_BITRATE: + return ("Incompatible bit-rate"); + case S_CEP_MISCONF: + return ("CEP-TDM mis-configuration"); + case S_PW_STATUS: + return ("PW Status"); + case S_UNASSIGN_TAI: + return ("Unassigned/Unrecognized TAI"); + case S_MISCONF_ERR: + return ("Generic Misconfiguration Error"); + case S_WITHDRAW_MTHD: + return ("Label Withdraw PW Status Method"); default: snprintf(buf, sizeof(buf), "[%08x]", status); return (buf); @@ -283,18 +300,74 @@ notification_name(u_int32_t status) } const char * -log_fec(struct map *map) +pw_type_name(u_int16_t pw_type) { - static char buf[32]; - char pstr[32]; + static char buf[64]; + + switch (pw_type) { + case PW_TYPE_ETHERNET_TAGGED: + return ("Eth Tagged"); + case PW_TYPE_ETHERNET: + return ("Ethernet"); + default: + snprintf(buf, sizeof(buf), "[%0x]", pw_type); + return (buf); + } +} + +const char * +log_map(struct map *map) +{ + static char buf[64]; + char pstr[64]; + + switch (map->type) { + case FEC_WILDCARD: + if (snprintf(buf, sizeof(buf), "wildcard")) + return ("???"); + break; + case FEC_PREFIX: + if (snprintf(buf, sizeof(buf), "%s/%u", + inet_ntop(AF_INET, &map->fec.ipv4.prefix, pstr, + sizeof(pstr)), map->fec.ipv4.prefixlen) == -1) + return ("???"); + break; + case FEC_PWID: + if (snprintf(buf, sizeof(buf), "pwid %u (%s)", + map->fec.pwid.pwid, + pw_type_name(map->fec.pwid.type)) == -1) + return ("???"); + break; + default: + return ("???"); + } + + return (buf); +} - if (map->flags & F_MAP_WILDCARD) - return ("wildcard"); +const char * +log_fec(struct fec *fec) +{ + static char buf[64]; + char pstr[32]; - if (snprintf(buf, sizeof(buf), "%s/%u", - inet_ntop(AF_INET, &map->prefix, pstr, sizeof(pstr)), - map->prefixlen) == -1) + switch (fec->type) { + case FEC_TYPE_IPV4: + if (snprintf(buf, sizeof(buf), "%s/%u", + inet_ntop(AF_INET, &fec->u.ipv4.prefix, pstr, + sizeof(pstr)), fec->u.ipv4.prefixlen) == -1) + return ("???"); + break; + case FEC_TYPE_PWID: + if (snprintf(buf, sizeof(buf), + "pwid %u (%s) - %s", + fec->u.pwid.pwid, pw_type_name(fec->u.pwid.type), + inet_ntoa(fec->u.pwid.nexthop)) == -1) + return ("???"); + break; + default: return ("???"); + } return (buf); } diff --git a/usr.sbin/ldpd/log.h b/usr.sbin/ldpd/log.h index ba10c360c8c..452fb986bc3 100644 --- a/usr.sbin/ldpd/log.h +++ b/usr.sbin/ldpd/log.h @@ -1,4 +1,4 @@ -/* $OpenBSD: log.h,v 1.5 2014/11/03 18:44:36 bluhm Exp $ */ +/* $OpenBSD: log.h,v 1.6 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -31,7 +31,10 @@ void log_info(const char *, ...); void log_debug(const char *, ...); void fatal(const char *) __dead; void fatalx(const char *) __dead; -const char *log_fec(struct map *); +const char *pw_type_name(u_int16_t); +const char *log_map(struct map *); +struct fec; +const char *log_fec(struct fec *); void log_rtmsg(u_char); #endif /* _LOG_H_ */ diff --git a/usr.sbin/ldpd/notification.c b/usr.sbin/ldpd/notification.c index e1dfacdc609..70045451cff 100644 --- a/usr.sbin/ldpd/notification.c +++ b/usr.sbin/ldpd/notification.c @@ -1,4 +1,4 @@ -/* $OpenBSD: notification.c,v 1.17 2014/10/25 03:23:49 lteo Exp $ */ +/* $OpenBSD: notification.c,v 1.18 2015/07/21 04:52:29 renato Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -39,26 +39,32 @@ int gen_status_tlv(struct ibuf *, u_int32_t, u_int32_t, u_int32_t); void -send_notification_nbr(struct nbr *nbr, u_int32_t status, u_int32_t msgid, - u_int32_t type) -{ - log_debug("send_notification_nbr: nbr ID %s, status %s", - inet_ntoa(nbr->id), notification_name(status)); - send_notification(status, nbr->tcp, msgid, type); - nbr_fsm(nbr, NBR_EVT_PDU_SENT); -} - -void -send_notification(u_int32_t status, struct tcp_conn *tcp, u_int32_t msgid, - u_int32_t type) +send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) { struct ibuf *buf; u_int16_t size; + if (tcp->nbr) + log_debug("send_notification_full: nbr ID %s, status %s", + inet_ntoa(tcp->nbr->id), notification_name(nm->status)); + if ((buf = ibuf_open(LDP_MAX_LEN)) == NULL) fatal("send_notification"); + /* calculate size */ size = LDP_HDR_SIZE + sizeof(struct ldp_msg) + STATUS_SIZE; + if (nm->flags & F_NOTIF_PW_STATUS) + size += PW_STATUS_TLV_LEN; + if (nm->flags & F_NOTIF_FEC) { + size += TLV_HDR_LEN; + switch (nm->fec.type) { + case FEC_PWID: + size += FEC_PWID_ELM_MIN_LEN; + if (nm->fec.flags & F_MAP_PW_ID) + size += sizeof(u_int32_t); + break; + } + } gen_ldp_hdr(buf, size); @@ -66,20 +72,46 @@ send_notification(u_int32_t status, struct tcp_conn *tcp, u_int32_t msgid, gen_msg_tlv(buf, MSG_TYPE_NOTIFICATION, size); - size -= sizeof(struct ldp_msg); - - gen_status_tlv(buf, status, msgid, type); + gen_status_tlv(buf, nm->status, nm->messageid, nm->type); + /* optional tlvs */ + if (nm->flags & F_NOTIF_PW_STATUS) + gen_pw_status_tlv(buf, nm->pw_status); + if (nm->flags & F_NOTIF_FEC) + gen_fec_tlv(buf, &nm->fec); evbuf_enqueue(&tcp->wbuf, buf); } +/* send a notification without optional tlvs */ +void +send_notification(u_int32_t status, struct tcp_conn *tcp, u_int32_t msgid, + u_int32_t type) +{ + struct notify_msg nm; + + bzero(&nm, sizeof(nm)); + nm.status = status; + nm.messageid = msgid; + nm.type = type; + + send_notification_full(tcp, &nm); +} + +void +send_notification_nbr(struct nbr *nbr, u_int32_t status, u_int32_t msgid, + u_int32_t type) +{ + send_notification(status, nbr->tcp, msgid, type); + nbr_fsm(nbr, NBR_EVT_PDU_SENT); +} + int recv_notification(struct nbr *nbr, char *buf, u_int16_t len) { struct ldp_msg not; struct status_tlv st; - - log_debug("recv_notification: neighbor ID %s", inet_ntoa(nbr->id)); + struct notify_msg nm; + int tlen; bcopy(buf, ¬, sizeof(not)); @@ -97,8 +129,89 @@ recv_notification(struct nbr *nbr, char *buf, u_int16_t len) session_shutdown(nbr, S_BAD_TLV_LEN, not.msgid, not.type); return (-1); } + buf += STATUS_SIZE; + len -= STATUS_SIZE; + + bzero(&nm, sizeof(nm)); + nm.status = ntohl(st.status_code); + + /* Optional Parameters */ + while (len > 0) { + struct tlv tlv; + + if (len < sizeof(tlv)) { + session_shutdown(nbr, S_BAD_TLV_LEN, not.msgid, + not.type); + return (-1); + } + + bcopy(buf, &tlv, sizeof(tlv)); + if (ntohs(tlv.length) > len - TLV_HDR_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, not.msgid, + not.type); + return (-1); + } + buf += TLV_HDR_LEN; + len -= TLV_HDR_LEN; + + switch (ntohs(tlv.type) & ~UNKNOWN_FLAG) { + case TLV_TYPE_EXTSTATUS: + case TLV_TYPE_RETURNEDPDU: + case TLV_TYPE_RETURNEDMSG: + /* TODO is there any use for this? */ + break; + case TLV_TYPE_PW_STATUS: + if (ntohs(tlv.length) != 4) { + session_shutdown(nbr, S_BAD_TLV_LEN, + not.msgid, not.type); + return (-1); + } + + nm.pw_status = ntohl(*(u_int32_t *)buf); + nm.flags |= F_NOTIF_PW_STATUS; + break; + case TLV_TYPE_FEC: + if ((tlen = tlv_decode_fec_elm(nbr, ¬, buf, + ntohs(tlv.length), &nm.fec)) == -1) + return (-1); + /* allow only one fec element */ + if (tlen != ntohs(tlv.length)) { + session_shutdown(nbr, S_BAD_TLV_VAL, + not.msgid, not.type); + return (-1); + } + nm.flags |= F_NOTIF_FEC; + break; + default: + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) { + send_notification_nbr(nbr, S_UNKNOWN_TLV, + not.msgid, not.type); + } + /* ignore unknown tlv */ + break; + } + buf += ntohs(tlv.length); + len -= ntohs(tlv.length); + } - /* TODO optional parameters: ext status, returned PDU and msg */ + if (nm.status == S_PW_STATUS) { + if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) { + send_notification_nbr(nbr, S_MISS_MSG, + not.msgid, not.type); + return (-1); + } + + switch (nm.fec.type) { + case FEC_PWID: + case FEC_GENPWID: + break; + default: + send_notification_nbr(nbr, S_BAD_TLV_VAL, + not.msgid, not.type); + return (-1); + break; + } + } if (st.status_code & htonl(STATUS_FATAL)) log_warnx("received notification from neighbor %s: %s", @@ -120,7 +233,10 @@ recv_notification(struct nbr *nbr, char *buf, u_int16_t len) nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); return (-1); } - /* XXX in some cases we should inform the RDE about non-fatal ones */ + + if (nm.status == S_PW_STATUS) + ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, + &nm, sizeof(nm)); return (ntohs(not.length)); } @@ -137,8 +253,8 @@ gen_status_tlv(struct ibuf *buf, u_int32_t status, u_int32_t msgid, st.length = htons(STATUS_TLV_LEN); st.status_code = htonl(status); - st.msg_id = msgid; - st.msg_type = type; + st.msg_id = htonl(msgid); + st.msg_type = htonl(type); return (ibuf_add(buf, &st, STATUS_SIZE)); } |