uint i;
u32 i32;
u64 i64;
+ float fl;
ip_addr a;
ip4_addr ip4;
ip6_addr ip6;
# 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, [[ ]], [[,]]))
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,)
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
*/
{
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;
+ }
+}
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
#include "nest/bird.h"
#include "string.h"
+#include <stdio.h>
#include <errno.h>
#include "nest/iface.h"
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() */
continue;
}
+ percent = fmt;
+
/* process flags */
flags = 0;
repeat:
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;
#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)
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)
{
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);
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)
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;
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)
{
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
;
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);
}
;
| 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:
| '{' 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)); } ;