From 9fdf9d29b6b570205c36934aab7e50539e042102 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 12 May 2015 16:42:22 +0200 Subject: [PATCH] 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 --- doc/bird.sgml | 2 +- nest/route.h | 6 ++ nest/rt-attr.c | 76 +++++++++++++ sysdep/bsd/krt-sys.h | 2 + sysdep/linux/krt-sys.h | 53 +++++++++ sysdep/linux/netlink.Y | 40 ++++++- sysdep/linux/netlink.c | 239 ++++++++++++++++++++++++++++++++--------- sysdep/unix/krt.c | 12 +-- sysdep/unix/krt.h | 3 +- 9 files changed, 368 insertions(+), 65 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index fc5fc9ae..1c2dda4b 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2248,7 +2248,7 @@ these attributes: ip (Linux) 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. int (Linux) The realm of the route. Can be used for traffic classification. diff --git a/nest/route.h b/nest/route.h index fccc571b..87f10ae3 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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--; } 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_walk(struct ea_walk_state *s, uint id, uint max); int ea_get_int(ea_list *, unsigned ea, int def); void ea_dump(ea_list *); void ea_sort(ea_list *); /* Sort entries in all sub-lists */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 938b2b44..c5537208 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -307,6 +307,82 @@ ea_find(ea_list *e, unsigned id) 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 * @e: attribute list diff --git a/sysdep/bsd/krt-sys.h b/sysdep/bsd/krt-sys.h index 9c0d4972..2c6e35c5 100644 --- a/sysdep/bsd/krt-sys.h +++ b/sysdep/bsd/krt-sys.h @@ -44,5 +44,7 @@ struct krt_state { 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 diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index 7e97968a..e32e4fe1 100644 --- a/sysdep/linux/krt-sys.h +++ b/sysdep/linux/krt-sys.h @@ -32,6 +32,59 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; } /* 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 struct krt_params { diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index b0e35151..f8137e23 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -10,7 +10,12 @@ CF_HDR 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 @@ -24,8 +29,37 @@ 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_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); }) +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_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 diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 48dd8bab..72837ce0 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -238,21 +238,24 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) return 1; } -void -nl_add_attr(struct nlmsghdr *h, unsigned bufsize, unsigned code, - void *data, unsigned dlen) +struct rtattr * +nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen) { - unsigned len = RTA_LENGTH(dlen); - unsigned pos = NLMSG_ALIGN(h->nlmsg_len); - struct rtattr *a; + uint pos = NLMSG_ALIGN(h->nlmsg_len); + uint len = RTA_LENGTH(dlen); if (pos + len > bufsize) 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_len = len; h->nlmsg_len = pos + len; - memcpy(RTA_DATA(a), data, dlen); + + if (dlen > 0) + memcpy(RTA_DATA(a), data, dlen); + + return a; } 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)); } -#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) +static inline struct rtattr * +nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code) { - 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)); + return nl_add_attr(h, bufsize, code, NULL, 0); } +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 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; - + struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH); + for (; nh; nh = nh->next) - { - len += RTNH_SIZE; - if (pos + len > bufsize) - bug("nl_add_multipath: packet buffer overflow"); + { + struct rtnexthop *rtnh = nl_open_nexthop(h, bufsize); - add_mpnexthop(buf, nh->gw, nh->iface->index, nh->weight); - buf += RTNH_SIZE; - } + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = nh->weight; + rtnh->rtnh_ifindex = nh->iface->index; - rt->rta_type = RTA_MULTIPATH; - rt->rta_len = len; - h->nlmsg_len = pos + len; + nl_add_attr_u32(h, bufsize, RTA_GATEWAY, nh->gw); + + nl_close_nexthop(h, rtnh); + } + + nl_close_attr(h, a); } - static struct mpnh * 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; } +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 @@ -617,7 +671,7 @@ nh_bufsize(struct mpnh *nh) { int rv = 0; for (; nh != NULL; nh = nh->next) - rv += RTNH_SIZE; + rv += RTNH_LENGTH(RTA_LENGTH(sizeof(ip_addr))); return rv; } @@ -630,7 +684,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) struct { struct nlmsghdr h; struct rtmsg r; - char buf[128 + nh_bufsize(a->nexthops)]; + char buf[128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops)]; } r; 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; 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)) - metric = ea->u.data; - if (metric != 0) - nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric); + nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data); if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) 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)) 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 */ switch (a->dest) @@ -834,7 +899,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) net->n.prefix, net->n.pxlen); return; } - + break; } @@ -896,7 +961,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) e->u.krt.type = i->rtm_type; 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 e->u.krt.metric = 0; @@ -932,6 +997,38 @@ nl_parse_route(struct nlmsghdr *h, int scan) 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) krt_got_route(p, e); 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; } +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 diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index efdf4bdd..cfb623ce 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -1191,7 +1191,7 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src) } static int -krt_get_attr(eattr * a, byte * buf, int buflen UNUSED) +krt_get_attr(eattr *a, byte *buf, int buflen) { switch (a->id) { @@ -1203,16 +1203,8 @@ krt_get_attr(eattr * a, byte * buf, int buflen UNUSED) bsprintf(buf, "metric"); return GA_NAME; - case EA_KRT_PREFSRC: - bsprintf(buf, "prefsrc"); - return GA_NAME; - - case EA_KRT_REALM: - bsprintf(buf, "realm"); - return GA_NAME; - default: - return GA_UNKNOWN; + return krt_sys_get_attr(a, buf, buflen); } } diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index f0fd6261..1940cbcd 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -28,8 +28,6 @@ struct kif_proto; #define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0) #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 */ @@ -131,6 +129,7 @@ void krt_sys_copy_config(struct krt_config *, struct krt_config *); int krt_capable(rte *e); 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); +int krt_sys_get_attr(eattr *a, byte *buf, int buflen); /* kif sysdep */