OpenBSD-4.6/usr.sbin/ldpd/lde_lib.c

Compare this file to the similar file:
Show the results in this format:

/*	$OpenBSD: lde_lib.c,v 1.3 2009/06/19 17:10:09 michele Exp $ */

/*
 * Copyright (c) 2009 Michele Marchetto <michele@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/ioctl.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_types.h>
#include <netmpls/mpls.h>
#include <ctype.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <event.h>

#include "ldpd.h"
#include "ldp.h"
#include "log.h"
#include "lde.h"

extern struct ldpd_conf		*ldeconf;
RB_HEAD(rt_tree, rt_node)	 rt;
RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare)
RB_GENERATE(rt_tree, rt_node, entry, rt_compare)

u_int32_t	lde_assign_label(void);

/* route table */
void
rt_init(void)
{
	RB_INIT(&rt);
}

int
rt_compare(struct rt_node *a, struct rt_node *b)
{
	if (ntohl(a->prefix.s_addr) < ntohl(b->prefix.s_addr))
		return (-1);
	if (ntohl(a->prefix.s_addr) > ntohl(b->prefix.s_addr))
		return (1);
	if (a->prefixlen < b->prefixlen)
		return (-1);
	if (a->prefixlen > b->prefixlen)
		return (1);

	return (0);
}

struct rt_node *
rt_find(in_addr_t prefix, u_int8_t prefixlen)
{
	struct rt_node	 s;

	s.prefix.s_addr = prefix;
	s.prefixlen = prefixlen;

	return (RB_FIND(rt_tree, &rt, &s));
}

int
rt_insert(struct rt_node *r)
{
	if (RB_INSERT(rt_tree, &rt, r) != NULL) {
		log_warnx("rt_insert failed for %s/%u",
		    inet_ntoa(r->prefix), r->prefixlen);
		free(r);
		return (-1);
	}

	return (0);
}

int
rt_remove(struct rt_node *r)
{
	if (RB_REMOVE(rt_tree, &rt, r) == NULL) {
		log_warnx("rt_remove failed for %s/%u",
		    inet_ntoa(r->prefix), r->prefixlen);
		return (-1);
	}

	free(r);
	return (0);
}

void
rt_dump(pid_t pid)
{
	struct rt_node		*r;
	static struct ctl_rt	 rtctl;

	RB_FOREACH(r, rt_tree, &rt) {
		rtctl.prefix.s_addr = r->prefix.s_addr;
		rtctl.prefixlen = r->prefixlen;
		rtctl.nexthop.s_addr = r->nexthop.s_addr;
		rtctl.flags = r->flags;
		rtctl.local_label = r->local_label;
		rtctl.remote_label = r->remote_label;

		lde_imsg_compose_ldpe(IMSG_CTL_SHOW_LIB, 0, pid, &rtctl,
		    sizeof(rtctl));
	}
}

void
rt_snap(u_int32_t peerid)
{
	struct rt_node	*r;
	struct map	 map;

	bzero(&map, sizeof(map));

	RB_FOREACH(r, rt_tree, &rt) {
		map.prefix = r->prefix.s_addr;
		map.prefixlen = r->prefixlen;
		map.label = (ntohl(r->local_label) & MPLS_LABEL_MASK) >>
		    MPLS_LABEL_OFFSET;

		lde_imsg_compose_ldpe(IMSG_MAPPING_ADD, peerid, 0, &map,
		    sizeof(map));
	}
}

void
rt_clear(void)
{
	struct rt_node	*r;

	while ((r = RB_MIN(rt_tree, &rt)) != NULL)
		rt_remove(r);
}

u_int32_t
lde_assign_label()
{
	static u_int32_t label = MPLS_LABEL_RESERVED_MAX;

	/* XXX some checks needed */
	label++;
	return (htonl(label << MPLS_LABEL_OFFSET));
}

void
lde_insert(struct kroute *kr)
{
	struct rt_node		*rn;
	struct rt_label		*rl;
	struct lde_nbr_address	*addr;

	rn = rt_find(kr->prefix.s_addr, kr->prefixlen);
	if (rn == NULL) {
		rn = calloc(1, sizeof(*rn));
		if (rn == NULL)
			fatal("lde_insert");

		rn->prefix.s_addr = kr->prefix.s_addr;
		rn->prefixlen = kr->prefixlen;
		TAILQ_INIT(&rn->labels_list);

		rt_insert(rn);
	}

	if (rn->present)
		return;

	rn->present = 1;
	rn->nexthop.s_addr = kr->nexthop.s_addr;

	/* There is static assigned label for this route, record it in lib */
	if (kr->local_label) {
		rn->local_label = (htonl(kr->local_label) << MPLS_LABEL_OFFSET);
		return;
	}

	/* There is already a local mapping, check if there
	   is also a remote one */
	if (rn->local_label) {
		TAILQ_FOREACH(rl, &rn->labels_list, entry) {
			addr = lde_address_find(rl->nexthop, &rn->nexthop);
			if (addr != NULL) {
				rn->remote_label =
				    htonl(rl->label << MPLS_LABEL_OFFSET);
				TAILQ_REMOVE(&rn->labels_list, rl, entry);
				free(rl);
				break;
			}
		}
	} else {
		/* Directly connected route */
		if (kr->nexthop.s_addr == INADDR_ANY) {
			rn->local_label =
			    htonl(MPLS_LABEL_IMPLNULL << MPLS_LABEL_OFFSET);
			rn->nexthop.s_addr = htonl(INADDR_LOOPBACK);
		} else
			rn->local_label = lde_assign_label();
	}

	lde_send_insert_klabel(rn);
}

void
lde_check_mapping(struct map *map, struct lde_nbr *ln)
{
	struct rt_node		*rn;
	struct rt_label		*rl;
	struct lde_nbr_address	*addr;
	struct lde_map_entry	*me, *menew;
	struct lde_req_entry	*req;
	struct iface		*iface;
	struct map		 localmap;

	/* The route is not yet in fib. If we are in liberal mode create a
	   route and record the label */
	rn = rt_find(map->prefix, map->prefixlen);
	if (rn == NULL) {
		if (ldeconf->mode & MODE_RET_CONSERVATIVE)
			return;

		rn = calloc(1, sizeof(*rn));
		if (rn == NULL)
			fatal("lde_check_mapping");

		rn->prefix.s_addr = map->prefix;
		rn->prefixlen = map->prefixlen;
		rn->local_label = lde_assign_label();
		rn->present = 0;

		TAILQ_INIT(&rn->labels_list);

		rt_insert(rn);
	}

	TAILQ_FOREACH(me, &ln->recv_map_list, entry) {
		if (me->prefix.s_addr == map->prefix &&
		    me->prefixlen == map->prefixlen) {
			if (me->label == map->label) {
				lde_send_labelrelease(ln->peerid, map);
				return;
			}
		}
	}

	addr = lde_address_find(ln, &rn->nexthop);
	if (addr == NULL || !rn->present) {
		if (ldeconf->mode & MODE_RET_CONSERVATIVE) {
			lde_send_labelrelease(ln->peerid, map);
			return;
		}

		rl = calloc(1, sizeof(*rl));
		if (rl == NULL)
			fatal("lde_check_mapping");

		rl->label = map->label;
		rl->nexthop = ln;

		TAILQ_INSERT_TAIL(&rn->labels_list, rl, entry);
		return;
	}

	rn->remote_label = htonl(map->label << MPLS_LABEL_OFFSET);

	/* If we are ingress for this LSP install the label */
	if (rn->nexthop.s_addr == INADDR_ANY)
		lde_send_change_klabel(rn);

	/* Record the mapping from this peer */	
	menew = calloc(1, sizeof(*menew));
	if (menew == NULL)
		fatal("lde_check_mapping");

	menew->prefix.s_addr = map->prefix;
	menew->prefixlen = map->prefixlen;
	menew->label = map->label;

	TAILQ_INSERT_HEAD(&ln->recv_map_list, menew, entry);

	/* Redistribute the current mapping to every nbr */
	localmap.label = rn->local_label;
	localmap.prefix = rn->prefix.s_addr;
	localmap.prefixlen = rn->prefixlen;

	LIST_FOREACH(iface, &ldeconf->iface_list, entry) {
		LIST_FOREACH(ln, &iface->lde_nbr_list, entry) {
			/* Did we already send a mapping to this peer? */
			TAILQ_FOREACH(me, &ln->sent_map_list, entry) {
				if (me->prefix.s_addr == rn->prefix.s_addr &&
				    me->prefixlen == rn->prefixlen)
					break;
			}
			if (me != NULL) {
				/* XXX: check RAttributes */
				continue;
			}

			if (ldeconf->mode & MODE_ADV_UNSOLICITED &&
			    ldeconf->mode & MODE_DIST_ORDERED) {
				lde_send_labelmapping(ln->peerid, &localmap);

				menew = calloc(1, sizeof(*menew));
				if (menew == NULL)
					fatal("lde_check_mapping");

				menew->prefix.s_addr = map->prefix;
				menew->prefixlen = map->prefixlen;
				menew->label = map->label;

				TAILQ_INSERT_HEAD(&ln->sent_map_list, menew,
				    entry);
			}

			TAILQ_FOREACH(req, &ln->req_list, entry) {
				if (req->prefix.s_addr == rn->prefix.s_addr &&
				    req->prefixlen == rn->prefixlen)
					break;
			}
			if (req != NULL) {
				lde_send_labelmapping(ln->peerid, &localmap);

				menew = calloc(1, sizeof(*menew));
				if (menew == NULL)
					fatal("lde_check_mapping");

				menew->prefix.s_addr = map->prefix;
				menew->prefixlen = map->prefixlen;
				menew->label = map->label;

				TAILQ_INSERT_HEAD(&ln->sent_map_list, menew,
				    entry);

				TAILQ_REMOVE(&ln->req_list, req, entry);
				free(req);
			}
		}
	}

	lde_send_change_klabel(rn);
}

void
lde_check_request(struct map *map, struct lde_nbr *ln)
{
	struct lde_req_entry	*lre, *newlre;
	struct rt_node		*rn;
	struct lde_nbr		*lnn;
	struct map		 localmap;

	bzero(&newlre, sizeof(newlre));

	rn = rt_find(map->prefix, map->prefixlen);
	if (rn == NULL || rn->remote_label == NO_LABEL) {
		lde_send_notification(ln->peerid, S_NO_ROUTE, map->messageid,
		    MSG_TYPE_LABELREQUEST);
		return;
	}

	if (lde_address_find(ln, &rn->nexthop)) {
		lde_send_notification(ln->peerid, S_LOOP_DETECTED,
		    map->messageid, MSG_TYPE_LABELREQUEST);
		return;
	}

	TAILQ_FOREACH(lre, &ln->req_list, entry) {
		if (lre->prefix.s_addr == map->prefix &&
		    lre->prefixlen == map->prefixlen)
			return;
	}

	/* XXX: if we are egress ? */
	if (rn->remote_label != NO_LABEL) {
		bzero(&localmap, sizeof(localmap));
		localmap.prefix = map->prefix;
		localmap.prefixlen = map->prefixlen;
		localmap.label = rn->local_label;

		lde_send_labelmapping(ln->peerid, &localmap);
	} else {
		lnn = lde_find_address(rn->nexthop);
		if (lnn == NULL)
			return;

		lde_send_labelrequest(lnn->peerid, map);

		newlre = calloc(1, sizeof(*newlre));
		if (newlre == NULL)
			fatal("lde_check_request");

		newlre->prefix.s_addr = map->prefix;
		newlre->prefixlen = map->prefixlen;

		TAILQ_INSERT_HEAD(&ln->req_list, newlre, entry);
	}
}