summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/bird.sgml2
-rw-r--r--nest/route.h6
-rw-r--r--nest/rt-attr.c76
-rw-r--r--sysdep/bsd/krt-sys.h2
-rw-r--r--sysdep/linux/krt-sys.h53
-rw-r--r--sysdep/linux/netlink.Y40
-rw-r--r--sysdep/linux/netlink.c235
-rw-r--r--sysdep/unix/krt.c12
-rw-r--r--sysdep/unix/krt.h3
9 files changed, 366 insertions, 63 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:
<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.
+ outgoing packets. Has to be one of the IP addresses of the router.
<tag>int <cf/krt_realm/</tag> (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
@@ -308,6 +308,82 @@ ea_find(ea_list *e, unsigned id)
}
/**
+ * 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
* @id: attribute ID
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,48 +271,58 @@ 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 struct rtattr *
+nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code)
+{
+ return nl_add_attr(h, bufsize, code, NULL, 0);
+}
static inline void
-add_mpnexthop(char *buf, ip_addr ipa, unsigned iface, unsigned char weight)
+nl_close_attr(struct nlmsghdr *h, struct rtattr *a)
{
- 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));
+ 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 */