src/usr.sbin/ldpd/lde.c

1426 lines
33 KiB
C

/* $OpenBSD: lde.c,v 1.78 2024/04/23 13:34:51 jsg Exp $ */
/*
* Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org>
* Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2004 Esben Norby <norby@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@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/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netmpls/mpls.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <pwd.h>
#include <unistd.h>
#include <limits.h>
#include "ldp.h"
#include "ldpd.h"
#include "ldpe.h"
#include "log.h"
#include "lde.h"
static void lde_sig_handler(int sig, short, void *);
static __dead void lde_shutdown(void);
static int lde_imsg_compose_parent(int, pid_t, void *, uint16_t);
static void lde_dispatch_imsg(int, short, void *);
static void lde_dispatch_parent(int, short, void *);
static __inline int lde_nbr_compare(struct lde_nbr *,
struct lde_nbr *);
static struct lde_nbr *lde_nbr_new(uint32_t, struct lde_nbr *);
static void lde_nbr_del(struct lde_nbr *);
static struct lde_nbr *lde_nbr_find(uint32_t);
static void lde_nbr_clear(void);
static void lde_nbr_addr_update(struct lde_nbr *,
struct lde_addr *, int);
static void lde_map_free(void *);
static int lde_address_add(struct lde_nbr *, struct lde_addr *);
static int lde_address_del(struct lde_nbr *, struct lde_addr *);
static void lde_address_list_free(struct lde_nbr *);
RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare)
struct ldpd_conf *ldeconf;
struct nbr_tree lde_nbrs = RB_INITIALIZER(&lde_nbrs);
static struct imsgev *iev_ldpe;
static struct imsgev *iev_main;
static void
lde_sig_handler(int sig, short event, void *arg)
{
/*
* signal handler rules don't apply, libevent decouples for us
*/
switch (sig) {
case SIGINT:
case SIGTERM:
lde_shutdown();
/* NOTREACHED */
default:
fatalx("unexpected signal");
}
}
/* label decision engine */
void
lde(int debug, int verbose)
{
struct event ev_sigint, ev_sigterm;
struct timeval now;
struct passwd *pw;
ldeconf = config_new_empty();
log_init(debug);
log_verbose(verbose);
setproctitle("label decision engine");
ldpd_process = PROC_LDE_ENGINE;
log_procname = "lde";
if ((pw = getpwnam(LDPD_USER)) == NULL)
fatal("getpwnam");
if (chroot(pw->pw_dir) == -1)
fatal("chroot");
if (chdir("/") == -1)
fatal("chdir(\"/\")");
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("can't drop privileges");
if (pledge("stdio recvfd", NULL) == -1)
fatal("pledge");
event_init();
/* setup signal handler */
signal_set(&ev_sigint, SIGINT, lde_sig_handler, NULL);
signal_set(&ev_sigterm, SIGTERM, lde_sig_handler, NULL);
signal_add(&ev_sigint, NULL);
signal_add(&ev_sigterm, NULL);
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* setup pipe and event handler to the parent process */
if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
fatal(NULL);
imsg_init(&iev_main->ibuf, 3);
iev_main->handler = lde_dispatch_parent;
iev_main->events = EV_READ;
event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
iev_main->handler, iev_main);
event_add(&iev_main->ev, NULL);
/* setup and start the LIB garbage collector */
evtimer_set(&gc_timer, lde_gc_timer, NULL);
lde_gc_start_timer();
gettimeofday(&now, NULL);
global.uptime = now.tv_sec;
event_dispatch();
lde_shutdown();
}
static __dead void
lde_shutdown(void)
{
/* close pipes */
msgbuf_clear(&iev_ldpe->ibuf.w);
close(iev_ldpe->ibuf.fd);
msgbuf_clear(&iev_main->ibuf.w);
close(iev_main->ibuf.fd);
lde_gc_stop_timer();
lde_nbr_clear();
fec_tree_clear();
config_clear(ldeconf);
free(iev_ldpe);
free(iev_main);
log_info("label decision engine exiting");
exit(0);
}
/* imesg */
static int
lde_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
{
return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
}
int
lde_imsg_compose_ldpe(int type, uint32_t peerid, pid_t pid, void *data,
uint16_t datalen)
{
return (imsg_compose_event(iev_ldpe, type, peerid, pid,
-1, data, datalen));
}
static void
lde_dispatch_imsg(int fd, short event, void *bula)
{
struct imsgev *iev = bula;
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
struct lde_nbr *ln;
struct map map;
struct lde_addr lde_addr;
struct notify_msg nm;
ssize_t n;
int shut = 0, verbose;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
fatal("imsg_read error");
if (n == 0) /* connection closed */
shut = 1;
}
if (event & EV_WRITE) {
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
fatal("msgbuf_write");
if (n == 0) /* connection closed */
shut = 1;
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("lde_dispatch_imsg: imsg_get error");
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_LABEL_MAPPING_FULL:
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
log_debug("%s: cannot find lde neighbor",
__func__);
break;
}
fec_snap(ln);
break;
case IMSG_LABEL_MAPPING:
case IMSG_LABEL_REQUEST:
case IMSG_LABEL_RELEASE:
case IMSG_LABEL_WITHDRAW:
case IMSG_LABEL_ABORT:
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map))
fatalx("lde_dispatch_imsg: wrong imsg len");
memcpy(&map, imsg.data, sizeof(map));
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
log_debug("%s: cannot find lde neighbor",
__func__);
break;
}
switch (imsg.hdr.type) {
case IMSG_LABEL_MAPPING:
lde_check_mapping(&map, ln);
break;
case IMSG_LABEL_REQUEST:
lde_check_request(&map, ln);
break;
case IMSG_LABEL_RELEASE:
lde_check_release(&map, ln);
break;
case IMSG_LABEL_WITHDRAW:
lde_check_withdraw(&map, ln);
break;
case IMSG_LABEL_ABORT:
/* not necessary */
break;
}
break;
case IMSG_ADDRESS_ADD:
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr))
fatalx("lde_dispatch_imsg: wrong imsg len");
memcpy(&lde_addr, imsg.data, sizeof(lde_addr));
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
log_debug("%s: cannot find lde neighbor",
__func__);
break;
}
if (lde_address_add(ln, &lde_addr) < 0) {
log_debug("%s: cannot add address %s, it "
"already exists", __func__,
log_addr(lde_addr.af, &lde_addr.addr));
}
break;
case IMSG_ADDRESS_DEL:
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr))
fatalx("lde_dispatch_imsg: wrong imsg len");
memcpy(&lde_addr, imsg.data, sizeof(lde_addr));
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
log_debug("%s: cannot find lde neighbor",
__func__);
break;
}
if (lde_address_del(ln, &lde_addr) < 0) {
log_debug("%s: cannot delete address %s, it "
"does not exist", __func__,
log_addr(lde_addr.af, &lde_addr.addr));
}
break;
case IMSG_NOTIFICATION:
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm))
fatalx("lde_dispatch_imsg: wrong imsg len");
memcpy(&nm, imsg.data, sizeof(nm));
ln = lde_nbr_find(imsg.hdr.peerid);
if (ln == NULL) {
log_debug("%s: cannot find lde neighbor",
__func__);
break;
}
switch (nm.status_code) {
case S_PW_STATUS:
l2vpn_recv_pw_status(ln, &nm);
break;
case S_ENDOFLIB:
/*
* Do nothing for now. Should be useful in
* the future when we implement LDP-IGP
* Synchronization (RFC 5443) and Graceful
* Restart (RFC 3478).
*/
default:
break;
}
break;
case IMSG_NEIGHBOR_UP:
if (imsg.hdr.len - IMSG_HEADER_SIZE !=
sizeof(struct lde_nbr))
fatalx("lde_dispatch_imsg: wrong imsg len");
if (lde_nbr_find(imsg.hdr.peerid))
fatalx("lde_dispatch_imsg: "
"neighbor already exists");
lde_nbr_new(imsg.hdr.peerid, imsg.data);
break;
case IMSG_NEIGHBOR_DOWN:
lde_nbr_del(lde_nbr_find(imsg.hdr.peerid));
break;
case IMSG_CTL_SHOW_LIB:
rt_dump(imsg.hdr.pid);
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));
log_verbose(verbose);
break;
default:
log_debug("%s: unexpected imsg %d", __func__,
imsg.hdr.type);
break;
}
imsg_free(&imsg);
}
if (!shut)
imsg_event_add(iev);
else {
/* this pipe is dead, so remove the event handler */
event_del(&iev->ev);
event_loopexit(NULL);
}
}
static void
lde_dispatch_parent(int fd, short event, void *bula)
{
static struct ldpd_conf *nconf;
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 && errno != EAGAIN)
fatal("imsg_read error");
if (n == 0) /* connection closed */
shut = 1;
}
if (event & EV_WRITE) {
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
fatal("msgbuf_write");
if (n == 0) /* connection closed */
shut = 1;
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("lde_dispatch_parent: imsg_get error");
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_NETWORK_ADD:
case IMSG_NETWORK_DEL:
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) {
log_warnx("%s: wrong imsg len", __func__);
break;
}
memcpy(&kr, imsg.data, sizeof(kr));
switch (kr.af) {
case AF_INET:
fec.type = FEC_TYPE_IPV4;
fec.u.ipv4.prefix = kr.prefix.v4;
fec.u.ipv4.prefixlen = kr.prefixlen;
break;
case AF_INET6:
fec.type = FEC_TYPE_IPV6;
fec.u.ipv6.prefix = kr.prefix.v6;
fec.u.ipv6.prefixlen = kr.prefixlen;
break;
default:
fatalx("lde_dispatch_parent: unknown af");
}
switch (imsg.hdr.type) {
case IMSG_NETWORK_ADD:
lde_kernel_insert(&fec, kr.af, &kr.nexthop,
kr.priority, kr.flags & F_CONNECTED, NULL);
break;
case IMSG_NETWORK_DEL:
lde_kernel_remove(&fec, kr.af, &kr.nexthop,
kr.priority);
break;
}
break;
case IMSG_SOCKET_IPC:
if (iev_ldpe) {
log_warnx("%s: received unexpected imsg fd "
"to ldpe", __func__);
break;
}
if ((fd = imsg_get_fd(&imsg)) == -1) {
log_warnx("%s: expected to receive imsg fd to "
"ldpe but didn't receive any", __func__);
break;
}
if ((iev_ldpe = malloc(sizeof(struct imsgev))) == NULL)
fatal(NULL);
imsg_init(&iev_ldpe->ibuf, fd);
iev_ldpe->handler = lde_dispatch_imsg;
iev_ldpe->events = EV_READ;
event_set(&iev_ldpe->ev, iev_ldpe->ibuf.fd,
iev_ldpe->events, iev_ldpe->handler, iev_ldpe);
event_add(&iev_ldpe->ev, NULL);
break;
case IMSG_RECONF_CONF:
if ((nconf = malloc(sizeof(struct ldpd_conf))) ==
NULL)
fatal(NULL);
memcpy(nconf, imsg.data, sizeof(struct ldpd_conf));
LIST_INIT(&nconf->iface_list);
LIST_INIT(&nconf->tnbr_list);
LIST_INIT(&nconf->nbrp_list);
LIST_INIT(&nconf->l2vpn_list);
LIST_INIT(&nconf->auth_list);
break;
case IMSG_RECONF_IFACE:
if ((niface = malloc(sizeof(struct iface))) == NULL)
fatal(NULL);
memcpy(niface, imsg.data, sizeof(struct iface));
LIST_INIT(&niface->addr_list);
LIST_INIT(&niface->ipv4.adj_list);
LIST_INIT(&niface->ipv6.adj_list);
niface->ipv4.iface = niface;
niface->ipv6.iface = niface;
LIST_INSERT_HEAD(&nconf->iface_list, niface, entry);
break;
case IMSG_RECONF_TNBR:
if ((ntnbr = malloc(sizeof(struct tnbr))) == NULL)
fatal(NULL);
memcpy(ntnbr, imsg.data, sizeof(struct tnbr));
LIST_INSERT_HEAD(&nconf->tnbr_list, ntnbr, entry);
break;
case IMSG_RECONF_NBRP:
if ((nnbrp = malloc(sizeof(struct nbr_params))) == NULL)
fatal(NULL);
memcpy(nnbrp, imsg.data, sizeof(struct nbr_params));
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_CONF_AUTH: {
struct ldp_auth *auth;
auth = malloc(sizeof(*auth));
if (auth == NULL)
fatal(NULL);
memcpy(auth, imsg.data, sizeof(*auth));
LIST_INSERT_HEAD(&nconf->auth_list, auth, entry);
break;
}
case IMSG_RECONF_END:
merge_config(ldeconf, nconf);
nconf = NULL;
break;
default:
log_debug("%s: unexpected imsg %d", __func__,
imsg.hdr.type);
break;
}
imsg_free(&imsg);
}
if (!shut)
imsg_event_add(iev);
else {
/* this pipe is dead, so remove the event handler */
event_del(&iev->ev);
event_loopexit(NULL);
}
}
uint32_t
lde_assign_label(void)
{
static uint32_t label = MPLS_LABEL_RESERVED_MAX;
/* XXX some checks needed */
label++;
return (label);
}
void
lde_send_change_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:
memset(&kr, 0, sizeof(kr));
kr.af = AF_INET;
kr.prefix.v4 = fn->fec.u.ipv4.prefix;
kr.prefixlen = fn->fec.u.ipv4.prefixlen;
kr.nexthop.v4 = fnh->nexthop.v4;
kr.local_label = fn->local_label;
kr.remote_label = fnh->remote_label;
kr.priority = fnh->priority;
lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr,
sizeof(kr));
if (fn->fec.u.ipv4.prefixlen == 32)
l2vpn_sync_pws(AF_INET, (union ldpd_addr *)
&fn->fec.u.ipv4.prefix);
break;
case FEC_TYPE_IPV6:
memset(&kr, 0, sizeof(kr));
kr.af = AF_INET6;
kr.prefix.v6 = fn->fec.u.ipv6.prefix;
kr.prefixlen = fn->fec.u.ipv6.prefixlen;
kr.nexthop.v6 = fnh->nexthop.v6;
kr.local_label = fn->local_label;
kr.remote_label = fnh->remote_label;
kr.priority = fnh->priority;
lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr,
sizeof(kr));
if (fn->fec.u.ipv6.prefixlen == 128)
l2vpn_sync_pws(AF_INET6, (union ldpd_addr *)
&fn->fec.u.ipv6.prefix);
break;
case FEC_TYPE_PWID:
if (fn->local_label == NO_LABEL ||
fnh->remote_label == NO_LABEL)
return;
pw = (struct l2vpn_pw *) fn->data;
pw->flags |= F_PW_STATUS_UP;
memset(&kpw, 0, sizeof(kpw));
kpw.ifindex = pw->ifindex;
kpw.pw_type = fn->fec.u.pwid.type;
kpw.af = pw->af;
kpw.nexthop = pw->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:
memset(&kr, 0, sizeof(kr));
kr.af = AF_INET;
kr.prefix.v4 = fn->fec.u.ipv4.prefix;
kr.prefixlen = fn->fec.u.ipv4.prefixlen;
kr.nexthop.v4 = fnh->nexthop.v4;
kr.local_label = fn->local_label;
kr.remote_label = fnh->remote_label;
kr.priority = fnh->priority;
lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr,
sizeof(kr));
if (fn->fec.u.ipv4.prefixlen == 32)
l2vpn_sync_pws(AF_INET, (union ldpd_addr *)
&fn->fec.u.ipv4.prefix);
break;
case FEC_TYPE_IPV6:
memset(&kr, 0, sizeof(kr));
kr.af = AF_INET6;
kr.prefix.v6 = fn->fec.u.ipv6.prefix;
kr.prefixlen = fn->fec.u.ipv6.prefixlen;
kr.nexthop.v6 = fnh->nexthop.v6;
kr.local_label = fn->local_label;
kr.remote_label = fnh->remote_label;
kr.priority = fnh->priority;
lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr,
sizeof(kr));
if (fn->fec.u.ipv6.prefixlen == 128)
l2vpn_sync_pws(AF_INET6, (union ldpd_addr *)
&fn->fec.u.ipv6.prefix);
break;
case FEC_TYPE_PWID:
pw = (struct l2vpn_pw *) fn->data;
if (!(pw->flags & F_PW_STATUS_UP))
return;
pw->flags &= ~F_PW_STATUS_UP;
memset(&kpw, 0, sizeof(kpw));
kpw.ifindex = pw->ifindex;
kpw.pw_type = fn->fec.u.pwid.type;
kpw.af = pw->af;
kpw.nexthop = pw->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)
{
memset(map, 0, sizeof(*map));
switch (fec->type) {
case FEC_TYPE_IPV4:
map->type = MAP_TYPE_PREFIX;
map->fec.prefix.af = AF_INET;
map->fec.prefix.prefix.v4 = fec->u.ipv4.prefix;
map->fec.prefix.prefixlen = fec->u.ipv4.prefixlen;
break;
case FEC_TYPE_IPV6:
map->type = MAP_TYPE_PREFIX;
map->fec.prefix.af = AF_INET6;
map->fec.prefix.prefix.v6 = fec->u.ipv6.prefix;
map->fec.prefix.prefixlen = fec->u.ipv6.prefixlen;
break;
case FEC_TYPE_PWID:
map->type = MAP_TYPE_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;
}
}
void
lde_map2fec(struct map *map, struct in_addr lsr_id, struct fec *fec)
{
memset(fec, 0, sizeof(*fec));
switch (map->type) {
case MAP_TYPE_PREFIX:
switch (map->fec.prefix.af) {
case AF_INET:
fec->type = FEC_TYPE_IPV4;
fec->u.ipv4.prefix = map->fec.prefix.prefix.v4;
fec->u.ipv4.prefixlen = map->fec.prefix.prefixlen;
break;
case AF_INET6:
fec->type = FEC_TYPE_IPV6;
fec->u.ipv6.prefix = map->fec.prefix.prefix.v6;
fec->u.ipv6.prefixlen = map->fec.prefix.prefixlen;
break;
default:
fatalx("lde_map2fec: unknown af");
break;
}
break;
case MAP_TYPE_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.lsr_id = lsr_id;
break;
}
}
void
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 l2vpn_pw *pw;
/*
* This function skips SL.1 - 3 and SL.9 - 14 because the label
* allocation is done way earlier (because of the merging nature of
* ldpd).
*/
lde_fec2map(&fn->fec, &map);
switch (fn->fec.type) {
case FEC_TYPE_IPV4:
if (!ln->v4_enabled)
return;
break;
case FEC_TYPE_IPV6:
if (!ln->v6_enabled)
return;
break;
case FEC_TYPE_PWID:
pw = (struct l2vpn_pw *) fn->data;
if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
/* not the remote end of the pseudowire */
return;
map.flags |= F_MAP_PW_IFMTU;
map.fec.pwid.ifmtu = pw->l2vpn->mtu;
if (pw->flags & F_PW_CWORD)
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;
}
break;
}
map.label = fn->local_label;
/* SL.6: is there a pending request for this mapping? */
lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec);
if (lre) {
/* set label request msg id in the mapping response. */
map.requestid = lre->msg_id;
map.flags = F_MAP_REQ_ID;
/* SL.7: delete record of pending request */
lde_req_del(ln, lre, 0);
}
/* 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);
if (me == NULL)
me = lde_map_add(ln, fn, 1);
me->map = map;
}
void
lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn,
struct map *wcard, struct status_tlv *st)
{
struct lde_wdraw *lw;
struct map map;
struct fec *f;
struct l2vpn_pw *pw;
if (fn) {
lde_fec2map(&fn->fec, &map);
switch (fn->fec.type) {
case FEC_TYPE_IPV4:
if (!ln->v4_enabled)
return;
break;
case FEC_TYPE_IPV6:
if (!ln->v6_enabled)
return;
break;
case FEC_TYPE_PWID:
pw = (struct l2vpn_pw *) fn->data;
if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
/* not the remote end of the pseudowire */
return;
if (pw->flags & F_PW_CWORD)
map.flags |= F_MAP_PW_CWORD;
break;
}
map.label = fn->local_label;
} else
memcpy(&map, wcard, sizeof(map));
if (st) {
map.st.status_code = st->status_code;
map.st.msg_id = st->msg_id;
map.st.msg_type = st->msg_type;
map.flags |= F_MAP_STATUS;
}
/* SWd.1: send label withdraw. */
lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD, ln->peerid, 0,
&map, sizeof(map));
lde_imsg_compose_ldpe(IMSG_WITHDRAW_ADD_END, ln->peerid, 0, NULL, 0);
/* SWd.2: record label withdraw. */
if (fn) {
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
if (lw == NULL)
lw = lde_wdraw_add(ln, fn);
lw->label = map.label;
} else {
struct lde_map *me;
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
if (lde_wildcard_apply(wcard, &fn->fec, me) == 0)
continue;
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw,
&fn->fec);
if (lw == NULL)
lw = lde_wdraw_add(ln, fn);
lw->label = map.label;
}
}
}
void
lde_send_labelwithdraw_wcard(struct lde_nbr *ln, uint32_t label)
{
struct map wcard;
memset(&wcard, 0, sizeof(wcard));
wcard.type = MAP_TYPE_WILDCARD;
wcard.label = label;
lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
}
void
lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *ln, uint16_t af,
uint32_t label)
{
struct map wcard;
memset(&wcard, 0, sizeof(wcard));
wcard.type = MAP_TYPE_TYPED_WCARD;
wcard.fec.twcard.type = MAP_TYPE_PREFIX;
wcard.fec.twcard.u.prefix_af = af;
wcard.label = label;
lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
}
void
lde_send_labelwithdraw_twcard_pwid(struct lde_nbr *ln, uint16_t pw_type,
uint32_t label)
{
struct map wcard;
memset(&wcard, 0, sizeof(wcard));
wcard.type = MAP_TYPE_TYPED_WCARD;
wcard.fec.twcard.type = MAP_TYPE_PWID;
wcard.fec.twcard.u.pw_type = pw_type;
wcard.label = label;
lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
}
void
lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *ln, uint16_t pw_type,
uint32_t group_id)
{
struct map wcard;
memset(&wcard, 0, sizeof(wcard));
wcard.type = MAP_TYPE_PWID;
wcard.fec.pwid.type = pw_type;
wcard.fec.pwid.group_id = group_id;
/* we can not append a Label TLV when using PWid group wildcards. */
wcard.label = NO_LABEL;
lde_send_labelwithdraw(ln, NULL, &wcard, NULL);
}
void
lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn,
struct map *wcard, uint32_t label)
{
struct map map;
struct l2vpn_pw *pw;
if (fn) {
lde_fec2map(&fn->fec, &map);
switch (fn->fec.type) {
case FEC_TYPE_IPV4:
if (!ln->v4_enabled)
return;
break;
case FEC_TYPE_IPV6:
if (!ln->v6_enabled)
return;
break;
case FEC_TYPE_PWID:
pw = (struct l2vpn_pw *) fn->data;
if (pw == NULL || pw->lsr_id.s_addr != ln->id.s_addr)
/* not the remote end of the pseudowire */
return;
if (pw->flags & F_PW_CWORD)
map.flags |= F_MAP_PW_CWORD;
break;
}
} else
memcpy(&map, wcard, sizeof(map));
map.label = label;
lde_imsg_compose_ldpe(IMSG_RELEASE_ADD, ln->peerid, 0,
&map, sizeof(map));
lde_imsg_compose_ldpe(IMSG_RELEASE_ADD_END, ln->peerid, 0, NULL, 0);
}
void
lde_send_notification(struct lde_nbr *ln, uint32_t status_code, uint32_t msg_id,
uint16_t msg_type)
{
struct notify_msg nm;
memset(&nm, 0, sizeof(nm));
nm.status_code = status_code;
/* 'msg_id' and 'msg_type' should be in network byte order */
nm.msg_id = msg_id;
nm.msg_type = msg_type;
lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
&nm, sizeof(nm));
}
void
lde_send_notification_eol_prefix(struct lde_nbr *ln, int af)
{
struct notify_msg nm;
memset(&nm, 0, sizeof(nm));
nm.status_code = S_ENDOFLIB;
nm.fec.type = MAP_TYPE_TYPED_WCARD;
nm.fec.fec.twcard.type = MAP_TYPE_PREFIX;
nm.fec.fec.twcard.u.prefix_af = af;
nm.flags |= F_NOTIF_FEC;
lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
&nm, sizeof(nm));
}
void
lde_send_notification_eol_pwid(struct lde_nbr *ln, uint16_t pw_type)
{
struct notify_msg nm;
memset(&nm, 0, sizeof(nm));
nm.status_code = S_ENDOFLIB;
nm.fec.type = MAP_TYPE_TYPED_WCARD;
nm.fec.fec.twcard.type = MAP_TYPE_PWID;
nm.fec.fec.twcard.u.pw_type = pw_type;
nm.flags |= F_NOTIF_FEC;
lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0,
&nm, sizeof(nm));
}
static __inline int
lde_nbr_compare(struct lde_nbr *a, struct lde_nbr *b)
{
return (a->peerid - b->peerid);
}
static struct lde_nbr *
lde_nbr_new(uint32_t peerid, struct lde_nbr *new)
{
struct lde_nbr *ln;
if ((ln = calloc(1, sizeof(*ln))) == NULL)
fatal(__func__);
ln->id = new->id;
ln->v4_enabled = new->v4_enabled;
ln->v6_enabled = new->v6_enabled;
ln->flags = new->flags;
ln->peerid = peerid;
fec_init(&ln->recv_map);
fec_init(&ln->sent_map);
fec_init(&ln->recv_req);
fec_init(&ln->sent_req);
fec_init(&ln->sent_wdraw);
TAILQ_INIT(&ln->addr_list);
if (RB_INSERT(nbr_tree, &lde_nbrs, ln) != NULL)
fatalx("lde_nbr_new: RB_INSERT failed");
return (ln);
}
static void
lde_nbr_del(struct lde_nbr *ln)
{
struct fec *f;
struct fec_node *fn;
struct fec_nh *fnh;
struct l2vpn_pw *pw;
if (ln == NULL)
return;
/* uninstall received mappings */
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
LIST_FOREACH(fnh, &fn->nexthops, entry) {
switch (f->type) {
case FEC_TYPE_IPV4:
case FEC_TYPE_IPV6:
if (!lde_address_find(ln, fnh->af,
&fnh->nexthop))
continue;
break;
case FEC_TYPE_PWID:
if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr)
continue;
pw = (struct l2vpn_pw *) fn->data;
if (pw)
l2vpn_pw_reset(pw);
break;
default:
break;
}
lde_send_delete_klabel(fn, fnh);
fnh->remote_label = NO_LABEL;
}
}
lde_address_list_free(ln);
fec_clear(&ln->recv_map, lde_map_free);
fec_clear(&ln->sent_map, lde_map_free);
fec_clear(&ln->recv_req, free);
fec_clear(&ln->sent_req, free);
fec_clear(&ln->sent_wdraw, free);
RB_REMOVE(nbr_tree, &lde_nbrs, ln);
free(ln);
}
static struct lde_nbr *
lde_nbr_find(uint32_t peerid)
{
struct lde_nbr ln;
ln.peerid = peerid;
return (RB_FIND(nbr_tree, &lde_nbrs, &ln));
}
struct lde_nbr *
lde_nbr_find_by_lsrid(struct in_addr addr)
{
struct lde_nbr *ln;
RB_FOREACH(ln, nbr_tree, &lde_nbrs)
if (ln->id.s_addr == addr.s_addr)
return (ln);
return (NULL);
}
struct lde_nbr *
lde_nbr_find_by_addr(int af, union ldpd_addr *addr)
{
struct lde_nbr *ln;
RB_FOREACH(ln, nbr_tree, &lde_nbrs)
if (lde_address_find(ln, af, addr) != NULL)
return (ln);
return (NULL);
}
static void
lde_nbr_clear(void)
{
struct lde_nbr *ln;
while ((ln = RB_ROOT(&lde_nbrs)) != NULL)
lde_nbr_del(ln);
}
static void
lde_nbr_addr_update(struct lde_nbr *ln, struct lde_addr *lde_addr, int removed)
{
struct fec *fec;
struct fec_node *fn;
struct fec_nh *fnh;
struct lde_map *me;
RB_FOREACH(fec, fec_tree, &ln->recv_map) {
fn = (struct fec_node *)fec_find(&ft, fec);
switch (fec->type) {
case FEC_TYPE_IPV4:
if (lde_addr->af != AF_INET)
continue;
break;
case FEC_TYPE_IPV6:
if (lde_addr->af != AF_INET6)
continue;
break;
default:
continue;
}
LIST_FOREACH(fnh, &fn->nexthops, entry) {
if (ldp_addrcmp(fnh->af, &fnh->nexthop,
&lde_addr->addr))
continue;
if (removed) {
lde_send_delete_klabel(fn, fnh);
fnh->remote_label = NO_LABEL;
} else {
me = (struct lde_map *)fec;
fnh->remote_label = me->map.label;
lde_send_change_klabel(fn, fnh);
}
break;
}
}
}
struct lde_map *
lde_map_add(struct lde_nbr *ln, struct fec_node *fn, int sent)
{
struct lde_map *me;
me = calloc(1, sizeof(*me));
if (me == NULL)
fatal(__func__);
me->fec = fn->fec;
me->nexthop = ln;
if (sent) {
LIST_INSERT_HEAD(&fn->upstream, me, entry);
if (fec_insert(&ln->sent_map, &me->fec))
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 to recv map",
log_fec(&me->fec));
}
return (me);
}
void
lde_map_del(struct lde_nbr *ln, struct lde_map *me, int sent)
{
if (sent)
fec_remove(&ln->sent_map, &me->fec);
else
fec_remove(&ln->recv_map, &me->fec);
lde_map_free(me);
}
static void
lde_map_free(void *ptr)
{
struct lde_map *map = ptr;
LIST_REMOVE(map, entry);
free(map);
}
struct lde_req *
lde_req_add(struct lde_nbr *ln, struct fec *fec, int sent)
{
struct fec_tree *t;
struct lde_req *lre;
t = sent ? &ln->sent_req : &ln->recv_req;
lre = calloc(1, sizeof(*lre));
if (lre != NULL) {
lre->fec = *fec;
if (fec_insert(t, &lre->fec)) {
log_warnx("failed to add %s to %s req",
log_fec(&lre->fec), sent ? "sent" : "recv");
free(lre);
return (NULL);
}
}
return (lre);
}
void
lde_req_del(struct lde_nbr *ln, struct lde_req *lre, int sent)
{
if (sent)
fec_remove(&ln->sent_req, &lre->fec);
else
fec_remove(&ln->recv_req, &lre->fec);
free(lre);
}
struct lde_wdraw *
lde_wdraw_add(struct lde_nbr *ln, struct fec_node *fn)
{
struct lde_wdraw *lw;
lw = calloc(1, sizeof(*lw));
if (lw == NULL)
fatal(__func__);
lw->fec = fn->fec;
if (fec_insert(&ln->sent_wdraw, &lw->fec))
log_warnx("failed to add %s to sent wdraw",
log_fec(&lw->fec));
return (lw);
}
void
lde_wdraw_del(struct lde_nbr *ln, struct lde_wdraw *lw)
{
fec_remove(&ln->sent_wdraw, &lw->fec);
free(lw);
}
void
lde_change_egress_label(int af, int was_implicit)
{
struct lde_nbr *ln;
struct fec *f;
struct fec_node *fn;
RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
/* explicit withdraw */
if (was_implicit)
lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLNULL);
else {
if (ln->v4_enabled)
lde_send_labelwithdraw_wcard(ln,
MPLS_LABEL_IPV4NULL);
if (ln->v6_enabled)
lde_send_labelwithdraw_wcard(ln,
MPLS_LABEL_IPV6NULL);
}
/* advertise new label of connected prefixes */
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
if (fn->local_label > MPLS_LABEL_RESERVED_MAX)
continue;
switch (af) {
case AF_INET:
if (fn->fec.type != FEC_TYPE_IPV4)
continue;
break;
case AF_INET6:
if (fn->fec.type != FEC_TYPE_IPV6)
continue;
break;
default:
fatalx("lde_change_egress_label: unknown af");
}
fn->local_label = egress_label(fn->fec.type);
lde_send_labelmapping(ln, fn, 0);
}
lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0,
NULL, 0);
}
}
static int
lde_address_add(struct lde_nbr *ln, struct lde_addr *lde_addr)
{
struct lde_addr *new;
if (lde_address_find(ln, lde_addr->af, &lde_addr->addr) != NULL)
return (-1);
if ((new = calloc(1, sizeof(*new))) == NULL)
fatal(__func__);
new->af = lde_addr->af;
new->addr = lde_addr->addr;
TAILQ_INSERT_TAIL(&ln->addr_list, new, entry);
/* reevaluate the previously received mappings from this neighbor */
lde_nbr_addr_update(ln, lde_addr, 0);
return (0);
}
static int
lde_address_del(struct lde_nbr *ln, struct lde_addr *lde_addr)
{
lde_addr = lde_address_find(ln, lde_addr->af, &lde_addr->addr);
if (lde_addr == NULL)
return (-1);
/* reevaluate the previously received mappings from this neighbor */
lde_nbr_addr_update(ln, lde_addr, 1);
TAILQ_REMOVE(&ln->addr_list, lde_addr, entry);
free(lde_addr);
return (0);
}
struct lde_addr *
lde_address_find(struct lde_nbr *ln, int af, union ldpd_addr *addr)
{
struct lde_addr *lde_addr;
TAILQ_FOREACH(lde_addr, &ln->addr_list, entry)
if (lde_addr->af == af &&
ldp_addrcmp(af, &lde_addr->addr, addr) == 0)
return (lde_addr);
return (NULL);
}
static void
lde_address_list_free(struct lde_nbr *ln)
{
struct lde_addr *lde_addr;
while ((lde_addr = TAILQ_FIRST(&ln->addr_list)) != NULL) {
TAILQ_REMOVE(&ln->addr_list, lde_addr, entry);
free(lde_addr);
}
}