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:
parent
5f507ad42d
commit
ea8bf6d8fc
@ -39,6 +39,7 @@ CF_DECLS
|
||||
uint i;
|
||||
u32 i32;
|
||||
u64 i64;
|
||||
float fl;
|
||||
ip_addr a;
|
||||
ip4_addr ip4;
|
||||
ip6_addr ip6;
|
||||
|
@ -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, [[ ]], [[,]]))
|
||||
|
@ -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,)
|
||||
|
100
lib/flowspec.c
100
lib/flowspec.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
11
lib/printf.c
11
lib/printf.c
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)); } ;
|
||||
|
Loading…
Reference in New Issue
Block a user