]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Flowspec: action formatting
authorJan Moskyto Matejka <mq@ucw.cz>
Tue, 4 Jul 2017 12:49:37 +0000 (14:49 +0200)
committerJan Moskyto Matejka <mq@ucw.cz>
Tue, 4 Jul 2017 12:49:37 +0000 (14:49 +0200)
conf/confbase.Y
conf/gen_keywords.m4
conf/gen_parser.m4
lib/flowspec.c
lib/flowspec.h
lib/printf.c
nest/rt-show.c
proto/static/config.Y

index 901ca2b2bff428173123ca49d3b9c4e2e516ef0d..fc4945b80f297ab44e7076e481f6d1662545c85e 100644 (file)
@@ -39,6 +39,7 @@ CF_DECLS
   uint i;
   u32 i32;
   u64 i64;
+  float fl;
   ip_addr a;
   ip4_addr ip4;
   ip6_addr ip6;
index cf3fb58ecb37bb6137a0e89d869294172e40ecbd..3566338c49c289c1310f85988fcf14efee11f5ba 100644 (file)
@@ -25,9 +25,14 @@ m4_define(CF_DEFINES, `m4_divert(-1)')
 # Keywords are translated to C initializers
 m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1, NULL },
 m4_divert(-1)')
+m4_define(CF_handle_kw_cs, `m4_divert(1){ "$1", $1, NULL },
+m4_divert(-1)')
 m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)CF_handle_kw($1)]])')
+m4_define(CF_keywd_cs, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)CF_handle_kw_cs($1)]])')
 m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
 )DNL')
+m4_define(CF_KEYWORDS_CS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd_cs]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
+)DNL')
 
 # CLI commands generate keywords as well
 m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
index 00b55023766177f55c86e5c1ade9bdcc3f039027..80e6e63a1f29dbb928efdd100e86bfd232f04143 100644 (file)
@@ -33,6 +33,8 @@ m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)')
 m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_define([[CF_toks]],CF_toks $1)]])')
 m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
 )DNL')
+m4_define(CF_KEYWORDS_CS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
+)DNL')
 
 # Dynamic syntax rules
 m4_define(CF_dyn_rules,)
index dd6a2cbabaa4c8f64708302f41de20a7d2a1c1fb..cdf80ba3763ed60ae7dccc70b82bc283b3e388fb 100644 (file)
@@ -80,6 +80,22 @@ flow_type_str(enum flow_type type, int ipv6)
   return ipv6 ? flow6_type_str[type] : flow4_type_str[type];
 }
 
+const char *
+flow_action_str(uint action)
+{
+#define C(c, s) case c: return s
+  switch(action) {
+    C(FLOW_ACTION_TRAFFIC_BYTERATE, "byterate");
+    C(FLOW_ACTION_TRAFFIC_ACTION, "action");
+    C(FLOW_ACTION_REDIRECT_AS2, "redirect");
+    C(FLOW_ACTION_REDIRECT_AS4, "redirect");
+    C(FLOW_ACTION_REDIRECT_IP4, "redirect");
+    C(FLOW_ACTION_TRAFFIC_MARKING, "mark");
+  }
+#undef C
+  return NULL;
+}
+
 /*
  *     Length
  */
@@ -1174,3 +1190,87 @@ flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f, const char *sep)
 {
   return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow6), 1, sep);
 }
+
+/* Action */
+
+u64
+flow_action_encode_byterate(u16 asn, float rate)
+{
+  u32 urate;
+  memcpy(&urate, &rate, 4);
+  return flow_action_encode(FLOW_ACTION_TRAFFIC_BYTERATE, (((u64) asn) << 32) | ((u64) urate));
+}
+
+u64
+flow_action_encode_redirect(u64 asn, u64 val)
+{
+  if (asn < 0x10000)
+    return (((u64) FLOW_ACTION_REDIRECT_AS2) << 48) | (asn << 32) | (val & 0xffffffff);
+  else
+    return (((u64) FLOW_ACTION_REDIRECT_AS4) << 48) | (asn << 16) | (val & 0xffff);
+}
+
+
+/**
+ * Flow actions are encoded as BGP Extended Community.
+ */
+uint
+flow_action_format_part(char *buf, uint blen, u64 ec)
+{
+  int type = (ec >> 48);
+  switch (type) {
+    case FLOW_ACTION_TRAFFIC_BYTERATE:
+      {
+       float rate;
+       u32 urate = (ec & 0xffffffff);
+       memcpy(&rate, &urate, sizeof(rate));
+       int asn = (ec >> 32) & 0xffff;
+
+       rate *= 8; /* Convert from byterate to bitrate */
+       const char *rs;
+       if (rate < 2) {
+         rs = "mbps"; rate *= 1000;
+       } else if (rate < 2000) {
+         rs = "bps";
+       } else if (rate < 2000000) {
+         rs = "kbps"; rate /= 1000;
+       } else if (rate < 2000000000) {
+         rs = "Mbps"; rate /= 1000000;
+       } else if (rate < 2000000000000) {
+         rs = "Gbps"; rate /= 1000000000;
+       } else {
+         rs = "Tbps"; rate /= 1000000000000;
+       }
+
+       return bsnprintf(buf, blen, "rate %.3f %s asn %u;", rate, rs, asn);
+      }
+    case FLOW_ACTION_TRAFFIC_ACTION:
+      {
+       int total = 0;
+       if (ec & FLOW_ACTION_LAST) {
+         int cnt = bsnprintf(buf, blen, "last;");
+         if (cnt < 0)
+           return -1;
+         ADVANCE(buf, blen, cnt);
+         total += cnt;
+       }
+
+       if (ec & FLOW_ACTION_SAMPLE) {
+         int cnt = bsnprintf(buf, blen, "sample;");
+         if (cnt < 0)
+           return -1;
+         ADVANCE(buf, blen, cnt);
+         total += cnt;
+       }
+       return total;
+      }
+    case FLOW_ACTION_REDIRECT_AS2:
+      return bsnprintf(buf, blen, "rt %d,%d;", ((ec >> 32) & 0xffff), (ec & 0xffffffff));
+    case FLOW_ACTION_REDIRECT_AS4:
+      return bsnprintf(buf, blen, "rt %d,%d;", ((ec >> 16) & 0xffffffff), (ec & 0xffff));
+    case FLOW_ACTION_TRAFFIC_MARKING:
+      return bsnprintf(buf, blen, "dscp %d;", (ec & 0x3f));
+    default:
+      return 0;
+  }
+}
index 90438b5b3dc73bef4eaf249610c39f7dbd5465dc..b8e520285428adef6fafad7969c9cb12fbf76581 100644 (file)
@@ -49,6 +49,33 @@ enum flow_type {
 
 const char *flow_type_str(enum flow_type type, int ipv6);
 
+#define FLOW_ACTION_TRAFFIC_BYTERATE   0x8006
+#define FLOW_ACTION_TRAFFIC_ACTION     0x8007
+#define FLOW_ACTION_REDIRECT_AS2       0x8008
+#define FLOW_ACTION_REDIRECT_IP4       0x8108  /* Not supported yet */
+#define FLOW_ACTION_REDIRECT_AS4       0x8208
+#define FLOW_ACTION_TRAFFIC_MARKING    0x8009
+
+#define FLOW_ACTION_LAST               0x000000000001ULL
+#define FLOW_ACTION_SAMPLE             0x000000000002ULL
+
+const char *flow_action_str(uint action);
+
+static inline u64 flow_action_encode(u16 key, u64 value)
+{ return value | (((u64) key) << 48); }
+u64 flow_action_encode_byterate(u16 asn, float rate);
+static inline u64 flow_action_encode_bitrate(u16 asn, float rate)
+{ return flow_action_encode_byterate(asn, rate/8); }
+static inline u64 flow_action_encode_sample(void)
+{ return flow_action_encode(FLOW_ACTION_TRAFFIC_ACTION, FLOW_ACTION_SAMPLE); }
+static inline u64 flow_action_encode_last(void)
+{ return flow_action_encode(FLOW_ACTION_TRAFFIC_ACTION, FLOW_ACTION_LAST); }
+u64 flow_action_encode_redirect(u64 asn, u64 val);
+static inline u64 flow_action_encode_dscp(u64 dscp)
+{ return (dscp & 0x3f) | (((u64) FLOW_ACTION_TRAFFIC_MARKING) << 48); }
+
+
+uint flow_action_format_part(char *buf, uint blen, u64 ec);
 
 /*
  *     Length
index 8e3cbbcfcc0c3125a5dd9c7945d905c6bb3d5d12..af795c21912ff67ae36eae330bfd7ce35b2fdea5 100644 (file)
@@ -10,6 +10,7 @@
 #include "nest/bird.h"
 #include "string.h"
 
+#include <stdio.h>
 #include <errno.h>
 
 #include "nest/iface.h"
@@ -143,6 +144,8 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
        const char *s;
        char ipbuf[NET_MAX_TEXT_LENGTH+1];
        struct iface *iface;
+       const char *percent;
+       char fmtbuf[strlen(fmt)];
 
        int flags;              /* flags to number() */
 
@@ -159,6 +162,8 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
                        continue;
                }
 
+               percent = fmt;
+
                /* process flags */
                flags = 0;
                repeat:
@@ -360,6 +365,12 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
                        s = ipbuf;
                        goto str;
 
+               /* float number formats delegated to stdio */
+               case 'f':
+                       memcpy(fmtbuf, percent, (fmt - percent) + 1);
+                       snprintf(ipbuf, sizeof(ipbuf), fmtbuf, va_arg(args, double));
+                       s = ipbuf;
+                       goto str;
                /* integer number formats - set up the flags and "break" */
                case 'o':
                        base = 8;
index 981f74e20e3fb1e0edf103ea061acfc9e8c93a13..c62ff91395a6604cfaf991632230a9b2de3152e8 100644 (file)
@@ -16,6 +16,7 @@
 #include "nest/iface.h"
 #include "filter/filter.h"
 #include "lib/flowspec.h"
+#include "proto/bgp/bgp.h"
 
 static void
 rt_show_table(struct cli *c, struct rt_show_data *d)
@@ -29,6 +30,46 @@ rt_show_table(struct cli *c, struct rt_show_data *d)
   d->last_table = d->tab;
 }
 
+static void
+rt_flow_action_format(char *buf, uint blen, rte *e)
+{
+  int cnt;
+  cnt = bsnprintf(buf, blen, "action {\n\t");
+  ADVANCE(buf, blen, cnt);
+
+  eattr *ea = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_EXT_COMMUNITY));
+  if (!ea) goto noaction;
+
+  u32 *data = int_set_get_data(ea->u.ptr);
+  int max = int_set_get_size(ea->u.ptr);
+  int empty = 1;
+  for (int i=0; i<max; i++) {
+    cnt = flow_action_format_part(buf, blen - 4, ec_get(data, i));
+    if (cnt < 0) {
+      const char finish[] = " ... }";
+      memcpy(buf + blen - MAX(blen, sizeof(finish)), finish, sizeof(finish));
+      return;
+    }
+
+    if (!cnt)
+      continue;
+
+    buf[cnt] = '\n';
+    buf[cnt+1] = '\t';
+    ADVANCE(buf, blen, cnt+2);
+    empty = 0;
+  }
+
+  if (empty) {
+noaction:
+    buf[-2] = ' ';
+    buf[-1] = '}';
+  } else {
+    buf[-1] = '}';
+    buf[0] = 0;
+  }
+}
+
 static void
 rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa)
 {
@@ -40,6 +81,14 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
   void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs);
   struct nexthop *nh;
 
+  byte ai[1024] = {0};
+  switch (e->net->n.addr->type) {
+    case NET_FLOW4:
+    case NET_FLOW6:
+      rt_flow_action_format(ai, sizeof(ai) - 1, e);
+      break;
+  }
+
   tm_format_datetime(tm, &config->tf_route, e->lastmod);
   if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
     bsprintf(from, " from %I", a->from);
@@ -64,7 +113,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
   if (d->last_table != d->tab)
     rt_show_table(c, d);
 
-  cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
+  cli_printf(c, -1007, "%-18s%s %s [%s %s%s]%s%s", ia, ai, rta_dest_name(a->dest),
             a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
 
   if (a->dest == RTD_UNICAST)
@@ -103,8 +152,12 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d, const uint addr_type)
   switch (addr_type)
     {
       case NET_FLOW4:
-       flow4_net_format(ia, sizeof(ia), (net_addr_flow4 *) n->n.addr, "\n\t");
-       break;
+       {
+         uint cnt = flow4_net_format(ia, sizeof(ia), (net_addr_flow4 *) n->n.addr, "\n\t");
+         ia[cnt-2] = '}';
+         ia[cnt-1] = '\n';
+         break;
+       }
       case NET_FLOW6:
        flow6_net_format(ia, sizeof(ia), (net_addr_flow6 *) n->n.addr, "\n\t");
        break;
index 66ae3c980e3808fd23f48323e97f7cda48ef55d3..bccd0c4c6ca587b92bc0bd481a84158254b2f3cb 100644 (file)
@@ -34,6 +34,17 @@ static_nexthop_new(void)
   return nh;
 };
 
+static void
+static_route_start(net_addr *n)
+{
+  this_srt = cfg_allocz(sizeof(struct static_route));
+  add_tail(&STATIC_CFG->routes, &this_srt->n);
+  this_srt->net = n;
+  this_srt_last_cmd = &(this_srt->cmds);
+  this_srt->mp_next = NULL;
+  this_snh = NULL;
+}
+
 static void
 static_route_finish(void)
 {
@@ -41,10 +52,30 @@ static_route_finish(void)
     cf_error("Unexpected or missing nexthop/type");
 }
 
+static void
+static_flow_action(u64 ec)
+{
+  NEW_F_VAL;
+
+  val->type = T_EC; val->val.ec = ec;
+  struct f_inst *fic = f_new_inst();
+  fic->code = 'C'; fic->a1.p = val;
+  *this_srt_last_cmd = f_generate_complex(
+    P('C','a'), 'a',
+    f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(EAP_BGP, BA_EXT_COMMUNITY)),
+    fic
+  );
+  this_srt_last_cmd = &((*this_srt_last_cmd)->next);
+}
+
 CF_DECLS
 
+%type <fl> float_rate
+
 CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
 CF_KEYWORDS(WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
+CF_KEYWORDS(RATE, SAMPLE, LAST, DSCP)
+CF_KEYWORDS_CS(mBps, mbps, Bps, bps, kBps, kbps, MBps, Mbps, GBps, Gbps, TBps, Tbps)
 
 
 CF_GRAMMAR
@@ -102,12 +133,9 @@ stat_nexthops:
 ;
 
 stat_route0: ROUTE net_any {
-     this_srt = cfg_allocz(sizeof(struct static_route));
-     add_tail(&STATIC_CFG->routes, &this_srt->n);
-     this_srt->net = $2;
-     this_srt_last_cmd = &(this_srt->cmds);
-     this_srt->mp_next = NULL;
-     this_snh = NULL;
+     if (net_type_match($2, NB_FLOW))
+       cf_error("Flowspec rules are not routes. Apologize!");
+     static_route_start($2);
   }
  ;
 
@@ -128,6 +156,7 @@ stat_route:
  | stat_route0 BLACKHOLE       { this_srt->dest = RTD_BLACKHOLE; }
  | stat_route0 UNREACHABLE     { this_srt->dest = RTD_UNREACHABLE; }
  | stat_route0 PROHIBIT                { this_srt->dest = RTD_PROHIBIT; }
+ | net_flow_ { static_route_start($1); } flowspec_action 
  ;
 
 stat_route_item:
@@ -144,6 +173,31 @@ stat_route_opt_list:
  | '{' stat_route_opts '}'
  ;
 
+flowspec_action: ACTION '{' flowspec_action0 '}' ;
+
+flowspec_action0:
+  /* empty */
+ | flowspec_action0 RATE float_rate ';' { static_flow_action(flow_action_encode_byterate(0, $3)); }
+ | flowspec_action0 SAMPLE ';' { static_flow_action(flow_action_encode_sample()); }
+ | flowspec_action0 LAST ';' { static_flow_action(flow_action_encode_last()); }
+ | flowspec_action0 RT cnum ',' cnum ';' { static_flow_action(flow_action_encode_redirect($3, $5)); }
+ | flowspec_action0 DSCP NUM ';' { static_flow_action(flow_action_encode_dscp($3)); }
+;
+
+float_rate:
+   NUM mBps { $$ = $1 / 1000.0; }
+ | NUM mbps { $$ = $1 / 8000.0; }
+ | NUM Bps { $$ = $1; }
+ | NUM bps { $$ = $1 / 8.0; }
+ | NUM kBps { $$ = 1000.0 * $1; }
+ | NUM kbps { $$ = 1000.0 * $1 / 8.0; }
+ | NUM MBps { $$ = 1000000.0 * $1; }
+ | NUM Mbps { $$ = 1000000.0 * $1 / 8.0; }
+ | NUM GBps { $$ = 1000000000.0 * $1; }
+ | NUM Gbps { $$ = 1000000000.0 * $1 / 8.0; }
+ | NUM TBps { $$ = 1000000000000.0 * $1; }
+ | NUM Tbps { $$ = 1000000000000.0 * $1 / 8.0; }
+;
 
 CF_CLI(SHOW STATIC, optsym, [<name>], [[Show details of static protocol]])
 { static_show(proto_get_named($3, &proto_static)); } ;