0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-05 08:31:53 +00:00

Flowspec: action formatting

This commit is contained in:
Jan Moskyto Matejka 2017-07-04 14:49:37 +02:00
parent 5f507ad42d
commit ea8bf6d8fc
8 changed files with 262 additions and 9 deletions

View File

@ -39,6 +39,7 @@ CF_DECLS
uint i;
u32 i32;
u64 i64;
float fl;
ip_addr a;
ip4_addr ip4;
ip6_addr ip6;

View 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, [[ ]], [[,]]))

View 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,)

View 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;
}
}

View 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

View 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;

View 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;

View 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)); } ;