diff options
Diffstat (limited to 'sys/net/route.c')
-rw-r--r-- | sys/net/route.c | 101 |
1 files changed, 100 insertions, 1 deletions
diff --git a/sys/net/route.c b/sys/net/route.c index 00d94f9bfea..d96e42183a2 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route.c,v 1.46 2004/07/28 13:13:41 markus Exp $ */ +/* $OpenBSD: route.c,v 1.47 2004/08/03 11:22:15 henning Exp $ */ /* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */ /* @@ -111,6 +111,7 @@ #include <sys/protosw.h> #include <sys/ioctl.h> #include <sys/kernel.h> +#include <sys/queue.h> #include <sys/pool.h> #include <net/if.h> @@ -147,6 +148,20 @@ static int rtdeletemsg(struct rtentry *); static int rtflushclone1(struct radix_node *, void *); static void rtflushclone(struct radix_node_head *, struct rtentry *); +#define LABELID_MAX 50000 + +struct rt_label { + TAILQ_ENTRY(rt_label) rtl_entry; + char rtl_name[RTLABEL_LEN]; + u_int16_t rtl_id; + int rtl_ref; +}; + +TAILQ_HEAD(rt_labels, rt_label) rt_labels = TAILQ_HEAD_INITIALIZER(rt_labels); + +static u_int16_t rtlabel_name2id(char *); +static void rtlabel_unref(u_int16_t); + #ifdef IPSEC static struct ifaddr * @@ -332,6 +347,7 @@ rtfree(rt) ifa = rt->rt_ifa; if (ifa) IFAFREE(ifa); + rtlabel_unref(rt->rt_labelid); Free(rt_key(rt)); pool_put(&rtentry_pool, rt); } @@ -670,6 +686,7 @@ rtrequest1(req, info, ret_nrt) struct radix_node_head *rnh; struct ifaddr *ifa; struct sockaddr *ndst; + struct sockaddr_rtlabel *sa_rl; #define senderr(x) { error = x ; goto bad; } if ((rnh = rt_tables[dst->sa_family]) == 0) @@ -767,6 +784,13 @@ rtrequest1(req, info, ret_nrt) senderr(EEXIST); } #endif + + if (info->rti_info[RTAX_LABEL] != NULL) { + sa_rl = (struct sockaddr_rtlabel *) + info->rti_info[RTAX_LABEL]; + rt->rt_labelid = rtlabel_name2id(sa_rl->sr_label); + } + ifa->ifa_refcnt++; rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; @@ -1172,3 +1196,78 @@ rt_timer_timer(arg) timeout_add(to, hz); /* every second */ } + +static u_int16_t +rtlabel_name2id(char *name) +{ + struct rt_label *label, *p = NULL; + u_int16_t new_id = 1; + + TAILQ_FOREACH(label, &rt_labels, rtl_entry) + if (strcmp(name, label->rtl_name) == 0) { + label->rtl_ref++; + return (label->rtl_id); + } + + /* + * to avoid fragmentation, we do a linear search from the beginning + * and take the first free slot we find. if there is none or the list + * is empty, append a new entry at the end. + */ + + if (!TAILQ_EMPTY(&rt_labels)) + for (p = TAILQ_FIRST(&rt_labels); p != NULL && + p->rtl_id == new_id; p = TAILQ_NEXT(p, rtl_entry)) + new_id = p->rtl_id + 1; + + if (new_id > LABELID_MAX) + return (0); + + label = (struct rt_label *)malloc(sizeof(struct rt_label), + M_TEMP, M_NOWAIT); + if (label == NULL) + return (0); + bzero(label, sizeof(struct rt_label)); + strlcpy(label->rtl_name, name, sizeof(label->rtl_name)); + label->rtl_id = new_id; + label->rtl_ref++; + + if (p != NULL) /* insert new entry before p */ + TAILQ_INSERT_BEFORE(p, label, rtl_entry); + else /* either list empty or no free slot in between */ + TAILQ_INSERT_TAIL(&rt_labels, label, rtl_entry); + + return (label->rtl_id); +} + +const char * +rtlabel_id2name(u_int16_t id) +{ + struct rt_label *label; + + TAILQ_FOREACH(label, &rt_labels, rtl_entry) + if (label->rtl_id == id) + return (label->rtl_name); + + return (NULL); +} + +static void +rtlabel_unref(u_int16_t id) +{ + struct rt_label *p, *next; + + if (id == 0) + return; + + for (p = TAILQ_FIRST(&rt_labels); p != NULL; p = next) { + next = TAILQ_NEXT(p, rtl_entry); + if (id == p->rtl_id) { + if (--p->rtl_ref == 0) { + TAILQ_REMOVE(&rt_labels, p, rtl_entry); + free(p, M_TEMP); + } + break; + } + } +} |