KRT: Add support for plenty of kernel route metrics

Linux kernel route metrics (RTA_METRICS netlink route attribute) are
represented and accessible as new route attributes:

krt_mtu, krt_window, krt_rtt, krt_rttvar, krt_sstresh, krt_cwnd, krt_advmss,
krt_reordering, krt_hoplimit, krt_initcwnd, krt_rto_min, krt_initrwnd,
krt_quickack, krt_lock_mtu, krt_lock_window, krt_lock_rtt, krt_lock_rttvar,
krt_lock_sstresh, krt_lock_cwnd, krt_lock_advmss, krt_lock_reordering,
krt_lock_hoplimit, krt_lock_rto_min, krt_feature_ecn, krt_feature_allfrag
This commit is contained in:
Ondrej Zajicek 2015-05-12 16:42:22 +02:00
parent 315f23a047
commit 9fdf9d29b6
9 changed files with 368 additions and 65 deletions

View file

@ -2248,7 +2248,7 @@ these attributes:
<tag>ip <cf/krt_prefsrc/</tag> (Linux) <tag>ip <cf/krt_prefsrc/</tag> (Linux)
The preferred source address. Used in source address selection for The preferred source address. Used in source address selection for
outgoing packets. Have to be one of IP addresses of the router. outgoing packets. Has to be one of the IP addresses of the router.
<tag>int <cf/krt_realm/</tag> (Linux) <tag>int <cf/krt_realm/</tag> (Linux)
The realm of the route. Can be used for traffic classification. The realm of the route. Can be used for traffic classification.

View file

@ -461,8 +461,14 @@ static inline void rt_lock_source(struct rte_src *src) { src->uc++; }
static inline void rt_unlock_source(struct rte_src *src) { src->uc--; } static inline void rt_unlock_source(struct rte_src *src) { src->uc--; }
void rt_prune_sources(void); void rt_prune_sources(void);
struct ea_walk_state {
ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */
eattr *ea; /* Current eattr, initially NULL */
u32 visited[4]; /* Bitfield, limiting max to 128 */
};
eattr *ea_find(ea_list *, unsigned ea); eattr *ea_find(ea_list *, unsigned ea);
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
int ea_get_int(ea_list *, unsigned ea, int def); int ea_get_int(ea_list *, unsigned ea, int def);
void ea_dump(ea_list *); void ea_dump(ea_list *);
void ea_sort(ea_list *); /* Sort entries in all sub-lists */ void ea_sort(ea_list *); /* Sort entries in all sub-lists */

View file

@ -307,6 +307,82 @@ ea_find(ea_list *e, unsigned id)
return a; return a;
} }
/**
* ea_walk - walk through extended attributes
* @s: walk state structure
* @id: start of attribute ID interval
* @max: length of attribute ID interval
*
* Given an extended attribute list, ea_walk() walks through the list looking
* for first occurrences of attributes with ID in specified interval from @id to
* (@id + @max - 1), returning pointers to found &eattr structures, storing its
* walk state in @s for subsequent calls.
* The function ea_walk() is supposed to be called in a loop, with initially
* zeroed walk state structure @s with filled the initial extended attribute
* list, returning one found attribute in each call or %NULL when no other
* attribute exists. The extended attribute list or the arguments should not be
* modified between calls. The maximum value of @max is 128.
*/
eattr *
ea_walk(struct ea_walk_state *s, uint id, uint max)
{
ea_list *e = s->eattrs;
eattr *a = s->ea;
eattr *a_max;
max = id + max;
if (a)
goto step;
for (; e; e = e->next)
{
if (e->flags & EALF_BISECT)
{
int l, r, m;
l = 0;
r = e->count - 1;
while (l < r)
{
m = (l+r) / 2;
if (e->attrs[m].id < id)
l = m + 1;
else
r = m;
}
a = e->attrs + l;
}
else
a = e->attrs;
step:
a_max = e->attrs + e->count;
for (; a < a_max; a++)
if ((a->id >= id) && (a->id < max))
{
int n = a->id - id;
if (BIT32_TEST(s->visited, n))
continue;
BIT32_SET(s->visited, n);
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
continue;
s->eattrs = e;
s->ea = a;
return a;
}
else if (e->flags & EALF_BISECT)
break;
}
return NULL;
}
/** /**
* ea_get_int - fetch an integer attribute * ea_get_int - fetch an integer attribute
* @e: attribute list * @e: attribute list

View file

@ -44,5 +44,7 @@ struct krt_state {
static inline void krt_sys_init(struct krt_proto *p UNUSED) { } static inline void krt_sys_init(struct krt_proto *p UNUSED) { }
static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { }
#endif #endif

View file

@ -32,6 +32,59 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; }
/* Kernel routes */ /* Kernel routes */
#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0x10)
#define EA_KRT_REALM EA_CODE(EAP_KRT, 0x11)
#define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */
#define KRT_METRICS_OFFSET 0x20 /* Offset of EA_KRT_* vs RTAX_* */
#define KRT_FEATURES_MAX 4
/*
* Following attributes are parts of RTA_METRICS kernel route attribute, their
* ids must be consistent with their RTAX_* constants (+ KRT_METRICS_OFFSET)
*/
#define EA_KRT_METRICS EA_CODE(EAP_KRT, 0x20) /* Dummy one */
#define EA_KRT_LOCK EA_CODE(EAP_KRT, 0x21)
#define EA_KRT_MTU EA_CODE(EAP_KRT, 0x22)
#define EA_KRT_WINDOW EA_CODE(EAP_KRT, 0x23)
#define EA_KRT_RTT EA_CODE(EAP_KRT, 0x24)
#define EA_KRT_RTTVAR EA_CODE(EAP_KRT, 0x25)
#define EA_KRT_SSTRESH EA_CODE(EAP_KRT, 0x26)
#define EA_KRT_CWND EA_CODE(EAP_KRT, 0x27)
#define EA_KRT_ADVMSS EA_CODE(EAP_KRT, 0x28)
#define EA_KRT_REORDERING EA_CODE(EAP_KRT, 0x29)
#define EA_KRT_HOPLIMIT EA_CODE(EAP_KRT, 0x2a)
#define EA_KRT_INITCWND EA_CODE(EAP_KRT, 0x2b)
#define EA_KRT_FEATURES EA_CODE(EAP_KRT, 0x2c)
#define EA_KRT_RTO_MIN EA_CODE(EAP_KRT, 0x2d)
#define EA_KRT_INITRWND EA_CODE(EAP_KRT, 0x2e)
#define EA_KRT_QUICKACK EA_CODE(EAP_KRT, 0x2f)
/* Bits of EA_KRT_LOCK, also based on RTAX_* constants */
#define EA_KRT_LOCK_MTU EA_KRT_LOCK | EA_BIT(0x2)
#define EA_KRT_LOCK_WINDOW EA_KRT_LOCK | EA_BIT(0x3)
#define EA_KRT_LOCK_RTT EA_KRT_LOCK | EA_BIT(0x4)
#define EA_KRT_LOCK_RTTVAR EA_KRT_LOCK | EA_BIT(0x5)
#define EA_KRT_LOCK_SSTHRESH EA_KRT_LOCK | EA_BIT(0x6)
#define EA_KRT_LOCK_CWND EA_KRT_LOCK | EA_BIT(0x7)
#define EA_KRT_LOCK_ADVMSS EA_KRT_LOCK | EA_BIT(0x8)
#define EA_KRT_LOCK_REORDERING EA_KRT_LOCK | EA_BIT(0x9)
#define EA_KRT_LOCK_HOPLIMIT EA_KRT_LOCK | EA_BIT(0xa)
// define EA_KRT_LOCK_INITCWND EA_KRT_LOCK | EA_BIT(0xb)
// define EA_KRT_LOCK_FEATURES EA_KRT_LOCK | EA_BIT(0xc)
#define EA_KRT_LOCK_RTO_MIN EA_KRT_LOCK | EA_BIT(0xd)
// define EA_KRT_LOCK_INITRWND EA_KRT_LOCK | EA_BIT(0xe)
/* Bits of EA_KRT_FEATURES, based on RTAX_FEATURE_* constants */
#define EA_KRT_FEATURE_ECN EA_KRT_FEATURES | EA_BIT(0x0)
// define EA_KRT_FEATURE_SACK EA_KRT_FEATURES | EA_BIT(0x1)
// define EA_KRT_FEATURE_TSTAMP EA_KRT_FEATURES | EA_BIT(0x2)
#define EA_KRT_FEATURE_ALLFRAG EA_KRT_FEATURES | EA_BIT(0x3)
#define NL_NUM_TABLES 256 #define NL_NUM_TABLES 256
struct krt_params { struct krt_params {

View file

@ -10,7 +10,12 @@ CF_HDR
CF_DECLS CF_DECLS
CF_KEYWORDS(KERNEL, TABLE, KRT_PREFSRC, KRT_REALM) CF_KEYWORDS(KERNEL, TABLE, KRT_PREFSRC, KRT_REALM, KRT_MTU, KRT_WINDOW, KRT_RTT,
KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING,
KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK,
KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR,
KRT_LOCK_SSTRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING,
KRT_LOCK_HOPLIMIT, KRT_LOCK_RTO_MIN, KRT_FEATURE_ECN, KRT_FEATURE_ALLFRAG)
CF_GRAMMAR CF_GRAMMAR
@ -27,6 +32,35 @@ kern_sys_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_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_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); })
CF_ADDTO(dynamic_attr, KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU); })
CF_ADDTO(dynamic_attr, KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); })
CF_ADDTO(dynamic_attr, KRT_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTT); })
CF_ADDTO(dynamic_attr, KRT_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTTVAR); })
CF_ADDTO(dynamic_attr, KRT_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SSTRESH); })
CF_ADDTO(dynamic_attr, KRT_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_CWND); })
CF_ADDTO(dynamic_attr, KRT_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_ADVMSS); })
CF_ADDTO(dynamic_attr, KRT_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REORDERING); })
CF_ADDTO(dynamic_attr, KRT_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_HOPLIMIT); })
CF_ADDTO(dynamic_attr, KRT_INITCWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITCWND); })
CF_ADDTO(dynamic_attr, KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTO_MIN); })
CF_ADDTO(dynamic_attr, KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITRWND); })
CF_ADDTO(dynamic_attr, KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_QUICKACK); })
CF_ADDTO(dynamic_attr, KRT_LOCK_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_MTU); })
CF_ADDTO(dynamic_attr, KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_WINDOW); })
CF_ADDTO(dynamic_attr, KRT_LOCK_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTT); })
CF_ADDTO(dynamic_attr, KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTTVAR); })
CF_ADDTO(dynamic_attr, KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_SSTHRESH); })
CF_ADDTO(dynamic_attr, KRT_LOCK_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_CWND); })
CF_ADDTO(dynamic_attr, KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_ADVMSS); })
CF_ADDTO(dynamic_attr, KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_REORDERING); })
CF_ADDTO(dynamic_attr, KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_HOPLIMIT); })
CF_ADDTO(dynamic_attr, KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTO_MIN); })
CF_ADDTO(dynamic_attr, KRT_FEATURE_ECN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ECN); })
CF_ADDTO(dynamic_attr, KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ALLFRAG); })
CF_CODE CF_CODE
CF_END CF_END

View file

@ -238,21 +238,24 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize)
return 1; return 1;
} }
void struct rtattr *
nl_add_attr(struct nlmsghdr *h, unsigned bufsize, unsigned code, nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen)
void *data, unsigned dlen)
{ {
unsigned len = RTA_LENGTH(dlen); uint pos = NLMSG_ALIGN(h->nlmsg_len);
unsigned pos = NLMSG_ALIGN(h->nlmsg_len); uint len = RTA_LENGTH(dlen);
struct rtattr *a;
if (pos + len > bufsize) if (pos + len > bufsize)
bug("nl_add_attr: packet buffer overflow"); bug("nl_add_attr: packet buffer overflow");
a = (struct rtattr *)((char *)h + pos);
struct rtattr *a = (struct rtattr *)((char *)h + pos);
a->rta_type = code; a->rta_type = code;
a->rta_len = len; a->rta_len = len;
h->nlmsg_len = pos + len; h->nlmsg_len = pos + len;
if (dlen > 0)
memcpy(RTA_DATA(a), data, dlen); memcpy(RTA_DATA(a), data, dlen);
return a;
} }
static inline void static inline void
@ -268,49 +271,59 @@ nl_add_attr_ipa(struct nlmsghdr *h, unsigned bufsize, int code, ip_addr ipa)
nl_add_attr(h, bufsize, code, &ipa, sizeof(ipa)); nl_add_attr(h, bufsize, code, &ipa, sizeof(ipa));
} }
#define RTNH_SIZE (sizeof(struct rtnexthop) + sizeof(struct rtattr) + sizeof(ip_addr)) static inline struct rtattr *
nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code)
static inline void
add_mpnexthop(char *buf, ip_addr ipa, unsigned iface, unsigned char weight)
{ {
struct rtnexthop *nh = (void *) buf; return nl_add_attr(h, bufsize, code, NULL, 0);
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 inline void
nl_close_attr(struct nlmsghdr *h, struct rtattr *a)
{
a->rta_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)a;
}
static inline struct rtnexthop *
nl_open_nexthop(struct nlmsghdr *h, uint bufsize)
{
uint pos = NLMSG_ALIGN(h->nlmsg_len);
uint len = RTNH_LENGTH(0);
if (pos + len > bufsize)
bug("nl_open_nexthop: packet buffer overflow");
h->nlmsg_len = pos + len;
return (void *)h + pos;
}
static inline void
nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh)
{
nh->rtnh_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)nh;
}
static void static void
nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh) nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh)
{ {
unsigned len = sizeof(struct rtattr); struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
unsigned pos = NLMSG_ALIGN(h->nlmsg_len);
char *buf = (char *)h + pos;
struct rtattr *rt = (void *) buf;
buf += len;
for (; nh; nh = nh->next) for (; nh; nh = nh->next)
{ {
len += RTNH_SIZE; struct rtnexthop *rtnh = nl_open_nexthop(h, bufsize);
if (pos + len > bufsize)
bug("nl_add_multipath: packet buffer overflow");
add_mpnexthop(buf, nh->gw, nh->iface->index, nh->weight); rtnh->rtnh_flags = 0;
buf += RTNH_SIZE; rtnh->rtnh_hops = nh->weight;
rtnh->rtnh_ifindex = nh->iface->index;
nl_add_attr_u32(h, bufsize, RTA_GATEWAY, nh->gw);
nl_close_nexthop(h, rtnh);
} }
rt->rta_type = RTA_MULTIPATH; nl_close_attr(h, a);
rt->rta_len = len;
h->nlmsg_len = pos + len;
} }
static struct mpnh * static struct mpnh *
nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
{ {
@ -374,6 +387,47 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
return first; return first;
} }
static void
nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max)
{
struct rtattr *a = nl_open_attr(h, bufsize, RTA_METRICS);
int t;
for (t = 1; t < max; t++)
if (metrics[0] & (1 << t))
nl_add_attr_u32(h, bufsize, t, metrics[t]);
nl_close_attr(h, a);
}
static int
nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max)
{
struct rtattr *a = RTA_DATA(hdr);
int len = RTA_PAYLOAD(hdr);
metrics[0] = 0;
for (; RTA_OK(a, len); a = RTA_NEXT(a, len))
{
if (a->rta_type == RTA_UNSPEC)
continue;
if (a->rta_type >= max)
continue;
if (RTA_PAYLOAD(a) != 4)
return -1;
metrics[0] |= 1 << a->rta_type;
metrics[a->rta_type] = *(u32 *)RTA_DATA(a);
}
if (len > 0)
return -1;
return 0;
}
/* /*
* Scanning of interfaces * Scanning of interfaces
@ -617,7 +671,7 @@ nh_bufsize(struct mpnh *nh)
{ {
int rv = 0; int rv = 0;
for (; nh != NULL; nh = nh->next) for (; nh != NULL; nh = nh->next)
rv += RTNH_SIZE; rv += RTNH_LENGTH(RTA_LENGTH(sizeof(ip_addr)));
return rv; return rv;
} }
@ -630,7 +684,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
struct { struct {
struct nlmsghdr h; struct nlmsghdr h;
struct rtmsg r; struct rtmsg r;
char buf[128 + nh_bufsize(a->nexthops)]; char buf[128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops)];
} r; } r;
DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new); DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new);
@ -649,13 +703,8 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
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);
u32 metric = 0;
if (new && e->attrs->source == RTS_INHERIT)
metric = e->u.krt.metric;
if (ea = ea_find(eattrs, EA_KRT_METRIC)) if (ea = ea_find(eattrs, EA_KRT_METRIC))
metric = ea->u.data; nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data);
if (metric != 0)
nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric);
if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
@ -663,6 +712,22 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
if (ea = ea_find(eattrs, EA_KRT_REALM)) if (ea = ea_find(eattrs, EA_KRT_REALM))
nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data); nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
u32 metrics[KRT_METRICS_MAX];
metrics[0] = 0;
struct ea_walk_state ews = { .eattrs = eattrs };
while (ea = ea_walk(&ews, EA_KRT_METRICS, KRT_METRICS_MAX))
{
int id = ea->id - EA_KRT_METRICS;
metrics[0] |= 1 << id;
metrics[id] = ea->u.data;
}
if (metrics[0])
nl_add_metrics(&r.h, sizeof(r), metrics, KRT_METRICS_MAX);
/* a->iface != NULL checked in krt_capable() for router and device routes */ /* a->iface != NULL checked in krt_capable() for router and device routes */
switch (a->dest) switch (a->dest)
@ -932,6 +997,38 @@ nl_parse_route(struct nlmsghdr *h, int scan)
memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4); memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4);
} }
if (a[RTA_METRICS])
{
u32 metrics[KRT_METRICS_MAX];
ea_list *ea = alloca(sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr));
int t, n = 0;
if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0)
{
log(L_ERR "KRT: Received route %I/%d with strange RTA_METRICS attribute",
net->n.prefix, net->n.pxlen);
return;
}
for (t = 1; t < KRT_METRICS_MAX; t++)
if (metrics[0] & (1 << t))
{
ea->attrs[n].id = EA_CODE(EAP_KRT, KRT_METRICS_OFFSET + t);
ea->attrs[n].flags = 0;
ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */
ea->attrs[n].u.data = metrics[t];
n++;
}
if (n > 0)
{
ea->next = ra.eattrs;
ea->flags = EALF_SORTED;
ea->count = n;
ra.eattrs = ea;
}
}
if (scan) if (scan)
krt_got_route(p, e); krt_got_route(p, e);
else else
@ -1130,6 +1227,50 @@ krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
d->sys.table_id = s->sys.table_id; d->sys.table_id = s->sys.table_id;
} }
static const char *krt_metrics_names[KRT_METRICS_MAX] = {
NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss",
"reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack"
};
static const char *krt_features_names[KRT_FEATURES_MAX] = {
"ecn", NULL, NULL, "allfrag"
};
int
krt_sys_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;
case EA_KRT_LOCK:
buf += bsprintf(buf, "lock:");
ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX);
return GA_FULL;
case EA_KRT_FEATURES:
buf += bsprintf(buf, "features:");
ea_format_bitfield(a, buf, buflen, krt_features_names, 0, KRT_FEATURES_MAX);
return GA_FULL;
default:;
int id = (int)EA_ID(a->id) - KRT_METRICS_OFFSET;
if (id > 0 && id < KRT_METRICS_MAX)
{
bsprintf(buf, "%s", krt_metrics_names[id]);
return GA_NAME;
}
return GA_UNKNOWN;
}
}
void void

View file

@ -1191,7 +1191,7 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src)
} }
static int static int
krt_get_attr(eattr * a, byte * buf, int buflen UNUSED) krt_get_attr(eattr *a, byte *buf, int buflen)
{ {
switch (a->id) switch (a->id)
{ {
@ -1203,16 +1203,8 @@ krt_get_attr(eattr * a, byte * buf, int buflen UNUSED)
bsprintf(buf, "metric"); bsprintf(buf, "metric");
return GA_NAME; return GA_NAME;
case EA_KRT_PREFSRC:
bsprintf(buf, "prefsrc");
return GA_NAME;
case EA_KRT_REALM:
bsprintf(buf, "realm");
return GA_NAME;
default: default:
return GA_UNKNOWN; return krt_sys_get_attr(a, buf, buflen);
} }
} }

View file

@ -28,8 +28,6 @@ struct kif_proto;
#define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0) #define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0)
#define EA_KRT_METRIC EA_CODE(EAP_KRT, 1) #define EA_KRT_METRIC EA_CODE(EAP_KRT, 1)
#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 2)
#define EA_KRT_REALM EA_CODE(EAP_KRT, 3)
/* Whenever we recognize our own routes, we allow learing of foreign routes */ /* Whenever we recognize our own routes, we allow learing of foreign routes */
@ -131,6 +129,7 @@ void krt_sys_copy_config(struct krt_config *, struct krt_config *);
int krt_capable(rte *e); int krt_capable(rte *e);
void krt_do_scan(struct krt_proto *); void krt_do_scan(struct krt_proto *);
void krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs); void krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs);
int krt_sys_get_attr(eattr *a, byte *buf, int buflen);
/* kif sysdep */ /* kif sysdep */