#include <sys/types.h>
#include <sys/socket.h>
#include <netmpls/mpls.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "ldpd.h"
#include "lde.h"
#include "ldpe.h"
#include "log.h"
static __inline int fec_compare(struct fec *, struct fec *);
static int lde_nbr_is_nexthop(struct fec_node *,
struct lde_nbr *);
static void fec_free(void *);
static struct fec_node *fec_add(struct fec *fec);
static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *,
uint8_t priority);
static void fec_nh_del(struct fec_nh *);
RB_GENERATE(fec_tree, fec, entry, fec_compare)
struct fec_tree ft = RB_INITIALIZER(&ft);
struct event gc_timer;
void
fec_init(struct fec_tree *fh)
{
RB_INIT(fh);
}
static __inline int
fec_compare(struct fec *a, struct fec *b)
{
if (a->type < b->type)
return (-1);
if (a->type > b->type)
return (1);
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);
case FEC_TYPE_IPV6:
if (memcmp(&a->u.ipv6.prefix, &b->u.ipv6.prefix,
sizeof(struct in6_addr)) < 0)
return (-1);
if (memcmp(&a->u.ipv6.prefix, &b->u.ipv6.prefix,
sizeof(struct in6_addr)) > 0)
return (1);
if (a->u.ipv6.prefixlen < b->u.ipv6.prefixlen)
return (-1);
if (a->u.ipv6.prefixlen > b->u.ipv6.prefixlen)
return (1);
return (0);
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.lsr_id.s_addr) <
ntohl(b->u.pwid.lsr_id.s_addr))
return (-1);
if (ntohl(a->u.pwid.lsr_id.s_addr) >
ntohl(b->u.pwid.lsr_id.s_addr))
return (1);
return (0);
}
return (-1);
}
struct fec *
fec_find(struct fec_tree *fh, struct fec *f)
{
return (RB_FIND(fec_tree, fh, f));
}
int
fec_insert(struct fec_tree *fh, struct fec *f)
{
if (RB_INSERT(fec_tree, fh, f) != NULL)
return (-1);
return (0);
}
int
fec_remove(struct fec_tree *fh, struct fec *f)
{
if (RB_REMOVE(fec_tree, fh, f) == NULL) {
log_warnx("%s failed for %s", __func__, log_fec(f));
return (-1);
}
return (0);
}
void
fec_clear(struct fec_tree *fh, void (*free_cb)(void *))
{
struct fec *f;
while ((f = RB_ROOT(fh)) != NULL) {
fec_remove(fh, f);
free_cb(f);
}
}
static int
lde_nbr_is_nexthop(struct fec_node *fn, struct lde_nbr *ln)
{
struct fec_nh *fnh;
LIST_FOREACH(fnh, &fn->nexthops, entry)
if (lde_address_find(ln, fnh->af, &fnh->nexthop))
return (1);
return (0);
}
void
rt_dump(pid_t pid)
{
struct fec *f;
struct fec_node *fn;
struct lde_map *me;
static struct ctl_rt rtctl;
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
if (fn->local_label == NO_LABEL &&
LIST_EMPTY(&fn->downstream))
continue;
switch (fn->fec.type) {
case FEC_TYPE_IPV4:
rtctl.af = AF_INET;
rtctl.prefix.v4 = fn->fec.u.ipv4.prefix;
rtctl.prefixlen = fn->fec.u.ipv4.prefixlen;
break;
case FEC_TYPE_IPV6:
rtctl.af = AF_INET6;
rtctl.prefix.v6 = fn->fec.u.ipv6.prefix;
rtctl.prefixlen = fn->fec.u.ipv6.prefixlen;
break;
default:
continue;
}
rtctl.local_label = fn->local_label;
LIST_FOREACH(me, &fn->downstream, entry) {
rtctl.in_use = lde_nbr_is_nexthop(fn, me->nexthop);
rtctl.nexthop = me->nexthop->id;
rtctl.remote_label = me->map.label;
lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid,
&rtctl, sizeof(rtctl));
}
if (LIST_EMPTY(&fn->downstream)) {
rtctl.in_use = 0;
rtctl.nexthop.s_addr = INADDR_ANY;
rtctl.remote_label = NO_LABEL;
lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid,
&rtctl, sizeof(rtctl));
}
}
}
void
fec_snap(struct lde_nbr *ln)
{
struct fec *f;
struct fec_node *fn;
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
if (fn->local_label == NO_LABEL)
continue;
lde_send_labelmapping(ln, fn, 0);
}
lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0);
if (ln->flags & F_NBR_CAP_UNOTIF) {
lde_send_notification_eol_prefix(ln, AF_INET);
lde_send_notification_eol_prefix(ln, AF_INET6);
lde_send_notification_eol_pwid(ln, PW_TYPE_WILDCARD);
}
}
static void
fec_free(void *arg)
{
struct fec_node *fn = arg;
struct fec_nh *fnh;
while ((fnh = LIST_FIRST(&fn->nexthops)))
fec_nh_del(fnh);
if (!LIST_EMPTY(&fn->downstream))
log_warnx("%s: fec %s downstream list not empty", __func__,
log_fec(&fn->fec));
if (!LIST_EMPTY(&fn->upstream))
log_warnx("%s: fec %s upstream list not empty", __func__,
log_fec(&fn->fec));
free(fn);
}
void
fec_tree_clear(void)
{
fec_clear(&ft, fec_free);
}
static struct fec_node *
fec_add(struct fec *fec)
{
struct fec_node *fn;
fn = calloc(1, sizeof(*fn));
if (fn == NULL)
fatal(__func__);
fn->fec = *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 to ft tree",
log_fec(&fn->fec));
return (fn);
}
struct fec_nh *
fec_nh_find(struct fec_node *fn, int af, union ldpd_addr *nexthop,
uint8_t priority)
{
struct fec_nh *fnh;
LIST_FOREACH(fnh, &fn->nexthops, entry)
if (fnh->af == af &&
ldp_addrcmp(af, &fnh->nexthop, nexthop) == 0 &&
fnh->priority == priority)
return (fnh);
return (NULL);
}
static struct fec_nh *
fec_nh_add(struct fec_node *fn, int af, union ldpd_addr *nexthop,
uint8_t priority)
{
struct fec_nh *fnh;
fnh = calloc(1, sizeof(*fnh));
if (fnh == NULL)
fatal(__func__);
fnh->af = af;
fnh->nexthop = *nexthop;
fnh->remote_label = NO_LABEL;
fnh->priority = priority;
LIST_INSERT_HEAD(&fn->nexthops, fnh, entry);
return (fnh);
}
static void
fec_nh_del(struct fec_nh *fnh)
{
LIST_REMOVE(fnh, entry);
free(fnh);
}
uint32_t
egress_label(enum fec_type fec_type)
{
switch (fec_type) {
case FEC_TYPE_IPV4:
if (ldeconf->ipv4.flags & F_LDPD_AF_EXPNULL)
return (MPLS_LABEL_IPV4NULL);
break;
case FEC_TYPE_IPV6:
if (ldeconf->ipv6.flags & F_LDPD_AF_EXPNULL)
return (MPLS_LABEL_IPV6NULL);
break;
default:
fatalx("egress_label: unexpected fec type");
}
return (MPLS_LABEL_IMPLNULL);
}
void
lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop,
uint8_t priority, int connected, void *data)
{
struct fec_node *fn;
struct fec_nh *fnh;
struct lde_map *me;
struct lde_nbr *ln;
fn = (struct fec_node *)fec_find(&ft, fec);
if (fn == NULL)
fn = fec_add(fec);
if (fec_nh_find(fn, af, nexthop, priority) != NULL)
return;
log_debug("lde add fec %s nexthop %s",
log_fec(&fn->fec), log_addr(af, nexthop));
if (fn->fec.type == FEC_TYPE_PWID)
fn->data = data;
if (fn->local_label == NO_LABEL) {
if (connected)
fn->local_label = egress_label(fn->fec.type);
else
fn->local_label = lde_assign_label();
RB_FOREACH(ln, nbr_tree, &lde_nbrs)
lde_send_labelmapping(ln, fn, 1);
}
fnh = fec_nh_add(fn, af, nexthop, priority);
lde_send_change_klabel(fn, fnh);
switch (fn->fec.type) {
case FEC_TYPE_IPV4:
case FEC_TYPE_IPV6:
ln = lde_nbr_find_by_addr(af, &fnh->nexthop);
break;
case FEC_TYPE_PWID:
ln = lde_nbr_find_by_lsrid(fn->fec.u.pwid.lsr_id);
break;
default:
ln = NULL;
break;
}
if (ln) {
me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
if (me)
lde_check_mapping(&me->map, ln);
}
}
void
lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop,
uint8_t priority)
{
struct fec_node *fn;
struct fec_nh *fnh;
struct lde_nbr *ln;
fn = (struct fec_node *)fec_find(&ft, fec);
if (fn == NULL)
return;
fnh = fec_nh_find(fn, af, nexthop, priority);
if (fnh == NULL)
return;
log_debug("lde remove fec %s nexthop %s",
log_fec(&fn->fec), log_addr(af, nexthop));
lde_send_delete_klabel(fn, fnh);
fec_nh_del(fnh);
if (LIST_EMPTY(&fn->nexthops)) {
RB_FOREACH(ln, nbr_tree, &lde_nbrs)
lde_send_labelwithdraw(ln, fn, NULL, NULL);
fn->local_label = NO_LABEL;
if (fn->fec.type == FEC_TYPE_PWID)
fn->data = NULL;
}
}
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_map *me;
struct l2vpn_pw *pw;
int msgsource = 0;
lde_map2fec(map, ln->id, &fec);
fn = (struct fec_node *)fec_find(&ft, &fec);
if (fn == NULL)
fn = fec_add(&fec);
lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec);
if (lre)
lde_req_del(ln, lre, 1);
if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map))
return;
me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
if (me) {
if (me->map.label != map->label && lre == NULL) {
lde_send_labelrelease(ln, fn, NULL, me->map.label);
LIST_FOREACH(fnh, &fn->nexthops, entry) {
if (lde_address_find(ln, fnh->af,
&fnh->nexthop) == NULL)
continue;
lde_send_delete_klabel(fn, fnh);
fnh->remote_label = NO_LABEL;
}
}
}
LIST_FOREACH(fnh, &fn->nexthops, entry) {
switch (fec.type) {
case FEC_TYPE_IPV4:
case FEC_TYPE_IPV6:
if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
continue;
fnh->remote_label = map->label;
lde_send_change_klabel(fn, fnh);
break;
case FEC_TYPE_PWID:
pw = (struct l2vpn_pw *) fn->data;
if (pw == NULL)
continue;
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;
fnh->remote_label = map->label;
if (l2vpn_pw_ok(pw, fnh))
lde_send_change_klabel(fn, fnh);
break;
default:
break;
}
msgsource = 1;
}
if (me == NULL)
me = lde_map_add(ln, fn, 0);
me->map = *map;
if (msgsource == 0)
return;
}
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;
if (map->type == MAP_TYPE_TYPED_WCARD) {
lde_check_request_wcard(map, ln);
return;
}
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, S_NO_ROUTE, map->msg_id,
htons(MSG_TYPE_LABELREQUEST));
return;
}
LIST_FOREACH(fnh, &fn->nexthops, entry) {
switch (fec.type) {
case FEC_TYPE_IPV4:
case FEC_TYPE_IPV6:
if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
continue;
lde_send_notification(ln, S_LOOP_DETECTED, map->msg_id,
htons(MSG_TYPE_LABELREQUEST));
return;
default:
break;
}
}
lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec);
if (lre != NULL)
return;
lre = lde_req_add(ln, &fn->fec, 0);
if (lre != NULL)
lre->msg_id = ntohl(map->msg_id);
lde_send_labelmapping(ln, fn, 1);
}
void
lde_check_request_wcard(struct map *map, struct lde_nbr *ln)
{
struct fec *f;
struct fec_node *fn;
struct lde_req *lre;
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
if (lde_wildcard_apply(map, &fn->fec, NULL) == 0)
continue;
if (LIST_EMPTY(&fn->nexthops))
continue;
lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec);
if (lre != NULL)
continue;
lre = lde_req_add(ln, &fn->fec, 0);
if (lre != NULL)
lre->msg_id = ntohl(map->msg_id);
lde_send_labelmapping(ln, fn, 1);
}
if (ln->flags & F_NBR_CAP_UNOTIF) {
switch (map->fec.twcard.type) {
case MAP_TYPE_PREFIX:
lde_send_notification_eol_prefix(ln,
map->fec.twcard.u.prefix_af);
break;
case MAP_TYPE_PWID:
lde_send_notification_eol_pwid(ln,
map->fec.twcard.u.pw_type);
break;
default:
break;
}
}
}
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;
if (map->type == MAP_TYPE_WILDCARD ||
map->type == MAP_TYPE_TYPED_WCARD ||
(map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) {
lde_check_release_wcard(map, ln);
return;
}
lde_map2fec(map, ln->id, &fec);
fn = (struct fec_node *)fec_find(&ft, &fec);
if (fn == NULL)
return;
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
if (lw && (map->label == NO_LABEL || map->label == lw->label)) {
lde_wdraw_del(ln, lw);
}
me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec);
if (me && (map->label == NO_LABEL || map->label == me->map.label))
lde_map_del(ln, me, 1);
}
void
lde_check_release_wcard(struct map *map, struct lde_nbr *ln)
{
struct fec *f;
struct fec_node *fn;
struct lde_wdraw *lw;
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(map, &fn->fec, me) == 0)
continue;
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
if (lw && (map->label == NO_LABEL || map->label == lw->label)) {
lde_wdraw_del(ln, lw);
}
if (me &&
(map->label == NO_LABEL || map->label == me->map.label))
lde_map_del(ln, me, 1);
}
}
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;
struct l2vpn_pw *pw;
if (map->type == MAP_TYPE_WILDCARD ||
map->type == MAP_TYPE_TYPED_WCARD ||
(map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) {
lde_check_withdraw_wcard(map, ln);
return;
}
lde_map2fec(map, ln->id, &fec);
fn = (struct fec_node *)fec_find(&ft, &fec);
if (fn == NULL)
fn = fec_add(&fec);
LIST_FOREACH(fnh, &fn->nexthops, entry) {
switch (fec.type) {
case FEC_TYPE_IPV4:
case FEC_TYPE_IPV6:
if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
continue;
break;
case FEC_TYPE_PWID:
pw = (struct l2vpn_pw *) fn->data;
if (pw == NULL)
continue;
break;
default:
break;
}
if (map->label != NO_LABEL && map->label != fnh->remote_label)
continue;
lde_send_delete_klabel(fn, fnh);
fnh->remote_label = NO_LABEL;
}
lde_send_labelrelease(ln, fn, NULL, map->label);
me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
if (me && (map->label == NO_LABEL || map->label == me->map.label))
lde_map_del(ln, me, 0);
}
void
lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
{
struct fec *f;
struct fec_node *fn;
struct fec_nh *fnh;
struct lde_map *me;
lde_send_labelrelease(ln, NULL, map, map->label);
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
if (lde_wildcard_apply(map, &fn->fec, me) == 0)
continue;
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;
break;
default:
break;
}
if (map->label != NO_LABEL && map->label !=
fnh->remote_label)
continue;
lde_send_delete_klabel(fn, fnh);
fnh->remote_label = NO_LABEL;
}
if (me && (map->label == NO_LABEL ||
map->label == me->map.label))
lde_map_del(ln, me, 0);
}
}
int
lde_wildcard_apply(struct map *wcard, struct fec *fec, struct lde_map *me)
{
switch (wcard->type) {
case MAP_TYPE_WILDCARD:
return (1);
case MAP_TYPE_TYPED_WCARD:
switch (wcard->fec.twcard.type) {
case MAP_TYPE_PREFIX:
if (wcard->fec.twcard.u.prefix_af == AF_INET &&
fec->type != FEC_TYPE_IPV4)
return (0);
if (wcard->fec.twcard.u.prefix_af == AF_INET6 &&
fec->type != FEC_TYPE_IPV6)
return (0);
return (1);
case MAP_TYPE_PWID:
if (fec->type != FEC_TYPE_PWID)
return (0);
if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD &&
wcard->fec.twcard.u.pw_type != fec->u.pwid.type)
return (0);
return (1);
default:
fatalx("lde_wildcard_apply: unexpected fec type");
}
break;
case MAP_TYPE_PWID:
if (fec->type != FEC_TYPE_PWID)
return (0);
if (fec->u.pwid.type != wcard->fec.pwid.type)
return (0);
if (me == NULL || (me->map.fec.pwid.group_id !=
wcard->fec.pwid.group_id))
return (0);
return (1);
default:
fatalx("lde_wildcard_apply: unexpected fec type");
}
}
void
lde_gc_timer(int fd, short event, void *arg)
{
struct fec *fec, *safe;
struct fec_node *fn;
int count = 0;
RB_FOREACH_SAFE(fec, fec_tree, &ft, safe) {
fn = (struct fec_node *) fec;
if (!LIST_EMPTY(&fn->nexthops) ||
!LIST_EMPTY(&fn->downstream) ||
!LIST_EMPTY(&fn->upstream))
continue;
fec_remove(&ft, &fn->fec);
free(fn);
count++;
}
if (count > 0)
log_debug("%s: %u entries removed", __func__, count);
lde_gc_start_timer();
}
void
lde_gc_start_timer(void)
{
struct timeval tv;
timerclear(&tv);
tv.tv_sec = LDE_GC_INTERVAL;
if (evtimer_add(&gc_timer, &tv) == -1)
fatal(__func__);
}
void
lde_gc_stop_timer(void)
{
if (evtimer_pending(&gc_timer, NULL) &&
evtimer_del(&gc_timer) == -1)
fatal(__func__);
}