]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
KRT: Add support for plenty of kernel route metrics
authorOndrej Zajicek <santiago@crfreenet.org>
Tue, 12 May 2015 14:42:22 +0000 (16:42 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Tue, 12 May 2015 14:42:22 +0000 (16:42 +0200)
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
nest/route.h
nest/rt-attr.c
sysdep/bsd/krt-sys.h
sysdep/linux/krt-sys.h
sysdep/linux/netlink.Y
sysdep/linux/netlink.c
sysdep/unix/krt.c
sysdep/unix/krt.h

index fc5fc9aefdabd56dc073b5a065a9519f6703abb0..1c2dda4b872fc8860f692eb428b00b89a5583a57 100644 (file)
@@ -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.
index fccc571b6d463f7d23e8220af454bac4a79d1fcd..87f10ae3776cb32b9b24ac32cd36a0cf2db4c47c 100644 (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--; }
 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 */
index 938b2b44623d03b6e4c6f9f8df96f0eb3046da5a..c55372082953ef64761cde699437c8720fc67a7d 100644 (file)
@@ -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
index 9c0d4972a7020c9977897691afba817aad31feb3..2c6e35c537a1453c7c6b0ebe85a2c44ba3d1b2e3 100644 (file)
@@ -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
index 7e97968a41dbdb49d920b1dc463f5e769089f5d9..e32e4fe1c2686b5840c92d16eb2ca7caeedbd34b 100644 (file)
@@ -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 {
index b0e35151b84a123c981da2c8584a507dbe78cb1c..f8137e23f748daa66e479e4443c0492b6b3e9d85 100644 (file)
@@ -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
 
index 48dd8babb264a331c2fb2dfb7bed91c6dda8b587..72837ce0084a19da532a856a56bc934e45e8c4d6 100644 (file)
@@ -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
index efdf4bdd2361dbee5025b46f2280c34eeafdb81d..cfb623ce443f87918a3d798f59596627c1a9b7ac 100644 (file)
@@ -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);
   }
 }
 
index f0fd6261d91f53ac3eef5ecf03fb93ab0f798507..1940cbcd764b77bbfb30811b14ec3caa9bd0abd1 100644 (file)
@@ -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 */