From 315f23a0470112ced04badbb117bc7854ee53e06 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 9 May 2015 18:50:15 +0200 Subject: [PATCH] Add bitfield route attribute type --- filter/filter.c | 82 ++++++++++++++++++++++++++++++++++--------------- nest/route.h | 5 ++- nest/rt-attr.c | 29 +++++++++++++++++ 3 files changed, 90 insertions(+), 26 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index a4f12330..3b14fc0c 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -462,7 +462,7 @@ static int f_flags; static inline void f_rte_cow(void) { - *f_rte = rte_cow(*f_rte); + *f_rte = rte_cow(*f_rte); } /* @@ -479,7 +479,7 @@ f_rta_cow(void) /* Store old rta to free it later */ f_old_rta = (*f_rte)->attrs; - /* + /* * Alloc new rta, do shallow copy and update rte. Fields eattrs * and nexthops of rta are shared with f_old_rta (they will be * copied when the cached rta will be obtained at the end of @@ -516,6 +516,9 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; #define ACCESS_RTE \ do { if (!f_rte) runtime("No route to access"); } while (0) +#define BITFIELD_MASK(what) \ + (1u << (what->a2.i >> 24)) + /** * interpret * @what: filter to interpret @@ -526,7 +529,7 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; * Each instruction has 4 fields: code (which is instruction code), * aux (which is extension to instruction code, typically type), * arg1 and arg2 - arguments. Depending on instruction, arguments - * are either integers, or pointers to instruction trees. Common + * are either integers, or pointers to instruction trees. Common * instructions like +, that have two expressions as arguments use * TWOARGS macro to get both of them evaluated. * @@ -585,7 +588,7 @@ interpret(struct f_inst *what) default: runtime( "Usage of unknown type" ); } break; - + case '&': case '|': ARG(v1, a1.p); @@ -625,7 +628,7 @@ interpret(struct f_inst *what) if (v1.type == T_INT) { ipv4_used = 0; key = v1.val.i; - } + } else if (v1.type == T_QUAD) { ipv4_used = 1; key = v1.val.i; } @@ -724,7 +727,7 @@ interpret(struct f_inst *what) #endif runtime( "Assigning to variable of incompatible type" ); } - *vp = v2; + *vp = v2; break; /* some constants have value in a2, some in *a1.p, strange. */ @@ -864,12 +867,14 @@ interpret(struct f_inst *what) ACCESS_RTE; { eattr *e = NULL; + u16 code = what->a2.i; + if (!(f_flags & FF_FORCE_TMPATTR)) - e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); - if (!e) - e = ea_find( (*f_tmp_attrs), what->a2.i ); + e = ea_find((*f_rte)->attrs->eattrs, code); + if (!e) + e = ea_find((*f_tmp_attrs), code); if ((!e) && (f_flags & FF_FORCE_TMPATTR)) - e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); + e = ea_find((*f_rte)->attrs->eattrs, code); if (!e) { /* A special case: undefined int_set looks like empty int_set */ @@ -878,8 +883,9 @@ interpret(struct f_inst *what) res.val.ad = adata_empty(f_pool, 0); break; } + /* The same special case for ec_set */ - else if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) { + if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) { res.type = T_ECLIST; res.val.ad = adata_empty(f_pool, 0); break; @@ -912,6 +918,10 @@ interpret(struct f_inst *what) res.type = T_PATH; res.val.ad = e->u.ptr; break; + case EAF_TYPE_BITFIELD: + res.type = T_BOOL; + res.val.i = !!(e->u.data & BITFIELD_MASK(what)); + break; case EAF_TYPE_INT_SET: res.type = T_CLIST; res.val.ad = e->u.ptr; @@ -933,13 +943,15 @@ interpret(struct f_inst *what) ONEARG; { struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr)); + u16 code = what->a2.i; l->next = NULL; l->flags = EALF_SORTED; l->count = 1; - l->attrs[0].id = what->a2.i; + l->attrs[0].id = code; l->attrs[0].flags = 0; l->attrs[0].type = what->aux | EAF_ORIGINATED; + switch (what->aux & EAF_TYPE_MASK) { case EAF_TYPE_INT: if (v1.type != T_INT) @@ -978,6 +990,26 @@ interpret(struct f_inst *what) runtime( "Setting path attribute to non-path value" ); l->attrs[0].u.ptr = v1.val.ad; break; + case EAF_TYPE_BITFIELD: + if (v1.type != T_BOOL) + runtime( "Setting bit in bitfield attribute to non-bool value" ); + { + /* First, we have to find the old value */ + eattr *e = NULL; + if (!(f_flags & FF_FORCE_TMPATTR)) + e = ea_find((*f_rte)->attrs->eattrs, code); + if (!e) + e = ea_find((*f_tmp_attrs), code); + if ((!e) && (f_flags & FF_FORCE_TMPATTR)) + e = ea_find((*f_rte)->attrs->eattrs, code); + u32 data = e ? e->u.data : 0; + + if (v1.val.i) + l->attrs[0].u.data = data | BITFIELD_MASK(what); + else + l->attrs[0].u.data = data & ~BITFIELD_MASK(what);; + } + break; case EAF_TYPE_INT_SET: if (v1.type != T_CLIST) runtime( "Setting clist attribute to non-clist value" ); @@ -1073,7 +1105,7 @@ interpret(struct f_inst *what) res = interpret(what->a2.p); if (res.type == T_RETURN) return res; - res.type &= ~T_RETURN; + res.type &= ~T_RETURN; break; case P('c','v'): /* Clear local variables */ for (sym = what->a1.p; sym != NULL; sym = sym->aux2) @@ -1090,7 +1122,7 @@ interpret(struct f_inst *what) debug( "No else statement?\n"); break; } - } + } /* It is actually possible to have t->data NULL */ res = interpret(t->data); @@ -1184,10 +1216,10 @@ interpret(struct f_inst *what) runtime("Can't add set"); else if (!arg_set) res.val.ad = int_set_add(f_pool, v1.val.ad, n); - else + else res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad); break; - + case 'd': if (!arg_set) res.val.ad = int_set_del(f_pool, v1.val.ad, n); @@ -1209,7 +1241,7 @@ interpret(struct f_inst *what) { /* Extended community list */ int arg_set = 0; - + /* v2.val is either EC or EC-set */ if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) arg_set = 1; @@ -1226,10 +1258,10 @@ interpret(struct f_inst *what) runtime("Can't add set"); else if (!arg_set) res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec); - else + else res.val.ad = ec_set_union(f_pool, v1.val.ad, v2.val.ad); break; - + case 'd': if (!arg_set) res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec); @@ -1353,7 +1385,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; - case 'c': + case 'c': switch (f1->aux) { case T_PREFIX_SET: @@ -1381,7 +1413,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) return 0; break; - case 'V': + case 'V': if (strcmp((char *) f1->a2.p, (char *) f2->a2.p)) return 0; break; @@ -1399,12 +1431,12 @@ i_same(struct f_inst *f1, struct f_inst *f2) case 'r': ONEARG; break; case P('c','p'): ONEARG; break; case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */ - ONEARG; + ONEARG; if (!i_same(f1->a2.p, f2->a2.p)) - return 0; + return 0; f2->a2.p = f1->a2.p; break; - case P('c','v'): break; /* internal instruction */ + case P('c','v'): break; /* internal instruction */ case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break; case P('i','M'): TWOARGS; break; case P('A','p'): TWOARGS; break; @@ -1414,7 +1446,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case P('R','C'): TWOARGS; /* Does not really make sense - ROA check resuls may change anyway */ - if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, + if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, ((struct f_inst_roa_check *) f2)->rtc->name)) return 0; break; diff --git a/nest/route.h b/nest/route.h index 5ee04a30..fccc571b 100644 --- a/nest/route.h +++ b/nest/route.h @@ -417,13 +417,15 @@ typedef struct eattr { #define EA_CODE_MASK 0xffff #define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */ +#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */ #define EAF_TYPE_MASK 0x0f /* Mask with this to get type */ -#define EAF_TYPE_INT 0x01 /* 32-bit signed integer number */ +#define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */ #define EAF_TYPE_OPAQUE 0x02 /* Opaque byte string (not filterable) */ #define EAF_TYPE_IP_ADDRESS 0x04 /* IP address */ #define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */ #define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */ +#define EAF_TYPE_BITFIELD 0x09 /* 32-bit embedded bitfield */ #define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */ #define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */ #define EAF_TYPE_UNDEF 0x0f /* `force undefined' entry */ @@ -469,6 +471,7 @@ void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffe int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */ unsigned int ea_hash(ea_list *e); /* Calculate 16-bit hash value */ ea_list *ea_append(ea_list *to, ea_list *what); +void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max); int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */ static inline int mpnh_same(struct mpnh *x, struct mpnh *y) diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 09691bf1..938b2b44 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -563,6 +563,32 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED) return GA_UNKNOWN; } +void +ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max) +{ + byte *bound = buf + bufsize - 32; + u32 data = a->u.data; + int i; + + for (i = min; i < max; i++) + if ((data & (1u << i)) && names[i]) + { + if (buf > bound) + { + strcpy(buf, " ..."); + return; + } + + buf += bsprintf(buf, " %s", names[i]); + data &= ~(1u << i); + } + + if (data) + bsprintf(buf, " %08x", data); + + return; +} + static inline void opaque_format(struct adata *ad, byte *buf, unsigned int size) { @@ -665,6 +691,9 @@ ea_show(struct cli *c, eattr *e) case EAF_TYPE_AS_PATH: as_path_format(ad, pos, end - pos); break; + case EAF_TYPE_BITFIELD: + bsprintf(pos, "%08x", e->u.data); + break; case EAF_TYPE_INT_SET: ea_show_int_set(c, ad, 1, pos, buf, end); return;