Multipath support for linux kernel protocol.

This commit is contained in:
Ondrej Zajicek 2010-12-07 23:36:11 +01:00
parent 57c574d82a
commit 32b4972834

View file

@ -238,37 +238,143 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize)
return 1;
}
static void
nl_add_attr_u32(struct nlmsghdr *h, unsigned maxsize, int code, u32 data)
void
nl_add_attr(struct nlmsghdr *h, unsigned bufsize, unsigned code,
void *data, unsigned dlen)
{
unsigned len = RTA_LENGTH(4);
unsigned len = RTA_LENGTH(dlen);
unsigned pos = NLMSG_ALIGN(h->nlmsg_len);
struct rtattr *a;
if (NLMSG_ALIGN(h->nlmsg_len) + len > maxsize)
bug("nl_add_attr32: packet buffer overflow");
a = (struct rtattr *)((char *)h + NLMSG_ALIGN(h->nlmsg_len));
if (pos + len > bufsize)
bug("nl_add_attr: packet buffer overflow");
a = (struct rtattr *)((char *)h + pos);
a->rta_type = code;
a->rta_len = len;
memcpy(RTA_DATA(a), &data, 4);
h->nlmsg_len = NLMSG_ALIGN(h->nlmsg_len) + len;
h->nlmsg_len = pos + len;
memcpy(RTA_DATA(a), data, dlen);
}
static void
nl_add_attr_ipa(struct nlmsghdr *h, unsigned maxsize, int code, ip_addr ipa)
static inline void
nl_add_attr_u32(struct nlmsghdr *h, unsigned bufsize, int code, u32 data)
{
unsigned len = RTA_LENGTH(sizeof(ipa));
struct rtattr *a;
nl_add_attr(h, bufsize, code, &data, 4);
}
if (NLMSG_ALIGN(h->nlmsg_len) + len > maxsize)
bug("nl_add_attr_ipa: packet buffer overflow");
a = (struct rtattr *)((char *)h + NLMSG_ALIGN(h->nlmsg_len));
a->rta_type = code;
a->rta_len = len;
static inline void
nl_add_attr_ipa(struct nlmsghdr *h, unsigned bufsize, int code, ip_addr ipa)
{
ipa_hton(ipa);
memcpy(RTA_DATA(a), &ipa, sizeof(ipa));
h->nlmsg_len = NLMSG_ALIGN(h->nlmsg_len) + len;
nl_add_attr(h, bufsize, code, &ipa, sizeof(ipa));
}
#define RTNH_SIZE (sizeof(struct rtnexthop) + sizeof(struct rtattr) + sizeof(ip_addr))
static inline void
add_mpnexthop(char *buf, ip_addr ipa, unsigned iface, unsigned char weight)
{
struct rtnexthop *nh = (void *) buf;
struct rtattr *rt = (void *) (buf + sizeof(*nh));
nh->rtnh_len = RTNH_SIZE;
nh->rtnh_flags = 0;
nh->rtnh_hops = weight;
nh->rtnh_ifindex = iface;
rt->rta_len = sizeof(*rt) + sizeof(ipa);
rt->rta_type = RTA_GATEWAY;
ipa_hton(ipa);
memcpy(buf + sizeof(*nh) + sizeof(*rt), &ipa, sizeof(ipa));
}
static void
nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh)
{
unsigned len = sizeof(struct rtattr);
unsigned pos = NLMSG_ALIGN(h->nlmsg_len);
char *buf = (char *)h + pos;
struct rtattr *rt = (void *) buf;
buf += len;
for (; nh; nh = nh->next)
{
len += RTNH_SIZE;
if (pos + len > bufsize)
bug("nl_add_multipath: packet buffer overflow");
add_mpnexthop(buf, nh->gw, nh->iface->index, nh->weight);
buf += RTNH_SIZE;
}
rt->rta_type = RTA_MULTIPATH;
rt->rta_len = len;
h->nlmsg_len = pos + len;
}
static struct mpnh *
nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
{
/* Temporary buffer for multicast nexthops */
static struct mpnh *nh_buffer;
static int nh_buf_size; /* in number of structures */
static int nh_buf_used;
struct rtattr *a[RTA_CACHEINFO+1];
struct rtnexthop *nh = RTA_DATA(ra);
struct mpnh *rv, *first, **last;
int len = RTA_PAYLOAD(ra);
first = NULL;
last = &first;
nh_buf_used = 0;
while (len)
{
/* Use RTNH_OK(nh,len) ?? */
if ((len < sizeof(*nh)) || (len < nh->rtnh_len))
return NULL;
if (nh_buf_used == nh_buf_size)
{
nh_buf_size = nh_buf_size ? (nh_buf_size * 2) : 4;
nh_buffer = xrealloc(nh_buffer, nh_buf_size * sizeof(struct mpnh));
}
*last = rv = nh_buffer + nh_buf_used++;
rv->next = NULL;
last = &(rv->next);
rv->weight = nh->rtnh_hops;
rv->iface = if_find_by_index(nh->rtnh_ifindex);
if (!rv->iface)
return NULL;
/* Nonexistent RTNH_PAYLOAD ?? */
nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
nl_parse_attrs(RTNH_DATA(nh), a, sizeof(a));
if (a[RTA_GATEWAY])
{
if (RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr))
return NULL;
memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr));
ipa_ntoh(rv->gw);
neighbor *ng = neigh_find2(&p->p, &rv->gw, rv->iface,
(nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
if (!ng || (ng->scope == SCOPE_HOST))
return NULL;
}
else
return NULL;
len -= NLMSG_ALIGN(nh->rtnh_len);
nh = RTNH_NEXT(nh);
}
return first;
}
/*
* Scanning of interfaces
*/
@ -490,6 +596,7 @@ krt_capable(rte *e)
case RTD_BLACKHOLE:
case RTD_UNREACHABLE:
case RTD_PROHIBIT:
case RTD_MULTIPATH:
break;
default:
return 0;
@ -497,6 +604,15 @@ krt_capable(rte *e)
return 1;
}
static inline int
nh_bufsize(struct mpnh *nh)
{
int rv = 0;
for (; nh != NULL; nh = nh->next)
rv += RTNH_SIZE;
return rv;
}
static void
nl_send_route(struct krt_proto *p, rte *e, int new)
{
@ -505,7 +621,7 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
struct {
struct nlmsghdr h;
struct rtmsg r;
char buf[128];
char buf[64 + nh_bufsize(a->nexthops)];
} r;
DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new);
@ -531,7 +647,7 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
/* a->iface != NULL checked in krt_capable() */
if (ipa_has_link_scope(a->gw))
nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, a->iface->index);
nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, a->iface->index);
break;
case RTD_DEVICE:
@ -549,6 +665,10 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
case RTD_PROHIBIT:
r.r.rtm_type = RTN_PROHIBIT;
break;
case RTD_MULTIPATH:
r.r.rtm_type = RTN_UNICAST;
nl_add_multipath(&r.h, sizeof(r), a->nexthops);
break;
default:
bug("krt_capable inconsistent with nl_send_route");
}
@ -566,6 +686,7 @@ krt_set_notify(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
nl_send_route(p, new, 1);
}
#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0)
static void
@ -671,6 +792,21 @@ nl_parse_route(struct nlmsghdr *h, int scan)
switch (i->rtm_type)
{
case RTN_UNICAST:
if (a[RTA_MULTIPATH])
{
ra.dest = RTD_MULTIPATH;
ra.nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]);
if (!ra.nexthops)
{
log(L_ERR "KRT: Received strange multipath route %I/%d",
net->n.prefix, net->n.pxlen);
return;
}
break;
}
ra.iface = if_find_by_index(oif);
if (!ra.iface)
{