Adds support for several Linux kernel route attributes.

This commit is contained in:
Ondrej Zajicek 2011-04-13 12:32:27 +02:00
parent 4aef102be1
commit 71ca77169d
6 changed files with 113 additions and 21 deletions

View file

@ -1420,12 +1420,20 @@ device routes from BIRD tables to kernel routing tables is restricted
to prevent accidental interference. This restriction can be disabled using to prevent accidental interference. This restriction can be disabled using
<cf/device routes/ switch. <cf/device routes/ switch.
<p>If your OS supports only a single routing table, you can configure only one <p>If your OS supports only a single routing table, you can configure
instance of the Kernel protocol. If it supports multiple tables (in order to only one instance of the Kernel protocol. If it supports multiple
allow policy routing; such an OS is for example Linux 2.2), you can run as many instances as you want, but each of tables (in order to allow policy routing; such an OS is for example
them must be connected to a different BIRD routing table and to a different Linux), you can run as many instances as you want, but each of them
must be connected to a different BIRD routing table and to a different
kernel table. kernel table.
<p>Because the kernel protocol is partially integrated with the
connected routing table, there are two limitations - it is not
possible to connect more kernel protocols to the same routing table
and changing route attributes (even the kernel ones) in an export
filter of a kernel protocol does not work. Both limitations can be
overcome using another routing table and the pipe protocol.
<sect1>Configuration <sect1>Configuration
<p><descrip> <p><descrip>
@ -1450,12 +1458,27 @@ kernel table.
only on systems supporting multiple routing tables. only on systems supporting multiple routing tables.
</descrip> </descrip>
<p>The Kernel protocol doesn't define any route attributes. <sect1>Attributes
<p>The Kernel protocol defines several attributes. These attributes
are translated to appropriate system (and OS-specific) route attributes.
We support these attributes:
<descrip>
<tag>ip <cf/krt_prefsrc/</tag> (Linux) The preferred source address.
Used in source address selection for outgoing packets. Have to
be one of IP addresses of the router.
<tag>int <cf/krt_realm/</tag> (Linux) The realm of the route. Can be
used for traffic classification.
</descrip>
<sect1>Example
<p>A simple configuration can look this way: <p>A simple configuration can look this way:
<p><code> <p><code>
protocol kernel { protocol kernel {
import all;
export all; export all;
} }
</code> </code>

View file

@ -344,7 +344,8 @@ typedef struct eattr {
#define EAP_BGP 1 /* BGP attributes */ #define EAP_BGP 1 /* BGP attributes */
#define EAP_RIP 2 /* RIP */ #define EAP_RIP 2 /* RIP */
#define EAP_OSPF 3 /* OSPF */ #define EAP_OSPF 3 /* OSPF */
#define EAP_MAX 4 #define EAP_KRT 4 /* Kernel route attributes */
#define EAP_MAX 5
#define EA_CODE(proto,id) (((proto) << 8) | (id)) #define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_PROTO(ea) ((ea) >> 8) #define EA_PROTO(ea) ((ea) >> 8)

View file

@ -10,7 +10,7 @@ CF_HDR
CF_DECLS CF_DECLS
CF_KEYWORDS(ASYNC, KERNEL, TABLE) CF_KEYWORDS(ASYNC, KERNEL, TABLE, KRT_PREFSRC, KRT_REALM)
CF_GRAMMAR CF_GRAMMAR
@ -24,6 +24,9 @@ nl_item:
} }
; ;
CF_ADDTO(dynamic_attr, KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); })
CF_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); })
CF_CODE CF_CODE
CF_END CF_END

View file

@ -18,6 +18,7 @@
#include "nest/route.h" #include "nest/route.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "nest/iface.h" #include "nest/iface.h"
#include "lib/alloca.h"
#include "lib/timer.h" #include "lib/timer.h"
#include "lib/unix.h" #include "lib/unix.h"
#include "lib/krt.h" #include "lib/krt.h"
@ -618,6 +619,7 @@ nh_bufsize(struct mpnh *nh)
static void static void
nl_send_route(struct krt_proto *p, rte *e, int new) nl_send_route(struct krt_proto *p, rte *e, int new)
{ {
eattr *ea;
net *net = e->net; net *net = e->net;
rta *a = e->attrs; rta *a = e->attrs;
struct { struct {
@ -641,6 +643,13 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
r.r.rtm_protocol = RTPROT_BIRD; r.r.rtm_protocol = RTPROT_BIRD;
r.r.rtm_scope = RT_SCOPE_UNIVERSE; r.r.rtm_scope = RT_SCOPE_UNIVERSE;
nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
if (ea = ea_find(a->eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
if (ea = ea_find(a->eattrs, EA_KRT_REALM))
nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
switch (a->dest) switch (a->dest)
{ {
case RTD_ROUTER: case RTD_ROUTER:
@ -698,10 +707,9 @@ nl_parse_route(struct nlmsghdr *h, int scan)
struct rtmsg *i; struct rtmsg *i;
struct rtattr *a[RTA_CACHEINFO+1]; struct rtattr *a[RTA_CACHEINFO+1];
int new = h->nlmsg_type == RTM_NEWROUTE; int new = h->nlmsg_type == RTM_NEWROUTE;
ip_addr dst;
rte *e; ip_addr dst = IPA_NONE;
net *net; u32 oif = ~0;
u32 oif;
int src; int src;
if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a))) if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a)))
@ -709,12 +717,14 @@ nl_parse_route(struct nlmsghdr *h, int scan)
if (i->rtm_family != BIRD_AF) if (i->rtm_family != BIRD_AF)
return; return;
if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) || if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) ||
(a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) ||
(a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||
#ifdef IPV6 #ifdef IPV6
(a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) || (a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) ||
#endif #endif
(a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr))) (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) ||
(a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) ||
(a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||
(a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) ||
(a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_OIF]) != 4))
{ {
log(L_ERR "KRT: Malformed message received"); log(L_ERR "KRT: Malformed message received");
return; return;
@ -725,13 +735,9 @@ nl_parse_route(struct nlmsghdr *h, int scan)
memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst)); memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst));
ipa_ntoh(dst); ipa_ntoh(dst);
} }
else
dst = IPA_NONE;
if (a[RTA_OIF]) if (a[RTA_OIF])
memcpy(&oif, RTA_DATA(a[RTA_OIF]), sizeof(oif)); memcpy(&oif, RTA_DATA(a[RTA_OIF]), sizeof(oif));
else
oif = ~0;
DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p->p.name); DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p->p.name);
@ -782,7 +788,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
src = KRT_SRC_ALIEN; src = KRT_SRC_ALIEN;
} }
net = net_get(p->p.table, dst, i->rtm_dst_len); net *net = net_get(p->p.table, dst, i->rtm_dst_len);
rta ra = { rta ra = {
.proto = &p->p, .proto = &p->p,
@ -871,15 +877,49 @@ nl_parse_route(struct nlmsghdr *h, int scan)
return; return;
} }
e = rte_get_temp(&ra); rte *e = rte_get_temp(&ra);
e->net = net; e->net = net;
e->u.krt.src = src; e->u.krt.src = src;
e->u.krt.proto = i->rtm_protocol; e->u.krt.proto = i->rtm_protocol;
e->u.krt.type = i->rtm_type; e->u.krt.type = i->rtm_type;
if (a[RTA_PRIORITY]) if (a[RTA_PRIORITY])
memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric));
else else
e->u.krt.metric = 0; e->u.krt.metric = 0;
if (a[RTA_PREFSRC])
{
ip_addr ps;
memcpy(&ps, RTA_DATA(a[RTA_PREFSRC]), sizeof(ps));
ipa_ntoh(ps);
ea_list *ea = alloca(sizeof(ea_list) + sizeof(eattr));
ea->next = ra.eattrs;
ra.eattrs = ea;
ea->flags = EALF_SORTED;
ea->count = 1;
ea->attrs[0].id = EA_KRT_PREFSRC;
ea->attrs[0].flags = 0;
ea->attrs[0].type = EAF_TYPE_IP_ADDRESS;
ea->attrs[0].u.ptr = alloca(sizeof(struct adata) + sizeof(ps));
ea->attrs[0].u.ptr->length = sizeof(ps);
memcpy(ea->attrs[0].u.ptr->data, &ps, sizeof(ps));
}
if (a[RTA_FLOW])
{
ea_list *ea = alloca(sizeof(ea_list) + sizeof(eattr));
ea->next = ra.eattrs;
ra.eattrs = ea;
ea->flags = EALF_SORTED;
ea->count = 1;
ea->attrs[0].id = EA_KRT_REALM;
ea->attrs[0].flags = 0;
ea->attrs[0].type = EAF_TYPE_INT;
memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4);
}
if (scan) if (scan)
krt_got_route(p, e); krt_got_route(p, e);
else else

View file

@ -890,6 +890,7 @@ krt_init(struct proto_config *c)
p->p.accept_ra_types = RA_OPTIMAL; p->p.accept_ra_types = RA_OPTIMAL;
p->p.import_control = krt_import_control; p->p.import_control = krt_import_control;
p->p.rt_notify = krt_notify; p->p.rt_notify = krt_notify;
return &p->p; return &p->p;
} }
@ -907,15 +908,35 @@ krt_reconfigure(struct proto *p, struct proto_config *new)
; ;
} }
static int
krt_get_attr(eattr * a, byte * buf, int buflen UNUSED)
{
switch (a->id)
{
case EA_KRT_PREFSRC:
bsprintf(buf, "prefsrc");
return GA_NAME;
case EA_KRT_REALM:
bsprintf(buf, "realm");
return GA_NAME;
default:
return GA_UNKNOWN;
}
}
struct protocol proto_unix_kernel = { struct protocol proto_unix_kernel = {
name: "Kernel", name: "Kernel",
template: "kernel%d", template: "kernel%d",
attr_class: EAP_KRT,
preconfig: krt_preconfig, preconfig: krt_preconfig,
postconfig: krt_postconfig, postconfig: krt_postconfig,
init: krt_init, init: krt_init,
start: krt_start, start: krt_start,
shutdown: krt_shutdown, shutdown: krt_shutdown,
reconfigure: krt_reconfigure, reconfigure: krt_reconfigure,
get_attr: krt_get_attr,
#ifdef KRT_ALLOW_LEARN #ifdef KRT_ALLOW_LEARN
dump: krt_dump, dump: krt_dump,
dump_attrs: krt_dump_attrs, dump_attrs: krt_dump_attrs,

View file

@ -30,6 +30,10 @@ struct kif_proto;
#define KRF_INSTALLED 0x80 /* This route should be installed in the kernel */ #define KRF_INSTALLED 0x80 /* This route should be installed in the kernel */
#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0)
#define EA_KRT_REALM EA_CODE(EAP_KRT, 1)
/* Whenever we recognize our own routes, we allow learing of foreign routes */ /* Whenever we recognize our own routes, we allow learing of foreign routes */
#ifdef CONFIG_SELF_CONSCIOUS #ifdef CONFIG_SELF_CONSCIOUS