From 66dbdbd993115c57acafdb776d2165d0b4a90a45 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sat, 1 Oct 2016 12:50:29 +0200 Subject: [PATCH 01/37] BGP: Support for large communities Add support for large communities (draft-ietf-idr-large-community), 96bit alternative to RFC 1997 communities. Thanks to Matt Griswold for the original patch. --- filter/config.Y | 43 +++++++++++- filter/filter.c | 171 ++++++++++++++++++++++++++++++++++++++++++++- filter/filter.h | 18 ++++- filter/test.conf | 17 +++++ nest/a-set.c | 127 ++++++++++++++++++++++++++++++++- nest/attrs.h | 32 +++++++++ nest/route.h | 5 +- nest/rt-attr.c | 15 ++++ proto/bgp/attrs.c | 42 ++++++++++- proto/bgp/bgp.h | 1 + proto/bgp/config.Y | 4 +- 11 files changed, 462 insertions(+), 13 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index 9fffd651..79e274ab 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -36,6 +36,7 @@ f_valid_set_type(int type) case T_ENUM: case T_IP: case T_EC: + case T_LC: return 1; default: @@ -148,6 +149,9 @@ f_generate_empty(struct f_inst *dyn) case EAF_TYPE_EC_SET: e->aux = T_ECLIST; break; + case EAF_TYPE_LC_SET: + e->aux = T_LCLIST; + break; default: cf_error("Can't empty that attribute"); } @@ -268,14 +272,44 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) return rv; } +static inline struct f_inst * +f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3) +{ + struct f_inst *rv; + + if ((t1->code == 'c') && (t2->code == 'c') && (t3->code == 'c')) { + if ((t1->aux != T_INT) || (t2->aux != T_INT) || (t3->aux != T_INT)) + cf_error( "LC - Can't operate with value of non-integer type in tuple constructor"); + + rv = f_new_inst(); + rv->code = 'C'; + + NEW_F_VAL; + rv->a1.p = val; + val->type = T_LC; + val->val.lc = (lcomm) { t1->a2.i, t2->a2.i, t3->a2.i }; + } + else + { + rv = cfg_allocz(sizeof(struct f_inst3)); + rv->lineno = ifs->lino; + rv->code = P('m','l'); + rv->a1.p = t1; + rv->a2.p = t2; + INST3(rv).p = t3; + } + + return rv; +} + CF_DECLS CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, ACCEPT, REJECT, ERROR, QUITBIRD, - INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, - SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, + INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, LC, + SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST, IF, THEN, ELSE, CASE, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX, @@ -327,17 +361,20 @@ type: | PAIR { $$ = T_PAIR; } | QUAD { $$ = T_QUAD; } | EC { $$ = T_EC; } + | LC { $$ = T_LC; } | STRING { $$ = T_STRING; } | BGPMASK { $$ = T_PATH_MASK; } | BGPPATH { $$ = T_PATH; } | CLIST { $$ = T_CLIST; } | ECLIST { $$ = T_ECLIST; } + | LCLIST { $$ = T_LCLIST; } | type SET { switch ($1) { case T_INT: case T_PAIR: case T_QUAD: case T_EC: + case T_LC: case T_IP: $$ = T_SET; break; @@ -657,6 +694,7 @@ constant: constructor: '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); } | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); } + | '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); } ; @@ -767,6 +805,7 @@ term: | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; } | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; } + | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; } | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; } | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; } diff --git a/filter/filter.c b/filter/filter.c index 2f5f00d8..510303a7 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -106,6 +106,18 @@ u64_cmp(u64 i1, u64 i2) return (int)(i1 > i2) - (int)(i1 < i2); } +static inline int +lcomm_cmp(lcomm v1, lcomm v2) +{ + if (v1.asn != v2.asn) + return (v1.asn > v2.asn) ? 1 : -1; + if (v1.ldp1 != v2.ldp1) + return (v1.ldp1 > v2.ldp1) ? 1 : -1; + if (v1.ldp2 != v2.ldp2) + return (v1.ldp2 > v2.ldp2) ? 1 : -1; + return 0; +} + /** * val_compare - compare two values * @v1: first value @@ -149,6 +161,8 @@ val_compare(struct f_val v1, struct f_val v2) return uint_cmp(v1.val.i, v2.val.i); case T_EC: return u64_cmp(v1.val.ec, v2.val.ec); + case T_LC: + return lcomm_cmp(v1.val.lc, v2.val.lc); case T_IP: return ipa_compare(v1.val.px.ip, v2.val.px.ip); case T_PREFIX: @@ -214,6 +228,7 @@ val_same(struct f_val v1, struct f_val v2) case T_PATH: case T_CLIST: case T_ECLIST: + case T_LCLIST: return adata_same(v1.val.ad, v2.val.ad); case T_SET: return same_tree(v1.val.t, v2.val.t); @@ -266,6 +281,10 @@ static inline int eclist_set_type(struct f_tree *set) { return set->from.type == T_EC; } +static inline int +lclist_set_type(struct f_tree *set) +{ return set->from.type == T_LC; } + static int clist_match_set(struct adata *clist, struct f_tree *set) { @@ -311,6 +330,30 @@ eclist_match_set(struct adata *list, struct f_tree *set) return 0; } +static int +lclist_match_set(struct adata *list, struct f_tree *set) +{ + if (!list) + return 0; + + if (!lclist_set_type(set)) + return CMP_ERROR; + + struct f_val v; + u32 *l = int_set_get_data(list); + int len = int_set_get_size(list); + int i; + + v.type = T_LC; + for (i = 0; i < len; i += 3) { + v.val.lc = lc_get(l, i); + if (find_tree(set, v)) + return 1; + } + + return 0; +} + static struct adata * clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos) { @@ -380,6 +423,38 @@ eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int po return res; } +static struct adata * +lclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos) +{ + if (!list) + return NULL; + + int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ + struct f_val v; + + int len = int_set_get_size(list); + u32 *l = int_set_get_data(list); + u32 tmp[len]; + u32 *k = tmp; + int i; + + v.type = T_LC; + for (i = 0; i < len; i += 3) { + v.val.lc = lc_get(l, i); + /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ + if ((tree ? !!find_tree(set.val.t, v) : lc_set_contains(set.val.ad, v.val.lc)) == pos) + k = lc_copy(k, l+i); + } + + int nl = (k - tmp) * 4; + if (nl == list->length) + return list; + + struct adata *res = adata_empty(pool, nl); + memcpy(res->data, tmp, nl); + return res; +} + /** * val_in_range - implement |~| operator * @v1: element @@ -407,6 +482,9 @@ val_in_range(struct f_val v1, struct f_val v2) if ((v1.type == T_EC) && (v2.type == T_ECLIST)) return ec_set_contains(v2.val.ad, v1.val.ec); + if ((v1.type == T_LC) && (v2.type == T_LCLIST)) + return lc_set_contains(v2.val.ad, v1.val.lc); + if ((v1.type == T_STRING) && (v2.type == T_STRING)) return patmatch(v2.val.s, v1.val.s); @@ -433,6 +511,9 @@ val_in_range(struct f_val v1, struct f_val v2) if (v1.type == T_ECLIST) return eclist_match_set(v1.val.ad, v2.val.t); + if (v1.type == T_LCLIST) + return lclist_match_set(v1.val.ad, v2.val.t); + if (v1.type == T_PATH) return as_path_match_set(v1.val.ad, v2.val.t); @@ -457,12 +538,14 @@ val_format(struct f_val v, buffer *buf) case T_PAIR: buffer_print(buf, "(%u,%u)", v.val.i >> 16, v.val.i & 0xffff); return; case T_QUAD: buffer_print(buf, "%R", v.val.i); return; case T_EC: ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return; + case T_LC: lc_format(buf2, v.val.lc); buffer_print(buf, "%s", buf2); return; case T_PREFIX_SET: trie_format(v.val.ti, buf); return; case T_SET: tree_format(v.val.t, buf); return; case T_ENUM: buffer_print(buf, "(enum %x)%u", v.type, v.val.i); return; case T_PATH: as_path_format(v.val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return; case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return; case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return; + case T_LCLIST: lc_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return; case T_PATH_MASK: pm_format(v.val.path_mask, buf); return; default: buffer_print(buf, "[unknown type %x]", v.type); return; } @@ -656,6 +739,7 @@ interpret(struct f_inst *what) runtime("Can't operate with value of non-integer type in EC constructor"); val = v2.val.i; + /* XXXX */ res.type = T_EC; if (what->aux == EC_GENERIC) { @@ -677,6 +761,24 @@ interpret(struct f_inst *what) break; } + case P('m','l'): + { + TWOARGS; + + /* Third argument hack */ + struct f_val v3 = interpret(INST3(what).p); + if (v3.type & T_RETURN) + return v3; + + if ((v1.type != T_INT) || (v2.type != T_INT) || (v3.type != T_INT)) + runtime( "Can't operate with value of non-integer type in LC constructor" ); + + res.type = T_LC; + res.val.lc = (lcomm) { v1.val.i, v2.val.i, v3.val.i }; + + break; + } + /* Relational operators */ #define COMPARE(x) \ @@ -912,6 +1014,13 @@ interpret(struct f_inst *what) break; } + /* The same special case for lc_set */ + if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_LC_SET) { + res.type = T_LCLIST; + res.val.ad = adata_empty(f_pool, 0); + break; + } + /* Undefined value */ res.type = T_VOID; break; @@ -951,6 +1060,10 @@ interpret(struct f_inst *what) res.type = T_ECLIST; res.val.ad = e->u.ptr; break; + case EAF_TYPE_LC_SET: + res.type = T_LCLIST; + res.val.ad = e->u.ptr; + break; case EAF_TYPE_UNDEF: res.type = T_VOID; break; @@ -1041,6 +1154,11 @@ interpret(struct f_inst *what) runtime( "Setting eclist attribute to non-eclist value" ); l->attrs[0].u.ptr = v1.val.ad; break; + case EAF_TYPE_LC_SET: + if (v1.type != T_LCLIST) + runtime( "Setting lclist attribute to non-lclist value" ); + l->attrs[0].u.ptr = v1.val.ad; + break; case EAF_TYPE_UNDEF: if (v1.type != T_VOID) runtime( "Setting void attribute to non-void value" ); @@ -1082,6 +1200,7 @@ interpret(struct f_inst *what) case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break; case T_CLIST: res.val.i = int_set_get_size(v1.val.ad); break; case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break; + case T_LCLIST: res.val.i = lc_set_get_size(v1.val.ad); break; default: runtime( "Prefix, path, clist or eclist expected" ); } break; @@ -1277,7 +1396,7 @@ interpret(struct f_inst *what) else if (v2.type == T_ECLIST) arg_set = 2; else if (v2.type != T_EC) - runtime("Can't add/delete non-pair"); + runtime("Can't add/delete non-ec"); res.type = T_ECLIST; switch (what->aux) @@ -1308,8 +1427,50 @@ interpret(struct f_inst *what) bug("unknown Ca operation"); } } + else if (v1.type == T_LCLIST) + { + /* Large community list */ + int arg_set = 0; + + /* v2.val is either LC or LC-set */ + if ((v2.type == T_SET) && lclist_set_type(v2.val.t)) + arg_set = 1; + else if (v2.type == T_LCLIST) + arg_set = 2; + else if (v2.type != T_LC) + runtime("Can't add/delete non-lc"); + + res.type = T_LCLIST; + switch (what->aux) + { + case 'a': + if (arg_set == 1) + runtime("Can't add set"); + else if (!arg_set) + res.val.ad = lc_set_add(f_pool, v1.val.ad, v2.val.lc); + else + res.val.ad = lc_set_union(f_pool, v1.val.ad, v2.val.ad); + break; + + case 'd': + if (!arg_set) + res.val.ad = lc_set_del(f_pool, v1.val.ad, v2.val.lc); + else + res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 0); + break; + + case 'f': + if (!arg_set) + runtime("Can't filter lc"); + res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 1); + break; + + default: + bug("unknown Ca operation"); + } + } else - runtime("Can't add/delete to non-(e)clist"); + runtime("Can't add/delete to non-[e|l]clist"); break; @@ -1401,6 +1562,12 @@ i_same(struct f_inst *f1, struct f_inst *f2) case '~': TWOARGS; break; case P('d','e'): ONEARG; break; + case P('m','l'): + TWOARGS; + if (!i_same(INST3(f1).p, INST3(f2).p)) + return 0; + break; + case 's': ARG(v2, a2.p); { diff --git a/filter/filter.h b/filter/filter.h index e59c8226..e0f34e4f 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -38,6 +38,17 @@ struct f_inst_roa_check { struct roa_table_config *rtc; }; +struct f_inst3 { + struct f_inst i; + union { + int i; + void *p; + } a3; +}; + +#define INST3(x) (((struct f_inst3 *) x)->a3) + + struct f_prefix { ip_addr ip; int len; @@ -53,6 +64,7 @@ struct f_val { union { uint i; u64 ec; + lcomm lc; /* ip_addr ip; Folded into prefix */ struct f_prefix px; char *s; @@ -168,8 +180,10 @@ void val_format(struct f_val v, buffer *buf); #define T_PATH_MASK 0x23 /* mask for BGP path */ #define T_PATH 0x24 /* BGP path */ #define T_CLIST 0x25 /* Community list */ -#define T_ECLIST 0x26 /* Extended community list */ -#define T_EC 0x27 /* Extended community value, u64 */ +#define T_EC 0x26 /* Extended community value, u64 */ +#define T_ECLIST 0x27 /* Extended community list */ +#define T_LC 0x28 /* Large community value, lcomm */ +#define T_LCLIST 0x29 /* Large community list */ #define T_RETURN 0x40 #define T_SET 0x80 diff --git a/filter/test.conf b/filter/test.conf index f61f0658..e8873fbf 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -27,6 +27,11 @@ function 'mkpair-a'(int a) return (1, a); } +function mktrip(int a) +{ + return (a, 2*a, 3*a); +} + function mkpath(int a; int b) { return [= a b 3 2 1 =]; @@ -89,6 +94,7 @@ clist l; clist l2; eclist el; eclist el2; +lclist ll; { print "Entering path test..."; pm1 = / 4 3 2 1 /; @@ -203,6 +209,17 @@ eclist el2; print "eclist A isect B: ", filter( el, el2 ); print "eclist A \ B: ", delete( el, el2 ); + ll = --- empty ---; + ll = add(ll, (ten, 20, 30)); + ll = add(ll, (1000, 2000, 3000)); + ll = add(ll, mktrip(100000)); + print "LC list (10, 20, 30) (1000, 2000, 3000) (100000, 200000, 300000):"; + print ll; + print "LC len: ", el.len; + print "Should be true: ", mktrip(1000) ~ ll, " ", ll ~ [(5,10,15), (10,20,30)], " ", ll ~ [(10,15..25,*)], " ", ll ~ [(ten, *, *)]; + print "Should be false: ", mktrip(100) ~ ll, " ", ll ~ [(5,10,15), (10,21,30)], " ", ll ~ [(10,21..25,*)], " ", ll ~ [(11, *, *)]; + print "LC filtered: ", filter(ll, [(5..15, *, *), (100000, 500..500000, *)]); + # test_roa(); } diff --git a/nest/a-set.c b/nest/a-set.c index a6116022..0484ab98 100644 --- a/nest/a-set.c +++ b/nest/a-set.c @@ -116,7 +116,7 @@ int ec_set_format(struct adata *set, int from, byte *buf, uint size) { u32 *z = int_set_get_data(set); - byte *end = buf + size - 24; + byte *end = buf + size - 64; int from2 = MAX(from, 0); int to = int_set_get_size(set); int i; @@ -141,6 +141,43 @@ ec_set_format(struct adata *set, int from, byte *buf, uint size) return 0; } +int +lc_format(byte *buf, lcomm lc) +{ + return bsprintf(buf, "(%d, %d, %d)", lc.asn, lc.ldp1, lc.ldp2); +} + +int +lc_set_format(struct adata *set, int from, byte *buf, uint bufsize) +{ + u32 *d = (u32 *) set->data; + byte *end = buf + bufsize - 64; + int from2 = MAX(from, 0); + int to = set->length / 4; + int i; + + for (i = from2; i < to; i += 3) + { + if (buf > end) + { + if (from < 0) + strcpy(buf, "..."); + else + buf[-1] = 0; + return i; + } + + buf += bsprintf(buf, "(%d, %d, %d)", d[i], d[i+1], d[i+2]); + *buf++ = ' '; + } + + if (i != from2) + buf--; + + *buf = 0; + return 0; +} + int int_set_contains(struct adata *list, u32 val) { @@ -177,6 +214,24 @@ ec_set_contains(struct adata *list, u64 val) return 0; } +int +lc_set_contains(struct adata *list, lcomm val) +{ + if (!list) + return 0; + + u32 *l = int_set_get_data(list); + int len = int_set_get_size(list); + int i; + + for (i = 0; i < len; i += 3) + if (lc_match(l, i, val)) + return 1; + + return 0; +} + + struct adata * int_set_add(struct linpool *pool, struct adata *list, u32 val) { @@ -208,13 +263,30 @@ ec_set_add(struct linpool *pool, struct adata *list, u64 val) if (list) memcpy(res->data, list->data, list->length); - u32 *l = (u32 *) (res->data + res->length - 8); + u32 *l = (u32 *) (res->data + olen); l[0] = ec_hi(val); l[1] = ec_lo(val); return res; } +struct adata * +lc_set_add(struct linpool *pool, struct adata *list, lcomm val) +{ + if (lc_set_contains(list, val)) + return list; + + int olen = list ? list->length : 0; + struct adata *res = lp_alloc(pool, sizeof(struct adata) + olen + LCOMM_LENGTH); + res->length = olen + LCOMM_LENGTH; + + if (list) + memcpy(res->data, list->data, list->length); + + lc_put((u32 *) (res->data + olen), val); + + return res; +} struct adata * int_set_del(struct linpool *pool, struct adata *list, u32 val) @@ -265,6 +337,27 @@ ec_set_del(struct linpool *pool, struct adata *list, u64 val) return res; } +struct adata * +lc_set_del(struct linpool *pool, struct adata *list, lcomm val) +{ + if (!lc_set_contains(list, val)) + return list; + + struct adata *res; + res = lp_alloc(pool, sizeof(struct adata) + list->length - LCOMM_LENGTH); + res->length = list->length - LCOMM_LENGTH; + + u32 *l = int_set_get_data(list); + u32 *k = int_set_get_data(res); + int len = int_set_get_size(list); + int i; + + for (i=0; i < len; i += 3) + if (! lc_match(l, i, val)) + k = lc_copy(k, l+i); + + return res; +} struct adata * int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2) @@ -328,3 +421,33 @@ ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2) memcpy(res->data + l1->length, tmp, len); return res; } + +struct adata * +lc_set_union(struct linpool *pool, struct adata *l1, struct adata *l2) +{ + if (!l1) + return l2; + if (!l2) + return l1; + + struct adata *res; + int len = int_set_get_size(l2); + u32 *l = int_set_get_data(l2); + u32 tmp[len]; + u32 *k = tmp; + int i; + + for (i = 0; i < len; i += 3) + if (!lc_set_contains(l1, lc_get(l, i))) + k = lc_copy(k, l+i); + + if (k == tmp) + return l1; + + len = (k - tmp) * 4; + res = lp_alloc(pool, sizeof(struct adata) + l1->length + len); + res->length = l1->length + len; + memcpy(res->data, l1->data, l1->length); + memcpy(res->data + l1->length, tmp, len); + return res; +} diff --git a/nest/attrs.h b/nest/attrs.h index 670b048f..548d71a9 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -68,6 +68,7 @@ int as_path_match(struct adata *path, struct f_path_mask *mask); /* Transitive bit (for first u32 half of EC) */ #define EC_TBIT 0x40000000 +#define ECOMM_LENGTH 8 static inline int int_set_get_size(struct adata *list) { return list->length / 4; } @@ -75,6 +76,9 @@ static inline int int_set_get_size(struct adata *list) static inline int ec_set_get_size(struct adata *list) { return list->length / 8; } +static inline int lc_set_get_size(struct adata *list) +{ return list->length / 12; } + static inline u32 *int_set_get_data(struct adata *list) { return (u32 *) list->data; } @@ -98,17 +102,45 @@ static inline u64 ec_ip4(u64 kind, u64 key, u64 val) static inline u64 ec_generic(u64 key, u64 val) { return (key << 32) | val; } +/* Large community value */ +typedef struct lcomm { + u32 asn; + u32 ldp1; + u32 ldp2; +} lcomm; + +#define LCOMM_LENGTH 12 + +static inline lcomm lc_get(const u32 *l, int i) +{ return (lcomm) { l[i], l[i+1], l[i+2] }; } + +static inline void lc_put(u32 *l, lcomm v) +{ l[0] = v.asn; l[1] = v.ldp1; l[2] = v.ldp2; } + +static inline int lc_match(const u32 *l, int i, lcomm v) +{ return (l[i] == v.asn && l[i+1] == v.ldp1 && l[i+2] == v.ldp2); } + +static inline u32 *lc_copy(u32 *dst, const u32 *src) +{ memcpy(dst, src, LCOMM_LENGTH); return dst + 3; } + + int int_set_format(struct adata *set, int way, int from, byte *buf, uint size); int ec_format(byte *buf, u64 ec); int ec_set_format(struct adata *set, int from, byte *buf, uint size); +int lc_format(byte *buf, lcomm lc); +int lc_set_format(struct adata *set, int from, byte *buf, uint size); int int_set_contains(struct adata *list, u32 val); int ec_set_contains(struct adata *list, u64 val); +int lc_set_contains(struct adata *list, lcomm val); struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val); struct adata *ec_set_add(struct linpool *pool, struct adata *list, u64 val); +struct adata *lc_set_add(struct linpool *pool, struct adata *list, lcomm val); struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val); struct adata *ec_set_del(struct linpool *pool, struct adata *list, u64 val); +struct adata *lc_set_del(struct linpool *pool, struct adata *list, lcomm val); struct adata *int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2); struct adata *ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2); +struct adata *lc_set_union(struct linpool *pool, struct adata *l1, struct adata *l2); #endif diff --git a/nest/route.h b/nest/route.h index 2fcb189a..07320566 100644 --- a/nest/route.h +++ b/nest/route.h @@ -442,7 +442,7 @@ typedef struct eattr { #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_MASK 0x1f /* Mask with this to get type */ #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 */ @@ -451,7 +451,8 @@ typedef struct eattr { #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 */ +#define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */ +#define EAF_TYPE_UNDEF 0x1f /* `force undefined' entry */ #define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */ #define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */ #define EAF_ORIGINATED 0x40 /* The attribute has originated locally */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index ab0069b5..9aeca846 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -828,6 +828,18 @@ ea_show_ec_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end) } } +static inline void +ea_show_lc_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end) +{ + int i = lc_set_format(ad, 0, pos, end - pos); + cli_printf(c, -1012, "\t%s", buf); + while (i) + { + i = lc_set_format(ad, i, buf, end - buf - 1); + cli_printf(c, -1012, "\t\t%s", buf); + } +} + /** * ea_show - print an &eattr to CLI * @c: destination CLI @@ -892,6 +904,9 @@ ea_show(struct cli *c, eattr *e) case EAF_TYPE_EC_SET: ea_show_ec_set(c, ad, pos, buf, end); return; + case EAF_TYPE_LC_SET: + ea_show_lc_set(c, ad, pos, buf, end); + return; case EAF_TYPE_UNDEF: default: bsprintf(pos, "", e->type); diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index b8371f32..6fe34e56 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -289,6 +289,12 @@ bgp_check_ext_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len) return ((len % 8) == 0) ? 0 : WITHDRAW; } +static int +bgp_check_large_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len) +{ + return ((len % 12) == 0) ? 0 : WITHDRAW; +} + static struct attr_desc bgp_attr_table[] = { { NULL, -1, 0, 0, 0, /* Undefined */ @@ -325,7 +331,10 @@ static struct attr_desc bgp_attr_table[] = { { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */ NULL, NULL }, { "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */ - NULL, NULL } + NULL, NULL }, + [BA_LARGE_COMMUNITY] = + { "large_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_LC_SET, 1, + bgp_check_large_community, NULL } }; /* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH. @@ -575,7 +584,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains) len = bgp_get_attr_len(a); /* Skip empty sets */ - if (((type == EAF_TYPE_INT_SET) || (type == EAF_TYPE_EC_SET)) && (len == 0)) + if (((type == EAF_TYPE_INT_SET) || (type == EAF_TYPE_EC_SET) || (type == EAF_TYPE_LC_SET)) && (len == 0)) continue; if (remains < len + 4) @@ -601,6 +610,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains) break; } case EAF_TYPE_INT_SET: + case EAF_TYPE_LC_SET: case EAF_TYPE_EC_SET: { u32 *z = int_set_get_data(a->u.ptr); @@ -683,6 +693,25 @@ bgp_normalize_ec_set(struct adata *ad, u32 *src, int internal) qsort(dst, ad->length / 8, 8, (int(*)(const void *, const void *)) bgp_compare_ec); } +static int +bgp_compare_lc(const u32 *x, const u32 *y) +{ + if (x[0] != y[0]) + return (x[0] > y[0]) ? 1 : -1; + if (x[1] != y[1]) + return (x[1] > y[1]) ? 1 : -1; + if (x[2] != y[2]) + return (x[2] > y[2]) ? 1 : -1; + return 0; +} + +static inline void +bgp_normalize_lc_set(u32 *dest, u32 *src, unsigned cnt) +{ + memcpy(dest, src, LCOMM_LENGTH * cnt); + qsort(dest, cnt, LCOMM_LENGTH, (int(*)(const void *, const void *)) bgp_compare_lc); +} + static void bgp_rehash_buckets(struct bgp_proto *p) { @@ -827,6 +856,14 @@ bgp_get_bucket(struct bgp_proto *p, net *n, ea_list *attrs, int originate) d->u.ptr = z; break; } + case EAF_TYPE_LC_SET: + { + struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length); + z->length = d->u.ptr->length; + bgp_normalize_lc_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / LCOMM_LENGTH); + d->u.ptr = z; + break; + } default: ; } d++; @@ -1797,6 +1834,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, uint len, struct linpool *po ipa_ntoh(*(ip_addr *)ad->data); break; case EAF_TYPE_INT_SET: + case EAF_TYPE_LC_SET: case EAF_TYPE_EC_SET: { u32 *z = (u32 *) ad->data; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index b1cca2d9..d5747c18 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -308,6 +308,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi #define BA_EXT_COMMUNITY 0x10 /* [RFC4360] */ #define BA_AS4_PATH 0x11 /* [RFC4893] */ #define BA_AS4_AGGREGATOR 0x12 +#define BA_LARGE_COMMUNITY 0x1e /* [draft-ietf-idr-large-community] */ /* BGP connection states */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index f3ba0e16..32ae88a3 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -27,7 +27,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE, - CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY) + CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY, BGP_LARGE_COMMUNITY) CF_GRAMMAR @@ -159,6 +159,8 @@ CF_ADDTO(dynamic_attr, BGP_CLUSTER_LIST { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); }) CF_ADDTO(dynamic_attr, BGP_EXT_COMMUNITY { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(EAP_BGP, BA_EXT_COMMUNITY)); }) +CF_ADDTO(dynamic_attr, BGP_LARGE_COMMUNITY + { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(EAP_BGP, BA_LARGE_COMMUNITY)); }) From 60566c5c804070c145fafd75ef2c17efb489a1eb Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sat, 1 Oct 2016 22:31:01 +0200 Subject: [PATCH 02/37] Filter: large community sets Add support for lc sets to filter code. Grammar of (small) community sets has to be updated to avoid parser collisions. --- filter/config.Y | 92 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index 79e274ab..29e3a734 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -67,6 +67,14 @@ f_merge_items(struct f_tree *a, struct f_tree *b) static inline struct f_tree * f_new_pair_item(int fa, int ta, int fb, int tb) { + check_u16(fa); + check_u16(ta); + check_u16(fb); + check_u16(tb); + + if ((ta < fa) || (tb < fb)) + cf_error( "From value cannot be higher that To value in pair sets"); + struct f_tree *t = f_new_tree(); t->right = t; t->from.type = t->to.type = T_PAIR; @@ -78,22 +86,26 @@ f_new_pair_item(int fa, int ta, int fb, int tb) static inline struct f_tree * f_new_pair_set(int fa, int ta, int fb, int tb) { - struct f_tree *lst = NULL; - int i; + check_u16(fa); + check_u16(ta); + check_u16(fb); + check_u16(tb); - if ((fa == ta) || ((fb == 0) && (tb == 0xFFFF))) - return f_new_pair_item(fa, ta, fb, tb); - if ((ta < fa) || (tb < fb)) cf_error( "From value cannot be higher that To value in pair sets"); + struct f_tree *lst = NULL; + int i; + for (i = fa; i <= ta; i++) lst = f_merge_items(lst, f_new_pair_item(i, i, fb, tb)); return lst; } +#define CC_ALL 0xFFFF #define EC_ALL 0xFFFFFFFF +#define LC_ALL 0xFFFFFFFF static struct f_tree * f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt) @@ -133,6 +145,17 @@ f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt) return t; } +static struct f_tree * +f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3) +{ + struct f_tree *t = f_new_tree(); + t->right = t; + t->from.type = t->to.type = T_LC; + t->from.val.lc = (lcomm) {f1, f2, f3}; + t->to.val.lc = (lcomm) {t1, t2, t3}; + return t; +} + static inline struct f_inst * f_generate_empty(struct f_inst *dyn) { @@ -327,9 +350,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %type term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr %type filter filter_body where_filter -%type type break_command pair_expr ec_kind -%type pair_atom ec_expr -%type pair_item ec_item set_item switch_item set_items switch_items switch_body +%type type break_command ec_kind +%type cnum +%type pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body %type fprefix_set %type set_atom switch_atom fprefix fprefix_s fipa %type decls declsn one_decl function_params @@ -550,30 +573,23 @@ switch_atom: | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } ; -pair_expr: - term { $$ = f_eval_int($1); check_u16($$); } - -pair_atom: - pair_expr { $$ = pair($1, $1); } - | pair_expr DDOT pair_expr { $$ = pair($1, $3); } - | '*' { $$ = 0xFFFF; } - ; +cnum: + term { $$ = f_eval_int($1); } pair_item: - '(' pair_atom ',' pair_atom ')' { - $$ = f_new_pair_set(pair_a($2), pair_b($2), pair_a($4), pair_b($4)); - } - | '(' pair_atom ',' pair_atom ')' DDOT '(' pair_expr ',' pair_expr ')' { - /* Hack: $2 and $4 should be pair_expr, but that would cause shift/reduce conflict */ - if ((pair_a($2) != pair_b($2)) || (pair_a($4) != pair_b($4))) - cf_error("syntax error"); - $$ = f_new_pair_item(pair_b($2), $8, pair_b($4), $10); - } + '(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); } + | '(' cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_item($2, $2, $4, $6); } + | '(' cnum ',' '*' ')' { $$ = f_new_pair_item($2, $2, 0, CC_ALL); } + | '(' cnum DDOT cnum ',' cnum ')' { $$ = f_new_pair_set($2, $4, $6, $6); } + | '(' cnum DDOT cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_set($2, $4, $6, $8); } + | '(' cnum DDOT cnum ',' '*' ')' { $$ = f_new_pair_item($2, $4, 0, CC_ALL); } + | '(' '*' ',' cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $4); } + | '(' '*' ',' cnum DDOT cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $6); } + | '(' '*' ',' '*' ')' { $$ = f_new_pair_item(0, CC_ALL, 0, CC_ALL); } + | '(' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ')' + { $$ = f_new_pair_item($2, $8, $4, $10); } ; -ec_expr: - term { $$ = f_eval_int($1); } - ec_kind: RT { $$ = EC_RT; } | RO { $$ = EC_RO; } @@ -582,14 +598,27 @@ ec_kind: ; ec_item: - '(' ec_kind ',' ec_expr ',' ec_expr ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); } - | '(' ec_kind ',' ec_expr ',' ec_expr DDOT ec_expr ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); } - | '(' ec_kind ',' ec_expr ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); } + '(' ec_kind ',' cnum ',' cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); } + | '(' ec_kind ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); } + | '(' ec_kind ',' cnum ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); } ; +lc_item: + '(' cnum ',' cnum ',' cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $6); } + | '(' cnum ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $8); } + | '(' cnum ',' cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $4, 0, LC_ALL); } + | '(' cnum ',' cnum DDOT cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $6, 0, LC_ALL); } + | '(' cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $2, 0, LC_ALL, 0, LC_ALL); } + | '(' cnum DDOT cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $4, 0, LC_ALL, 0, LC_ALL); } + | '(' '*' ',' '*' ',' '*' ')' { $$ = f_new_lc_item(0, LC_ALL, 0, LC_ALL, 0, LC_ALL); } + | '(' cnum ',' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ',' cnum ')' + { $$ = f_new_lc_item($2, $10, $4, $12, $6, $14); } +; + set_item: pair_item | ec_item + | lc_item | set_atom { $$ = f_new_item($1, $1); } | set_atom DDOT set_atom { $$ = f_new_item($1, $3); } ; @@ -597,6 +626,7 @@ set_item: switch_item: pair_item | ec_item + | lc_item | switch_atom { $$ = f_new_item($1, $1); } | switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); } ; From cec4a73ccb22ed412e87560e4210b6df40832aad Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 4 Oct 2016 00:31:43 +0200 Subject: [PATCH 03/37] Doc: Documentation for large communities --- doc/bird.sgml | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 26673f03..bb88dc61 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1048,7 +1048,16 @@ foot). for int set look like [ 1, 2, 5..7 ]. As you can see, both simple @@ -1067,9 +1076,15 @@ foot). (like define one=1; @@ -1205,6 +1220,13 @@ foot). The same operations (like @@ -2193,6 +2215,14 @@ some of them (marked with `lclist + List of large community values associated with the route. Large BGP + communities is another variant of communities, but contrary to extended + communities they behave very much the same way as regular communities, + just larger -- they are uniform untyped triplets of 32bit numbers. + Individual community values are represented using an quad This attribute is created by the route reflector when reflecting the route and contains the router ID of the originator of the route in the From a46e01eeef17a7efe876618623397f60e62afe37 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 4 Oct 2016 12:45:39 +0200 Subject: [PATCH 04/37] Nest: Fix signedness of large communities --- nest/a-set.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nest/a-set.c b/nest/a-set.c index 0484ab98..fde34cab 100644 --- a/nest/a-set.c +++ b/nest/a-set.c @@ -144,7 +144,7 @@ ec_set_format(struct adata *set, int from, byte *buf, uint size) int lc_format(byte *buf, lcomm lc) { - return bsprintf(buf, "(%d, %d, %d)", lc.asn, lc.ldp1, lc.ldp2); + return bsprintf(buf, "(%u, %u, %u)", lc.asn, lc.ldp1, lc.ldp2); } int @@ -167,7 +167,7 @@ lc_set_format(struct adata *set, int from, byte *buf, uint bufsize) return i; } - buf += bsprintf(buf, "(%d, %d, %d)", d[i], d[i+1], d[i+2]); + buf += bsprintf(buf, "(%u, %u, %u)", d[i], d[i+1], d[i+2]); *buf++ = ' '; } From a998836d4bd4df8820e67f51e16d81a5a8dc9e9b Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 4 Oct 2016 23:19:35 +0200 Subject: [PATCH 05/37] Filter: fix missing separator --- filter/trie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter/trie.c b/filter/trie.c index 8af9015e..fba395d1 100644 --- a/filter/trie.c +++ b/filter/trie.c @@ -297,7 +297,7 @@ trie_format(struct f_trie *t, buffer *buf) buffer_puts(buf, "["); if (t->zero) - buffer_print(buf, "%I/%d", IPA_NONE, 0); + buffer_print(buf, "%I/%d, ", IPA_NONE, 0); trie_node_format(t->root, buf); /* Undo last separator */ From 9faf72c8cc9a099b41c90ee1822e8bca22fd0596 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Thu, 29 Sep 2016 11:20:04 +0200 Subject: [PATCH 06/37] Doc: Fix whitespaces --- doc/sbase/dist/birddoc/groff/mapping | 144 ++++++++++++------------- doc/sbase/dist/birddoc/html/mapping | 60 +++++------ doc/sbase/dist/birddoc/latex2e/mapping | 42 ++++---- doc/sbase/dtd/birddoc.dtd | 102 +++++++++--------- 4 files changed, 174 insertions(+), 174 deletions(-) diff --git a/doc/sbase/dist/birddoc/groff/mapping b/doc/sbase/dist/birddoc/groff/mapping index 71d2c935..3861a28d 100644 --- a/doc/sbase/dist/birddoc/groff/mapping +++ b/doc/sbase/dist/birddoc/groff/mapping @@ -6,7 +6,7 @@ % Based on qwertz replacement file by Tom Gordon % linuxdoc mods by mdw -% Groff dependencies are few. To port to another roff: +% Groff dependencies are few. To port to another roff: % 1. Check and modify, if necessary, font changes. (e.g. In psroff the % same fonts have other names.) % 2. Check the code for including Encapsulated PostScript, generated @@ -19,13 +19,13 @@ % Hacked by mdw ".nr PI 3n\n" - ".ds CF \\\\n\%\n" + ".ds CF \\\\n\%\n" ".ds CH \\&\n" ".ds dR $\n" % dollar, to avoid EQN conflicts % Start with no TOC ".ds printtoc\n" - + % Footnote style ".nr FF 1\n" @@ -51,16 +51,16 @@ ".nr HM 0i\n" ".nr FM 0i\n" - % Turn off right-margin filling + % Turn off right-margin filling ".na\n" - + % h is 1 if first paragraph after heading - ".nr h 0\n" + ".nr h 0\n" % initialize heading level - - ".nr il 1\n" + + ".nr il 1\n" % Number registers for list @@ -68,20 +68,20 @@ ".nr ll 0\n" % list level, stores current level ".nr el 0\n" % current enumeration level - % Not all list levels are enumerations, as + % Not all list levels are enumerations, as % itemizations can be embedded within enumerations % and vice versa - + % type of list level is in \n(t\n(ll, where % 0 : itemize, 1 : enumerate, 2: description % enumerator for an enumeration level is in % \n(e\n(el -- i.e. \n(e1=2 means current item of % enumeration level 1 is 2 - + % context-sensitive paragraph macro -% Bug: There's some problem using this to re-start paragraphs after the +% Bug: There's some problem using this to re-start paragraphs after the % and , so after verb and code I insert .LP. That's fine % except that is loses indentation when using verb or code inside of a list. @@ -95,21 +95,21 @@ % for this enumeration level ".if \\\\n(t\\\\n(ll=1 \\{.IP \\\\n+(e\\\\n(el.\\}\n" % if first par element of descrip, do nothing -".\\}\n" +".\\}\n" ".el .sp \n" % subsequent par element of item ".\\}\n" ".el \\{\\\n" % not within list -".ie \\\\nh=1 \\{\\\n" % first par after heading -".LP\n" +".ie \\\\nh=1 \\{\\\n" % first par after heading +".LP\n" ".nr h 0\n" % reset h flag -".\\}\n" +".\\}\n" ".el .LP \n" % Changed from .PP, mdw ".\\}\n" ".nh\n" -"..\n" +"..\n" + + - - % for each level, a number register is created % to store its type and current item number, where % -1=bullet of an itemized list. @@ -141,7 +141,7 @@ % set initial level of headings, in register il
+ ".nr il 0" + -
+ ".if '\\*[printtoc]'true' .PX\n" + + ".if '\\*[printtoc]'true' .PX\n" + ".nr il 1" + + ".bp\n" @@ -153,23 +153,23 @@ ".bp\n" ".TC" + - + + ".nr il -1" + - + % Hacked up titlepag stuff to look more reasonable. Titles and author % names are now stored in strings, printed by the end of . -% Wake up! This uses groff-like long string names. You must use groff +% Wake up! This uses groff-like long string names. You must use groff % to format this. + ".ds mdwtitle\n" ".ds mdwsubtitle\n" - ".ds mdwdate\n" + ".ds mdwdate\n" ".de printabstract\n" "..\n" + + "\\*[mdwtitle]\n" @@ -181,10 +181,10 @@ "\\*[mdwdate]\n" ".br\n" ".printabstract\n" - ".br\n" + ".br\n" % + ".TL" + -% +% + ".ds mdwtitle " + @@ -194,13 +194,13 @@ % ".SM" + % + ".LG" + - + ".ds mdwsubtitle " + + ".ds mdwsubtitle " + - + ".ds mdwdate " + + ".ds mdwdate " + - + ".de printabstract\n" + + ".de printabstract\n" ".LP\n" + ".." + @@ -215,10 +215,10 @@ + ".br" + - + - "\\**\n" + "\\**\n" ".FS" + + ".FE" + @@ -229,11 +229,11 @@ + ".br" - + -
-
+
+
+ ".EH '" "'''" + @@ -263,13 +263,13 @@ - + - + - + ".bp\n" + + ".bp\n" ".NH \\n(il " + @@ -283,7 +283,7 @@ + ".NH 4+\\n(il" + - + + ".NH 5+\\n(il" + @@ -292,10 +292,10 @@ + "\\*h\n" ".XS \\n%\n" "\\*(SN \\*h\n" - ".XE\n" + ".XE\n" ".nr h 1\n" % set heading flag to true -

+ ".Pp" + +

+ ".Pp" +

+ ".nr ll +1\n" % increment list level @@ -309,9 +309,9 @@ ".af e\\n(el \\*(f\\n(el\n" % style of enumerator ".if \\n(ll>1 .RS" + + ".if \\n(ll>1 .RE\n" - ".br\n" + ".br\n" ".nr el -1\n" % decrement enumeration level - ".nr ll -1\n" % decrement list level + ".nr ll -1\n" % decrement list level + ".RS\n" ".nr ll +1\n" % increment list level @@ -324,7 +324,7 @@ % If bi=1 then the paragraph is the first one of the item. + ".nr bi 1\n.Pp" + - + + ".IP \"\\fB" "\\fR\"\n" @@ -337,12 +337,12 @@
"" + ".\[\n[ID]\n.\]" + - + + ".\[\n[ID]\n.\]\n([NOTE])" - " (-- " + " (-- " "--)" + "\\*Q" @@ -353,20 +353,20 @@ + ".nr LL \\n(LL+\\n(PI\n" ".RE" + - "\\fI" - "\\fP" + "\\fI" + "\\fP" - "\\fB" - "\\fR" + "\\fB" + "\\fR" - "\\fI" - "\\fR" + "\\fI" + "\\fR" - "\\fR" - "\\fR" + "\\fR" + "\\fR" - "\\fI" - "\\fR" + "\\fI" + "\\fR" % Changed by mdw "\\fC" @@ -394,10 +394,10 @@ "??" - + - + + ".\[\n" @@ -423,7 +423,7 @@ % ".Pp" + % continue previous paragraph (changed mdw) ".LP" -% tscreen added by mdw +% tscreen added by mdw + ".br\n" ".po 0.75i\n" ".ll 6.0i\n" @@ -487,8 +487,8 @@ % mathematics -- this nroff version needs work. - - + + + ".DS L" + + ".DE" + @@ -496,8 +496,8 @@ + ".DS L" + + ".DE" + - - + + "{" "} over " @@ -505,7 +505,7 @@ "{" "}" - + @@ -527,7 +527,7 @@ " sum " - + % limitation: eqn only does square roots! @@ -539,7 +539,7 @@ "[ca]." + + ".TE" + - "\n" + "\n" "|" @@ -567,8 +567,8 @@ % limitation: no calligraphic characters, using helvetica italics instead. Is there a better font? - "\\fI" - "\\fP" + "\\fI" + "\\fP" " roman }" "}" @@ -584,12 +584,12 @@ + ".if t .PSPIC [file].ps\n" ".if n .sp 4" + - - + + % Are TeX units properly handled by this translation of ph? + ".sp [VSPACE]" + - + + ".sp\n.ce" + @@ -619,7 +619,7 @@ + ".nr PS 18" + - + + ".bp\n\\&" + % letters -- replacement for email, using mh format. diff --git a/doc/sbase/dist/birddoc/html/mapping b/doc/sbase/dist/birddoc/html/mapping index 353d7774..d95b1759 100644 --- a/doc/sbase/dist/birddoc/html/mapping +++ b/doc/sbase/dist/birddoc/html/mapping @@ -21,7 +21,7 @@ + "<@@enddoc>" + % Manual Pages are expected to be formatted using nroff (or groff), unless -% they are included as sections of other qwertz documents. +% they are included as sections of other qwertz documents. @@ -35,7 +35,7 @@ + "<@@title>" - + "

" + + "

" "

" + @@ -48,26 +48,26 @@ + "Thanks " - + + "

" "

" + "
" - +
+
+ "\\markboth"
@@ -110,7 +110,7 @@ "{" "}\n\n" -

+

"\\phantomsection{}"

"\n\n" + "\\begin{itemize}" + @@ -128,7 +128,7 @@ + "\\item " - + "\\item\[{\\ttfamily " + + "\\phantomsection\\item\[{\\ttfamily " "}\] \\hfil\\break\n" + + "\\item\[ " @@ -224,17 +224,17 @@ "\\cparam{" "}" - "\\ref{[ID]} {([NAME])}" + "\\hyperref\[[ID]\]{[NAME]} (p.\\,\\getpagerefnumber{[ID]})" "\\pageref{[ID]}" %url added by HG - "\\nameurl{[URL]}{[NAME]}" + "\\href{[URL]}{[NAME]}" - "\\onlynameurl{[NAME]}" + "\\href{[URL]}{[NAME]}" diff --git a/doc/sbase/dtd/birddoc.dtd b/doc/sbase/dtd/birddoc.dtd index 1654b0e5..6db5066a 100644 --- a/doc/sbase/dtd/birddoc.dtd +++ b/doc/sbase/dtd/birddoc.dtd @@ -564,9 +564,9 @@ anywhere else. - + - + From 22558357d45c27583156f8c11412e37ce48a42e0 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Mon, 3 Oct 2016 10:22:24 +0200 Subject: [PATCH 09/37] Doc: Add command-line options --version, --help --- doc/bird.sgml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/bird.sgml b/doc/bird.sgml index f2001e75..5cc12b28 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -181,6 +181,12 @@ BIRD executable by configuring out routing protocols you don't use, and apply graceful restart recovery after start. + + + display bird version. + + + display command-line options to bird.

BIRD writes messages about its work to log files or syslog (according to config). From f5952c7343841fe4b7b63b7a56e95aba104f2e82 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Mon, 3 Oct 2016 10:32:28 +0200 Subject: [PATCH 10/37] Doc: Daemon command-line options alphabet order --- doc/bird.sgml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 5cc12b28..68851e3a 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -154,39 +154,39 @@ BIRD executable by configuring out routing protocols you don't use, and

BIRD writes messages about its work to log files or syslog (according to config). From f15dc6813870565d01378265ab20e017757af220 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Mon, 3 Oct 2016 10:59:43 +0200 Subject: [PATCH 11/37] Doc: Enable break lines in --- doc/sbase/dist/birddoc/latex2e/mapping | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/sbase/dist/birddoc/latex2e/mapping b/doc/sbase/dist/birddoc/latex2e/mapping index 9b8962d8..161227d2 100644 --- a/doc/sbase/dist/birddoc/latex2e/mapping +++ b/doc/sbase/dist/birddoc/latex2e/mapping @@ -4,6 +4,7 @@ % The \relax is there to avoid sgml2latex rewriting the class + "\\relax\\documentclass\[a4paper,10pt,openany\]{book}\n" "\\usepackage\[colorlinks=true,linkcolor=blue,pdftitle={BIRD User's Guide}\]{hyperref}\n" + "\\usepackage{enumitem}\n" "\\usepackage{birddoc}\n" "\\usepackage{qwertz}\n" "\\usepackage{url}\n" @@ -122,7 +123,7 @@ + "\\begin{list}{}{}\n" + + "\\end{list}" + - + "\\begin{description}" + + + "\\begin{description}\[style=unboxed\]" + + "\\end{description}" + + "\\item " From 74d76f6c3877e4a745fb63b55486810322076153 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Mon, 3 Oct 2016 11:36:44 +0200 Subject: [PATCH 12/37] Doc: Fix unnecessary special chars --- doc/bird.sgml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 68851e3a..591bb87a 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -815,7 +815,7 @@ This argument can be omitted if there exists only a single instance. number of networks, number of routes before and after filtering). If you use

" "
" + diff --git a/doc/sbase/dist/birddoc/latex2e/mapping b/doc/sbase/dist/birddoc/latex2e/mapping index 7653cb78..e7c62e0a 100644 --- a/doc/sbase/dist/birddoc/latex2e/mapping +++ b/doc/sbase/dist/birddoc/latex2e/mapping @@ -156,7 +156,7 @@ % The idea here is to automatically insert soft hyphens after every slash in % the filename, so long filenames will break naturally. The url{} macro is % a kluge but it works, - "\\url{" + "{\\tt " "}" "\\footnote{" From 9c20a8b7ae69487397392c720a5e75087c343df1 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Mon, 3 Oct 2016 12:35:36 +0200 Subject: [PATCH 16/37] Doc: Fix inline Don't make space before or after link name. --- doc/sbase/dist/birddoc/html/mapping | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/sbase/dist/birddoc/html/mapping b/doc/sbase/dist/birddoc/html/mapping index 6d080738..5ce36066 100644 --- a/doc/sbase/dist/birddoc/html/mapping +++ b/doc/sbase/dist/birddoc/html/mapping @@ -202,9 +202,7 @@ "<@@endurl>" +
- + "<@@url>[URL]\n" - "[NAME]\n" - "<@@endurl>" + + "[NAME]" % ref modified to have an optional name field From 7935b9d21228dcd1eb95ebcb056b2a815e3e854b Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Thu, 29 Sep 2016 18:08:40 +0200 Subject: [PATCH 17/37] Doc: Add tag for links to RFCs --- doc/bird.sgml | 238 +++++++++++-------------- doc/sbase/dist/birddoc/html/mapping | 4 + doc/sbase/dist/birddoc/latex2e/mapping | 3 + doc/sbase/dtd/birddoc.dtd | 6 +- 4 files changed, 118 insertions(+), 133 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 159cac46..24be3de0 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -73,8 +73,8 @@ running on background which does the dynamic part of Internet routing, that is it communicates with the other routers, calculates routing tables and sends them to the OS kernel which does the actual packet forwarding. There already exist other such routing daemons: routed (RIP only), GateD (non-free), -Zebra and -MRTD , + and +, but their capabilities are limited and they are relatively hard to configure and maintain. @@ -485,9 +485,9 @@ protocol rip { used to validate route origination of BGP routes. A ROA table contains ROA entries, each consist of a network prefix, a max prefix length and an AS number. A ROA entry specifies prefixes which could be originated - by that AS number. ROA tables could be filled with data from RPKI (RFC - 6480) or from public databases like Whois. ROA tables are examined by - ) or from public databases like Whois. ROA tables are + examined by roa , which can be used to populate the ROA table with static @@ -1270,9 +1270,9 @@ clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set).

There is one operator related to ROA infrastructure - roa_check(, which checks -current route (which should be from BGP to have AS_PATH argument) in the +examines a ROA table and does route origin validation for a +given network prefix. The basic usage is roa_check(, which +checks current route (which should be from BGP to have AS_PATH argument) in the specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA, ROA_VALID if there is a matching ROA, or ROA_INVALID if there are some relevant ROAs but none of them match. There is also an extended variant @@ -1434,11 +1434,12 @@ corresponding protocol sections. Introduction

The Babel protocol (RFC6126) is a loop-avoiding distance-vector routing -protocol that is robust and efficient both in ordinary wired networks and in -wireless mesh networks. Babel is conceptually very simple in its operation and -"just works" in its default configuration, though some configuration is possible -and in some cases desirable. +

The Babel protocol +() is a loop-avoiding distance-vector routing protocol that is +robust and efficient both in ordinary wired networks and in wireless mesh +networks. Babel is conceptually very simple in its operation and "just works" +in its default configuration, though some configuration is possible and in some +cases desirable.

While the Babel protocol is dual stack (i.e., can carry both IPv4 and IPv6 routes over the same IPv6 transport), BIRD presently implements only the IPv6 @@ -1580,14 +1581,10 @@ addresses and associated interfaces. When a session changes its state, these protocols are notified and act accordingly (e.g. break an OSPF adjacency when the BFD session went down). -

BIRD implements basic BFD behavior as defined in -RFC 5880 -(some advanced features like the echo mode or authentication are not implemented), -IP transport for BFD as defined in -RFC 5881 and -RFC 5883 -and interaction with client protocols as defined in -RFC 5882. +

BIRD implements basic BFD behavior as defined in (some +advanced features like the echo mode or authentication are not implemented), IP +transport for BFD as defined in and and +interaction with client protocols as defined in .

Note that BFD implementation in BIRD is currently a new feature in development, expect some rough edges and possible UI and configuration changes @@ -1764,31 +1761,16 @@ the packet will travel through if it uses the particular route) in order to avoid routing loops.

BIRD supports all requirements of the BGP4 standard as defined in -RFC 4271 -It also supports the community attributes -(RFC 1997), -capability negotiation -(RFC 5492), -MD5 password authentication -(RFC 2385), -extended communities -(RFC 4360), -route reflectors -(RFC 4456), -graceful restart -(RFC 4724), -multiprotocol extensions -(RFC 4760), -4B AS numbers -(RFC 4893), -and 4B AS numbers in extended communities -(RFC 5668). + It also supports the community attributes (), +capability negotiation (), MD5 password authentication (), extended communities (), route reflectors (), graceful restart (), multiprotocol extensions +(), 4B AS numbers (), and 4B AS numbers in +extended communities (). For IPv6, it uses the standard multiprotocol extensions defined in -RFC 4760 -and applied to IPv6 according to -RFC 2545. + and applied to IPv6 according to . Route selection rules

Open Shortest Path First (OSPF) is a quite complex interior gateway -protocol. The current IPv4 version (OSPFv2) is defined in RFC 2328 - -and the current IPv6 version (OSPFv3) is defined in RFC 5340 - -It's a link state (a.k.a. shortest path first) protocol -- each router maintains -a database describing the autonomous system's topology. Each participating -router has an identical copy of the database and all routers run the same -algorithm calculating a shortest path tree with themselves as a root. OSPF -chooses the least cost path as the best path. +protocol. The current IPv4 version (OSPFv2) is defined in and +the current IPv6 version (OSPFv3) is defined in It's a link +state (a.k.a. shortest path first) protocol -- each router maintains a database +describing the autonomous system's topology. Each participating router has an +identical copy of the database and all routers run the same algorithm +calculating a shortest path tree with themselves as a root. OSPF chooses the +least cost path as the best path.

In OSPF, the autonomous system can be split to several areas in order to reduce the amount of resources consumed for exchanging the routing information @@ -2708,8 +2689,7 @@ protocol ospf <name> { This option controls compatibility of routing table calculation with - RFC 1583 . - Default value is no. + . Default value is no.

BIRD supports RIPv1 -(RFC 1058), -RIPv2 (RFC 2453), -RIPng (RFC 2080), -and RIP cryptographic authentication (SHA-1 not implemented) -(RFC 4822). +

BIRD supports RIPv1 (), RIPv2 (), RIPng (), and RIP cryptographic authentication (SHA-1 not implemented) +().

RIP is a very simple protocol, and it has a lot of shortcomings. Slow convergence, big network load and inability to handle larger networks makes it @@ -3707,8 +3680,9 @@ protocol rip [<name>] { compatibility with neighbors regardless of whether they use ttl security. - For RIPng, TTL security is a standard behavior (required by RFC 2080) - and therefore default value is yes. For IPv4 RIP, default value is no. + For RIPng, TTL security is a standard behavior (required by ) and therefore default value is yes. For IPv4 RIP, default + value is no.

There are several options that give sense only with certain protocols: - -

There are just few configuration options for the Direct protocol:

-

BIRD supports RIPv1 (), RIPv2 (), RIPng (), and RIP cryptographic authentication (SHA-1 not implemented) -(). +id="2080">), and RIP cryptographic authentication ().

RIP is a very simple protocol, and it has a lot of shortcomings. Slow convergence, big network load and inability to handle larger networks makes it @@ -3545,6 +3575,9 @@ protocol rip [<name>] { generate to "<date>"; accept from "<date>"; accept to "<date>"; + from "<date>"; + to "<date>"; + algorithm ( keyed md5 | keyed sha1 | hmac sha1 | hmac sha256 | hmac sha384 | hmac sha512 ); }; }; } @@ -3658,7 +3691,9 @@ protocol rip [<name>] { Selects authentication method to be used. password section. Default: none. @@ -3704,8 +3739,8 @@ protocol rip [<name>] { consideration. When the link disappears (e.g. an ethernet cable is unplugged), neighbors are immediately considered unreachable and all routes received from them are withdrawn. It is possible that some - hardware drivers or platforms do not implement this feature. Default: - no. + hardware drivers or platforms do not implement this feature. + Default: no. Attributes @@ -3737,8 +3772,9 @@ protocol rip { period 12; garbage time 60; interface "eth0" { metric 3; mode multicast; }; - interface "eth*" { metric 2; mode broadcast; }; - authentication none; + interface "eth*" { metric 2; mode broadcast; }; + authentication cryptographic; + password "secret-shared-key" { algorithm hmac sha256; }; import filter { print "importing"; accept; }; export filter { print "exporting"; accept; }; } From 390601f038b69d5de3841c691f92af0fcd088454 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 26 Oct 2016 16:07:45 +0200 Subject: [PATCH 33/37] RIP: Use message authentication interface Based on former commit from Pavel Tvrdik --- lib/mac.h | 4 +++ lib/string.h | 10 +++++++ nest/password.c | 15 ++++++++++ nest/password.h | 2 ++ proto/rip/config.Y | 22 +++++++++++--- proto/rip/packets.c | 72 +++++++++++++++++++++++++++++---------------- proto/rip/rip.c | 7 ++--- proto/rip/rip.h | 2 +- 8 files changed, 100 insertions(+), 34 deletions(-) diff --git a/lib/mac.h b/lib/mac.h index 9dba8f89..5fc216fd 100644 --- a/lib/mac.h +++ b/lib/mac.h @@ -21,6 +21,7 @@ #define ALG_SHA256 0x04 #define ALG_SHA384 0x05 #define ALG_SHA512 0x06 +#define ALG_HMAC 0x10 #define ALG_HMAC_MD5 0x11 #define ALG_HMAC_SHA1 0x12 #define ALG_HMAC_SHA224 0x13 @@ -34,6 +35,9 @@ #define HASH_STORAGE sizeof(struct sha512_context) #define MAC_STORAGE sizeof(struct hmac_context) +/* This value is used by several IETF protocols for padding */ +#define HMAC_MAGIC htonl(0x878FE1F3) + /* Generic context used by hash functions */ struct hash_context { diff --git a/lib/string.h b/lib/string.h index bf0b7cb0..75cb88dd 100644 --- a/lib/string.h +++ b/lib/string.h @@ -39,6 +39,16 @@ xstrdup(const char *c) return z; } +static inline void +memset32(void *D, u32 val, uint n) +{ + u32 *dst = D; + uint i; + + for (i = 0; i < n; i++) + dst[i] = val; +} + #define ROUTER_ID_64_LENGTH 23 #endif diff --git a/nest/password.c b/nest/password.c index d6e2087f..e4813741 100644 --- a/nest/password.c +++ b/nest/password.c @@ -10,6 +10,7 @@ #include "nest/bird.h" #include "nest/password.h" #include "lib/string.h" +#include "lib/mac.h" struct password_item *last_password_item = NULL; @@ -66,3 +67,17 @@ password_find_by_value(list *l, char *pass, uint size) return NULL; } +uint +max_mac_length(list *l) +{ + struct password_item *pi; + uint val = 0; + + if (!l) + return 0; + + WALK_LIST(pi, *l) + val = MAX(val, mac_type_length(pi->alg)); + + return val; +} diff --git a/nest/password.h b/nest/password.h index 7392389b..f21483c4 100644 --- a/nest/password.h +++ b/nest/password.h @@ -34,4 +34,6 @@ static inline int password_verify(struct password_item *p1, char *p2, uint size) return !memcmp(buf, p2, size); } +uint max_mac_length(list *l); + #endif diff --git a/proto/rip/config.Y b/proto/rip/config.Y index e15599e0..4ec45c7a 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -98,15 +98,29 @@ rip_iface_start: rip_iface_finish: { + /* Default mode is broadcast for RIPv1, multicast for RIPv2 and RIPng */ + if (!RIP_IFACE->mode) + RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ? + RIP_IM_BROADCAST : RIP_IM_MULTICAST; + RIP_IFACE->passwords = get_passwords(); if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords) log(L_WARN "Authentication and password options should be used together"); - /* Default mode is broadcast for RIPv1, multicast for RIPv2 and RIPng */ - if (!RIP_IFACE->mode) - RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ? - RIP_IM_BROADCAST : RIP_IM_MULTICAST; + if (RIP_IFACE->passwords) + { + struct password_item *pass; + WALK_LIST(pass, *RIP_IFACE->passwords) + { + if (pass->alg && (RIP_IFACE->auth_type != RIP_AUTH_CRYPTO)) + cf_error("Password algorithm option requires cryptographic authentication"); + + /* Set default crypto algorithm (MD5) */ + if (!pass->alg && (RIP_IFACE->auth_type == RIP_AUTH_CRYPTO)) + pass->alg = ALG_MD5; + } + } RIP_CFG->min_timeout_time = MIN_(RIP_CFG->min_timeout_time, RIP_IFACE->timeout_time); RIP_CFG->max_garbage_time = MAX_(RIP_CFG->max_garbage_time, RIP_IFACE->garbage_time); diff --git a/proto/rip/packets.c b/proto/rip/packets.c index 381b4771..468927e6 100644 --- a/proto/rip/packets.c +++ b/proto/rip/packets.c @@ -10,7 +10,6 @@ */ #include "rip.h" -#include "lib/md5.h" #include "lib/mac.h" @@ -18,9 +17,7 @@ #define RIP_CMD_RESPONSE 2 /* responding to request */ #define RIP_BLOCK_LENGTH 20 - #define RIP_PASSWD_LENGTH 16 -#define RIP_MD5_LENGTH 16 #define RIP_AF_IPV4 2 #define RIP_AF_AUTH 0xffff @@ -73,7 +70,7 @@ struct rip_auth_tail { u16 must_be_ffff; u16 must_be_0001; - byte auth_data[]; + byte auth_data[0]; }; /* Internal representation of RTE block data */ @@ -221,16 +218,24 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p auth->auth_type = htons(RIP_AUTH_CRYPTO); auth->packet_len = htons(*plen); auth->key_id = pass->id; - auth->auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; + auth->auth_len = mac_type_length(pass->alg); auth->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0; auth->unused1 = 0; auth->unused2 = 0; ifa->csn_ready = 1; + if (pass->alg < ALG_HMAC) + auth->auth_len += sizeof(struct rip_auth_tail); + /* * Note that RFC 4822 is unclear whether auth_len should cover whole * authentication trailer or just auth_data length. * + * FIXME: We should use just auth_data length by default. Currently we put + * the whole auth trailer length in keyed hash case to keep old behavior, + * but we put just auth_data length in the new HMAC case. Note that Quagga + * has config option for this. + * * Crypto sequence numbers are increased by sender in rip_update_csn(). * First CSN should be zero, this is handled by csn_ready. */ @@ -238,14 +243,18 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p struct rip_auth_tail *tail = (void *) ((byte *) pkt + *plen); tail->must_be_ffff = htons(0xffff); tail->must_be_0001 = htons(0x0001); - strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); - *plen += sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; + uint auth_len = mac_type_length(pass->alg); + *plen += sizeof(struct rip_auth_tail) + auth_len; - struct hash_context ctx; - md5_init(&ctx); - md5_update(&ctx, (byte *) pkt, *plen); - memcpy(tail->auth_data, md5_final(&ctx), RIP_MD5_LENGTH); + /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ + if (pass->alg < ALG_HMAC) + strncpy(tail->auth_data, pass->password, auth_len); + else + memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); + + mac_fill(pass->alg, pass->password, pass->length, + (byte *) pkt, *plen, tail->auth_data); return; default: @@ -288,13 +297,25 @@ rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_ DROP("no suitable password found", auth->key_id); uint data_len = ntohs(auth->packet_len); - uint auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; + uint auth_len = mac_type_length(pass->alg); + uint auth_len2 = sizeof(struct rip_auth_tail) + auth_len; - if (data_len + auth_len != *plen) - DROP("packet length mismatch", data_len); + /* + * Ideally, first check should be check for internal consistency: + * (data_len + sizeof(struct rip_auth_tail) + auth->auth_len) != *plen + * + * Second one should check expected code length: + * auth->auth_len != auth_len + * + * But as auth->auth_len has two interpretations, we simplify this + */ - if ((auth->auth_len != RIP_MD5_LENGTH) && (auth->auth_len != auth_len)) - DROP("authentication data length mismatch", auth->auth_len); + if (data_len + auth_len2 != *plen) + DROP("packet length mismatch", *plen); + + /* Warning: two interpretations of auth_len field */ + if ((auth->auth_len != auth_len) && (auth->auth_len != auth_len2)) + DROP("wrong authentication length", auth->auth_len); struct rip_auth_tail *tail = (void *) ((byte *) pkt + data_len); if ((tail->must_be_ffff != htons(0xffff)) || (tail->must_be_0001 != htons(0x0001))) @@ -312,17 +333,18 @@ rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_ return 0; } - char received[RIP_MD5_LENGTH]; - memcpy(received, tail->auth_data, RIP_MD5_LENGTH); - strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); + byte *auth_data = alloca(auth_len); + memcpy(auth_data, tail->auth_data, auth_len); - struct hash_context ctx; - md5_init(&ctx); - md5_update(&ctx, (byte *) pkt, *plen); - char *computed = md5_final(&ctx); + /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ + if (pass->alg < ALG_HMAC) + strncpy(tail->auth_data, pass->password, auth_len); + else + memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); - if (memcmp(received, computed, RIP_MD5_LENGTH)) - DROP("wrong MD5 digest", pass->id); + if (!mac_verify(pass->alg, pass->password, pass->length, + (byte *) pkt, *plen, auth_data)) + DROP("wrong authentication code", pass->id); *plen = data_len; n->csn = rcv_csn; diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 37cfa9ac..7b380097 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -590,7 +590,7 @@ rip_iface_update_buffers(struct rip_iface *ifa) ifa->tx_plen = tbsize - headers; if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) - ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH; + ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH + max_mac_length(ifa->cf->passwords); } static inline void @@ -702,12 +702,11 @@ rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_ifa ifa->cf = new; + rip_iface_update_buffers(ifa); + if (ifa->next_regular > (now + new->update_time)) ifa->next_regular = now + (random() % new->update_time) + 1; - if ((new->tx_length != old->tx_length) || (new->rx_buffer != old->rx_buffer)) - rip_iface_update_buffers(ifa); - if (new->check_link != old->check_link) rip_iface_update_state(ifa); diff --git a/proto/rip/rip.h b/proto/rip/rip.h index f245e612..b24d9536 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -40,7 +40,7 @@ #define RIP_NG_PORT 521 /* RIPng */ #define RIP_MAX_PKT_LENGTH 532 /* 512 + IP4_HEADER_LENGTH */ -#define RIP_AUTH_TAIL_LENGTH 20 /* 4 + MD5 length */ +#define RIP_AUTH_TAIL_LENGTH 4 /* Without auth_data */ #define RIP_DEFAULT_ECMP_LIMIT 16 #define RIP_DEFAULT_INFINITY 16 From 29239ba2bbee3e9ec7d17793b25936a1bfc795ca Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 27 Oct 2016 20:58:21 +0200 Subject: [PATCH 34/37] OSPF: Use message authentication interface Based on former commit from Pavel Tvrdik --- proto/ospf/config.Y | 14 ++++++ proto/ospf/iface.c | 24 ++++++++-- proto/ospf/ospf.h | 21 +++++---- proto/ospf/packet.c | 108 ++++++++++++++++++++------------------------ 4 files changed, 94 insertions(+), 73 deletions(-) diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index c859960f..7b35b191 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -42,6 +42,20 @@ ospf_iface_finish(void) if ((ip->autype == OSPF_AUTH_NONE) && (ip->passwords != NULL)) log(L_WARN "Password option without authentication option does not make sense"); + + if (ip->passwords) + { + struct password_item *pass; + WALK_LIST(pass, *ip->passwords) + { + if (pass->alg && (ip->autype != OSPF_AUTH_CRYPT)) + cf_error("Password algorithm option requires cryptographic authentication"); + + /* Set default OSPF crypto algorithms */ + if (!pass->alg && (ip->autype == OSPF_AUTH_CRYPT)) + pass->alg = ospf_cfg_is_v2() ? ALG_MD5 : ALG_HMAC_SHA256; + } + } } static void diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 67ae094d..280fa4c1 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -9,6 +9,7 @@ */ #include "ospf.h" +#include "nest/password.h" const char *ospf_is_names[] = { @@ -51,6 +52,18 @@ ifa_tx_length(struct ospf_iface *ifa) return ifa->cf->tx_length ?: ifa->iface->mtu; } +static inline uint +ifa_tx_hdrlen(struct ospf_iface *ifa) +{ + uint hlen = SIZE_OF_IP_HEADER; + + /* Relevant just for OSPFv2 */ + if (ifa->autype == OSPF_AUTH_CRYPT) + hlen += max_mac_length(ifa->passwords); + + return hlen; +} + static inline uint ifa_bufsize(struct ospf_iface *ifa) { @@ -67,11 +80,7 @@ ifa_flood_queue_size(struct ospf_iface *ifa) int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen) { - plen += SIZE_OF_IP_HEADER; - - /* This is relevant just for OSPFv2 */ - if (ifa->autype == OSPF_AUTH_CRYPT) - plen += OSPF_AUTH_CRYPT_SIZE; + plen += ifa->tx_hdrlen; if (plen <= ifa->sk->tbsize) return 0; @@ -574,6 +583,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i ifa->stub = ospf_iface_stubby(ip, addr); ifa->ioprob = OSPF_I_OK; ifa->tx_length = ifa_tx_length(ifa); + ifa->tx_hdrlen = ifa_tx_hdrlen(ifa); ifa->check_link = ip->check_link; ifa->ecmp_weight = ip->ecmp_weight; ifa->check_ttl = (ip->ttl_security == 1); @@ -684,6 +694,7 @@ ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip) ifa->deadint = ip->deadint; ifa->inftransdelay = ip->inftransdelay; ifa->tx_length = ospf_is_v2(p) ? IP4_MIN_MTU : IP6_MIN_MTU; + ifa->tx_hdrlen = ifa_tx_hdrlen(ifa); ifa->autype = ip->autype; ifa->passwords = ip->passwords; ifa->instance_id = ip->instance_id; @@ -824,6 +835,9 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* Update passwords */ ifa->passwords = new->passwords; + /* Update header length */ + ifa->tx_hdrlen = ifa_tx_hdrlen(ifa); + /* Remaining options are just for proper interfaces */ if (ifa->type == OSPF_IT_VLINK) return 1; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index ca30e4e0..81c610d5 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -180,11 +180,7 @@ struct ospf_iface_patt #define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ u8 instance_id; - u8 autype; /* Not really used in OSPFv3 */ -#define OSPF_AUTH_NONE 0 -#define OSPF_AUTH_SIMPLE 1 -#define OSPF_AUTH_CRYPT 2 -#define OSPF_AUTH_CRYPT_SIZE 16 + u8 autype; /* OSPF_AUTH_*, not really used in OSPFv3 */ u8 strictnbma; u8 check_link; u8 ecmp_weight; @@ -334,6 +330,7 @@ struct ospf_iface u8 marked; /* Used in OSPF reconfigure, 2 for force restart */ u16 rxbuf; /* Buffer size */ u16 tx_length; /* Soft TX packet length limit, usually MTU */ + u16 tx_hdrlen; /* Expected packet header length, less than tx_length */ u8 check_link; /* Whether iface link change is used */ u8 ecmp_weight; /* Weight used for ECMP */ u8 link_lsa_suppression; /* Suppression of Link-LSA origination */ @@ -424,6 +421,11 @@ struct ospf_neighbor #define ISM_UNLOOP 5 /* Link up */ #define ISM_DOWN 6 /* Interface down */ +/* OSPF authentication types */ +#define OSPF_AUTH_NONE 0 +#define OSPF_AUTH_SIMPLE 1 +#define OSPF_AUTH_CRYPT 2 + /* OSPF neighbor states */ #define NEIGHBOR_DOWN 0 @@ -455,7 +457,6 @@ struct ospf_neighbor #define TRANS_WAIT 2 /* Waiting before the end of translation */ - /* Generic option flags */ #define OPT_V6 0x01 /* OSPFv3, LSA relevant for IPv6 routing calculation */ #define OPT_E 0x02 /* Related to AS-external LSAs */ @@ -491,7 +492,7 @@ struct ospf_packet u8 autype; /* Undefined for OSPFv3 */ }; -struct ospf_md5 +struct ospf_auth_crypto { u16 zero; u8 keyid; @@ -502,7 +503,7 @@ struct ospf_md5 union ospf_auth { u8 password[8]; - struct ospf_md5 md5; + struct ospf_auth_crypto c32; }; /* Packet types */ @@ -896,7 +897,6 @@ void ospf_sh_neigh_info(struct ospf_neighbor *n); /* packet.c */ void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type); -uint ospf_pkt_maxsize(struct ospf_iface *ifa); int ospf_rx_hook(sock * sk, uint size); // void ospf_tx_hook(sock * sk); void ospf_err_hook(sock * sk, int err); @@ -905,6 +905,9 @@ void ospf_send_to(struct ospf_iface *ifa, ip_addr ip); void ospf_send_to_agt(struct ospf_iface *ifa, u8 state); void ospf_send_to_bdr(struct ospf_iface *ifa); +static inline uint ospf_pkt_maxsize(struct ospf_iface *ifa) +{ return ifa->tx_length - ifa->tx_hdrlen; } + static inline void ospf_send_to_all(struct ospf_iface *ifa) { ospf_send_to(ifa, ifa->all_routers); } diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index a2fbd089..6b6a97a4 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -32,25 +32,12 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) pkt->autype = ifa->autype; } -uint -ospf_pkt_maxsize(struct ospf_iface *ifa) -{ - uint headers = SIZE_OF_IP_HEADER; - - /* Relevant just for OSPFv2 */ - if (ifa->autype == OSPF_AUTH_CRYPT) - headers += OSPF_AUTH_CRYPT_SIZE; - - return ifa->tx_length - headers; -} - /* We assume OSPFv2 in ospf_pkt_finalize() */ static void -ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) +ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen) { - struct password_item *passwd = NULL; + struct password_item *pass = NULL; union ospf_auth *auth = (void *) (pkt + 1); - uint plen = ntohs(pkt->length); pkt->checksum = 0; pkt->autype = ifa->autype; @@ -62,25 +49,25 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) switch (ifa->autype) { case OSPF_AUTH_SIMPLE: - passwd = password_find(ifa->passwords, 1); - if (!passwd) + pass = password_find(ifa->passwords, 1); + if (!pass) { log(L_ERR "No suitable password found for authentication"); return; } - strncpy(auth->password, passwd->password, sizeof(auth->password)); + strncpy(auth->password, pass->password, sizeof(auth->password)); case OSPF_AUTH_NONE: { void *body = (void *) (auth + 1); - uint blen = plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth); + uint blen = *plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth); pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet), body, blen, NULL); } break; case OSPF_AUTH_CRYPT: - passwd = password_find(ifa->passwords, 0); - if (!passwd) + pass = password_find(ifa->passwords, 0); + if (!pass) { log(L_ERR "No suitable password found for authentication"); return; @@ -101,20 +88,25 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) ifa->csn_use = now; - auth->md5.zero = 0; - auth->md5.keyid = passwd->id; - auth->md5.len = OSPF_AUTH_CRYPT_SIZE; - auth->md5.csn = htonl(ifa->csn); + uint auth_len = mac_type_length(pass->alg); + byte *auth_tail = ((byte *) pkt + *plen); + *plen += auth_len; - void *tail = ((void *) pkt) + plen; - char password[OSPF_AUTH_CRYPT_SIZE]; - strncpy(password, passwd->password, sizeof(password)); + ASSERT(*plen < ifa->sk->tbsize); - struct hash_context ctx; - md5_init(&ctx); - md5_update(&ctx, (char *) pkt, plen); - md5_update(&ctx, password, OSPF_AUTH_CRYPT_SIZE); - memcpy((byte *) tail, md5_final(&ctx), MD5_SIZE); + auth->c32.zero = 0; + auth->c32.keyid = pass->id; + auth->c32.len = auth_len; + auth->c32.csn = htonl(ifa->csn); + + /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */ + if (pass->alg < ALG_HMAC) + strncpy(auth_tail, pass->password, auth_len); + else + memset32(auth_tail, HMAC_MAGIC, auth_len / 4); + + mac_fill(pass->alg, pass->password, pass->length, + (byte *) pkt, *plen, auth_tail); break; default: @@ -155,13 +147,19 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ return 1; case OSPF_AUTH_CRYPT: - if (auth->md5.len != OSPF_AUTH_CRYPT_SIZE) - DROP("invalid MD5 digest length", auth->md5.len); + pass = password_find_by_id(ifa->passwords, auth->c32.keyid); + if (!pass) + DROP("no suitable password found", auth->c32.keyid); - if (plen + OSPF_AUTH_CRYPT_SIZE > len) - DROP("length mismatch", len); + uint auth_len = mac_type_length(pass->alg); - u32 rcv_csn = ntohl(auth->md5.csn); + if (plen + auth->c32.len > len) + DROP("packet length mismatch", len); + + if (auth->c32.len != auth_len) + DROP("wrong authentication length", auth->c32.len); + + u32 rcv_csn = ntohl(auth->c32.csn); if (n && (rcv_csn < n->csn)) // DROP("lower sequence number", rcv_csn); { @@ -172,22 +170,19 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ return 0; } - pass = password_find_by_id(ifa->passwords, auth->md5.keyid); - if (!pass) - DROP("no suitable password found", auth->md5.keyid); + byte *auth_tail = ((byte *) pkt) + plen; + byte *auth_data = alloca(auth_len); + memcpy(auth_data, auth_tail, auth_len); - byte *tail = ((byte *) pkt) + plen; - char received[OSPF_AUTH_CRYPT_SIZE]; - memcpy(received, tail, OSPF_AUTH_CRYPT_SIZE); - strncpy(tail, pass->password, OSPF_AUTH_CRYPT_SIZE); + /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */ + if (pass->alg < ALG_HMAC) + strncpy(auth_tail, pass->password, auth_len); + else + memset32(auth_tail, HMAC_MAGIC, auth_len / 4); - struct hash_context ctx; - md5_init(&ctx); - md5_update(&ctx, (byte *) pkt, plen + OSPF_AUTH_CRYPT_SIZE); - char *computed = md5_final(&ctx); - - if (memcmp(received, computed, OSPF_AUTH_CRYPT_SIZE)) - DROP("wrong MD5 digest", pass->id); + if (!mac_verify(pass->alg, pass->password, pass->length, + (byte *) pkt, plen + auth_len, auth_data)) + DROP("wrong authentication code", pass->id); if (n) n->csn = rcv_csn; @@ -473,15 +468,10 @@ ospf_send_to(struct ospf_iface *ifa, ip_addr dst) { sock *sk = ifa->sk; struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf; - int plen = ntohs(pkt->length); + uint plen = ntohs(pkt->length); if (ospf_is_v2(ifa->oa->po)) - { - if (ifa->autype == OSPF_AUTH_CRYPT) - plen += OSPF_AUTH_CRYPT_SIZE; - - ospf_pkt_finalize(ifa, pkt); - } + ospf_pkt_finalize(ifa, pkt, &plen); int done = sk_send_to(sk, plen, dst, 0); if (!done) From e03dc6a984555e3c943735d50376cada2220bac8 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sun, 30 Oct 2016 23:51:23 +0100 Subject: [PATCH 35/37] BFD: Authentication Implement BFD authentication (part of RFC 5880). Supports plaintext passwords and cryptographic MD5 / SHA-1 authentication. Based on former commit from Pavel Tvrdik --- doc/bird.sgml | 41 +++++++- proto/bfd/bfd.c | 2 + proto/bfd/bfd.h | 19 +++- proto/bfd/config.Y | 46 ++++++++- proto/bfd/packets.c | 245 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 333 insertions(+), 20 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 7c34c208..6af0e0f6 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -672,7 +672,7 @@ agreement"). authentication is enabled, authentication can be enabled by separate, protocol-dependent authentication none + No passwords are sent in BFD packets. This is the default value. + + authentication simple + Every packet carries 16 bytes of password. Received packets lacking this + password are ignored. This authentication mechanism is very weak. + + authentication [meticulous] keyed md5|sha1 + An authentication code is appended to each packet. The cryptographic + algorithm is keyed MD5 or keyed SHA-1. Note that the algorithm is common + for all keys (on one interface), in contrast to OSPF or RIP, where it + is a per-key option. Passwords (keys) are not sent open via network. + + The password "text" + Specifies a password used for authentication. See common option for detailed description. Note that + password option Example diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index e7be6a8a..79135fae 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -316,6 +316,7 @@ bfd_session_timeout(struct bfd_session *s) s->rem_min_rx_int = 1; s->rem_demand_mode = 0; s->rem_detect_mult = 0; + s->rx_csn_known = 0; s->poll_active = 0; s->poll_scheduled = 0; @@ -429,6 +430,7 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface * s->rem_min_rx_int = 1; s->detect_mult = ifa->cf->multiplier; s->passive = ifa->cf->passive; + s->tx_csn = random_u32(); s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0); s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0); diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h index 9b61be64..46e09879 100644 --- a/proto/bfd/bfd.h +++ b/proto/bfd/bfd.h @@ -14,6 +14,7 @@ #include "nest/iface.h" #include "nest/protocol.h" #include "nest/route.h" +#include "nest/password.h" #include "conf/conf.h" #include "lib/hash.h" #include "lib/resource.h" @@ -52,6 +53,8 @@ struct bfd_iface_config u32 idle_tx_int; u8 multiplier; u8 passive; + u8 auth_type; /* Authentication type (BFD_AUTH_*) */ + list *passwords; /* Passwords for authentication */ }; struct bfd_neighbor @@ -114,7 +117,7 @@ struct bfd_session u8 passive; u8 poll_active; u8 poll_scheduled; - + u8 loc_state; u8 rem_state; u8 loc_diag; @@ -141,6 +144,11 @@ struct bfd_session list request_list; /* List of client requests (struct bfd_request) */ bird_clock_t last_state_change; /* Time of last state change */ u8 notify_running; /* 1 if notify hooks are running */ + + u8 rx_csn_known; /* Received crypto sequence number is known */ + u32 rx_csn; /* Last received crypto sequence number */ + u32 tx_csn; /* Last transmitted crypto sequence number */ + u32 tx_csn_time; /* Timestamp of last tx_csn change */ }; @@ -172,6 +180,15 @@ extern const char *bfd_state_names[]; #define BFD_FLAG_DEMAND (1 << 1) #define BFD_FLAG_MULTIPOINT (1 << 0) +#define BFD_AUTH_NONE 0 +#define BFD_AUTH_SIMPLE 1 +#define BFD_AUTH_KEYED_MD5 2 +#define BFD_AUTH_METICULOUS_KEYED_MD5 3 +#define BFD_AUTH_KEYED_SHA1 4 +#define BFD_AUTH_METICULOUS_KEYED_SHA1 5 + +extern const u8 bfd_auth_type_to_hash_alg[]; + static inline void bfd_lock_sessions(struct bfd_proto *p) { pthread_spin_lock(&p->lock); } static inline void bfd_unlock_sessions(struct bfd_proto *p) { pthread_spin_unlock(&p->lock); } diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y index 4affb927..73414362 100644 --- a/proto/bfd/config.Y +++ b/proto/bfd/config.Y @@ -22,11 +22,12 @@ extern struct bfd_config *bfd_cf; CF_DECLS CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE, - INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL) + INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION, + NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1) %type bfd_neigh_iface %type bfd_neigh_local -%type bfd_neigh_multihop +%type bfd_neigh_multihop bfd_auth_type CF_GRAMMAR @@ -62,12 +63,35 @@ bfd_proto: bfd_iface_start: { this_ipatt = cfg_allocz(sizeof(struct bfd_iface_config)); + add_tail(&BFD_CFG->patt_list, NODE this_ipatt); init_list(&this_ipatt->ipn_list); BFD_IFACE->min_rx_int = BFD_DEFAULT_MIN_RX_INT; BFD_IFACE->min_tx_int = BFD_DEFAULT_MIN_TX_INT; BFD_IFACE->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT; BFD_IFACE->multiplier = BFD_DEFAULT_MULTIPLIER; + + reset_passwords(); +}; + +bfd_iface_finish: +{ + BFD_IFACE->passwords = get_passwords(); + + if (!BFD_IFACE->auth_type != !BFD_IFACE->passwords) + log(L_WARN "Authentication and password options should be used together"); + + if (BFD_IFACE->passwords) + { + struct password_item *pass; + WALK_LIST(pass, *BFD_IFACE->passwords) + { + if (pass->alg) + cf_error("Password algorithm option not available in BFD protocol"); + + pass->alg = bfd_auth_type_to_hash_alg[BFD_IFACE->auth_type]; + } + } }; bfd_iface_item: @@ -77,6 +101,17 @@ bfd_iface_item: | IDLE TX INTERVAL expr_us { BFD_IFACE->idle_tx_int = $4; } | MULTIPLIER expr { BFD_IFACE->multiplier = $2; } | PASSIVE bool { BFD_IFACE->passive = $2; } + | AUTHENTICATION bfd_auth_type { BFD_IFACE->auth_type = $2; } + | password_list {} + ; + +bfd_auth_type: + NONE { $$ = BFD_AUTH_NONE; } + | SIMPLE { $$ = BFD_AUTH_SIMPLE; } + | KEYED MD5 { $$ = BFD_AUTH_KEYED_MD5; } + | KEYED SHA1 { $$ = BFD_AUTH_KEYED_SHA1; } + | METICULOUS KEYED MD5 { $$ = BFD_AUTH_METICULOUS_KEYED_MD5; } + | METICULOUS KEYED SHA1 { $$ = BFD_AUTH_METICULOUS_KEYED_SHA1; } ; bfd_iface_opts: @@ -89,10 +124,11 @@ bfd_iface_opt_list: | '{' bfd_iface_opts '}' ; -bfd_iface: bfd_iface_start iface_patt_list_nopx bfd_iface_opt_list -{ add_tail(&BFD_CFG->patt_list, NODE this_ipatt); }; +bfd_iface: + bfd_iface_start iface_patt_list_nopx bfd_iface_opt_list bfd_iface_finish; -bfd_multihop: bfd_iface_start bfd_iface_opt_list +bfd_multihop: + bfd_iface_start bfd_iface_opt_list bfd_iface_finish { BFD_CFG->multihop = BFD_IFACE; }; diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c index deb501fc..129db72f 100644 --- a/proto/bfd/packets.c +++ b/proto/bfd/packets.c @@ -5,24 +5,60 @@ */ #include "bfd.h" +#include "lib/mac.h" struct bfd_ctl_packet { - u8 vdiag; /* version and diagnostic */ - u8 flags; /* state and flags */ + u8 vdiag; /* Version and diagnostic */ + u8 flags; /* State and flags */ u8 detect_mult; - u8 length; - u32 snd_id; /* sender ID, aka 'my discriminator' */ - u32 rcv_id; /* receiver ID, aka 'your discriminator' */ + u8 length; /* Whole packet length */ + u32 snd_id; /* Sender ID, aka 'my discriminator' */ + u32 rcv_id; /* Receiver ID, aka 'your discriminator' */ u32 des_min_tx_int; u32 req_min_rx_int; u32 req_min_echo_rx_int; }; +struct bfd_auth +{ + u8 type; /* Authentication type (BFD_AUTH_*) */ + u8 length; /* Authentication section length */ +}; + +struct bfd_simple_auth +{ + u8 type; /* BFD_AUTH_SIMPLE */ + u8 length; /* Length of bfd_simple_auth + pasword length */ + u8 key_id; /* Key ID */ + byte password[0]; /* Password itself, variable length */ +}; + +#define BFD_MAX_PASSWORD_LENGTH 16 + +struct bfd_crypto_auth +{ + u8 type; /* BFD_AUTH_*_MD5 or BFD_AUTH_*_SHA1 */ + u8 length; /* Length of bfd_crypto_auth + hash length */ + u8 key_id; /* Key ID */ + u8 zero; /* Reserved, zero on transmit */ + u32 csn; /* Cryptographic sequence number */ + byte data[0]; /* Authentication key/hash, length 16 or 20 */ +}; + #define BFD_BASE_LEN sizeof(struct bfd_ctl_packet) #define BFD_MAX_LEN 64 +#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) + +#define LOG_PKT(msg, args...) \ + log(L_REMOTE "%s: " msg, p->p.name, args) + +#define LOG_PKT_AUTH(msg, args...) \ + log(L_AUTH "%s: " msg, p->p.name, args) + + static inline u8 bfd_pack_vdiag(u8 version, u8 diag) { return (version << 5) | diag; } @@ -59,6 +95,189 @@ bfd_format_flags(u8 flags, char *buf) return buf; } +const u8 bfd_auth_type_to_hash_alg[] = { + [BFD_AUTH_NONE] = ALG_UNDEFINED, + [BFD_AUTH_SIMPLE] = ALG_UNDEFINED, + [BFD_AUTH_KEYED_MD5] = ALG_MD5, + [BFD_AUTH_METICULOUS_KEYED_MD5] = ALG_MD5, + [BFD_AUTH_KEYED_SHA1] = ALG_SHA1, + [BFD_AUTH_METICULOUS_KEYED_SHA1] = ALG_SHA1, +}; + + +/* Fill authentication section and modifies final length in control section packet */ +static void +bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt) +{ + struct bfd_iface_config *cf = s->ifa->cf; + struct password_item *pass = password_find(cf->passwords, 0); + uint meticulous = 0; + + if (!pass) + { + /* FIXME: This should not happen */ + log(L_ERR "%s: No suitable password found for authentication", p->p.name); + return; + } + + switch (cf->auth_type) + { + case BFD_AUTH_SIMPLE: + { + struct bfd_simple_auth *auth = (void *) (pkt + 1); + uint pass_len = MIN(pass->length, BFD_MAX_PASSWORD_LENGTH); + + auth->type = BFD_AUTH_SIMPLE; + auth->length = sizeof(struct bfd_simple_auth) + pass_len; + auth->key_id = pass->id; + + pkt->flags |= BFD_FLAG_AP; + pkt->length += auth->length; + + memcpy(auth->password, pass->password, pass_len); + return; + } + + case BFD_AUTH_METICULOUS_KEYED_MD5: + case BFD_AUTH_METICULOUS_KEYED_SHA1: + meticulous = 1; + + case BFD_AUTH_KEYED_MD5: + case BFD_AUTH_KEYED_SHA1: + { + struct bfd_crypto_auth *auth = (void *) (pkt + 1); + uint hash_alg = bfd_auth_type_to_hash_alg[cf->auth_type]; + uint hash_len = mac_type_length(pass->alg); + + /* Increase CSN about one time per second */ + u32 new_time = (u64) current_time() >> 20; + if ((new_time != s->tx_csn_time) || meticulous) + { + s->tx_csn++; + s->tx_csn_time = new_time; + } + + DBG("[%I] CSN: %u\n", s->addr, s->last_tx_csn); + + auth->type = cf->auth_type; + auth->length = sizeof(struct bfd_crypto_auth) + hash_len; + auth->key_id = pass->id; + auth->zero = 0; + auth->csn = htonl(s->tx_csn); + + pkt->flags |= BFD_FLAG_AP; + pkt->length += auth->length; + + strncpy(auth->data, pass->password, hash_len); + mac_fill(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth->data); + return; + } + } +} + +static int +bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt) +{ + struct bfd_iface_config *cf = s->ifa->cf; + const char *err_dsc = NULL; + uint err_val = 0; + uint auth_type = 0; + uint meticulous = 0; + + if (pkt->flags & BFD_FLAG_AP) + { + struct bfd_auth *auth = (void *) (pkt + 1); + + if ((pkt->length < (BFD_BASE_LEN + sizeof(struct bfd_auth))) || + (pkt->length < (BFD_BASE_LEN + auth->length))) + DROP("packet length mismatch", pkt->length); + + /* Zero is reserved, we use it as BFD_AUTH_NONE internally */ + if (auth->type == 0) + DROP("reserved authentication type", 0); + + auth_type = auth->type; + } + + if (auth_type != cf->auth_type) + DROP("authentication method mismatch", auth_type); + + switch (auth_type) + { + case BFD_AUTH_NONE: + return 1; + + case BFD_AUTH_SIMPLE: + { + struct bfd_simple_auth *auth = (void *) (pkt + 1); + + if (auth->length < sizeof(struct bfd_simple_auth)) + DROP("wrong authentication length", auth->length); + + struct password_item *pass = password_find_by_id(cf->passwords, auth->key_id); + if (!pass) + DROP("no suitable password found", auth->key_id); + + uint pass_len = MIN(pass->length, BFD_MAX_PASSWORD_LENGTH); + uint auth_len = sizeof(struct bfd_simple_auth) + pass_len; + + if ((auth->length != auth_len) || memcmp(auth->password, pass->password, pass_len)) + DROP("wrong password", pass->id); + + return 1; + } + + case BFD_AUTH_METICULOUS_KEYED_MD5: + case BFD_AUTH_METICULOUS_KEYED_SHA1: + meticulous = 1; + + case BFD_AUTH_KEYED_MD5: + case BFD_AUTH_KEYED_SHA1: + { + struct bfd_crypto_auth *auth = (void *) (pkt + 1); + uint hash_alg = bfd_auth_type_to_hash_alg[cf->auth_type]; + uint hash_len = mac_type_length(hash_alg); + + if (auth->length != (sizeof(struct bfd_crypto_auth) + hash_len)) + DROP("wrong authentication length", auth->length); + + struct password_item *pass = password_find_by_id(cf->passwords, auth->key_id); + if (!pass) + DROP("no suitable password found", auth->key_id); + + /* BFD CSNs are in 32-bit circular number space */ + u32 csn = ntohl(auth->csn); + if (s->rx_csn_known && + (((csn - s->rx_csn) > (3 * s->detect_mult)) || + (meticulous && (csn == s->rx_csn)))) + { + /* We want to report both new and old CSN */ + LOG_PKT_AUTH("Authentication failed for %I - " + "wrong sequence number (rcv %u, old %u)", + s->addr, csn, s->rx_csn); + return 0; + } + + byte *auth_data = alloca(hash_len); + memcpy(auth_data, auth->data, hash_len); + strncpy(auth->data, pass->password, hash_len); + + if (!mac_verify(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth_data)) + DROP("wrong authentication code", pass->id); + + s->rx_csn = csn; + s->rx_csn_known = 1; + + return 1; + } + } + +drop: + LOG_PKT_AUTH("Authentication failed for %I - %s (%u)", + s->addr, err_dsc, err_val); + return 0; +} + void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final) { @@ -85,6 +304,9 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final) else if (s->poll_active) pkt->flags |= BFD_FLAG_POLL; + if (s->ifa->cf->auth_type) + bfd_fill_authentication(p, s, pkt); + if (sk->tbuf != sk->tpos) log(L_WARN "%s: Old packet overwritten in TX buffer", p->p.name); @@ -94,8 +316,6 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final) sk_send_to(sk, pkt->length, s->addr, sk->dport); } -#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) - static int bfd_rx_hook(sock *sk, uint len) { @@ -151,10 +371,9 @@ bfd_rx_hook(sock *sk, uint len) return 1; } - /* FIXME: better authentication handling and message */ - if (pkt->flags & BFD_FLAG_AP) - DROP("authentication not supported", 0); - + /* bfd_check_authentication() has its own error logging */ + if (!bfd_check_authentication(p, s, pkt)) + return 1; u32 old_tx_int = s->des_min_tx_int; u32 old_rx_int = s->rem_min_rx_int; @@ -173,8 +392,8 @@ bfd_rx_hook(sock *sk, uint len) bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int); return 1; - drop: - log(L_REMOTE "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val); +drop: + LOG_PKT("Bad packet from %I - %s (%u)", sk->faddr, err_dsc, err_val); return 1; } From 920a86e8493fe25008f084f67f368aea9b197efd Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 3 Nov 2016 09:53:53 +0100 Subject: [PATCH 36/37] Add missing extern --- lib/mac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mac.h b/lib/mac.h index 5fc216fd..b6f3af52 100644 --- a/lib/mac.h +++ b/lib/mac.h @@ -89,7 +89,7 @@ struct mac_desc { byte *(*hash_final)(struct hash_context *ctx); }; -const struct mac_desc mac_table[ALG_MAX]; +extern const struct mac_desc mac_table[ALG_MAX]; static inline const char *mac_type_name(uint id) { return mac_table[id].name; } From c8cafc8ebb5320ac7c6117c17e6460036f0fdf62 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 8 Nov 2016 17:46:29 +0100 Subject: [PATCH 37/37] Minor code cleanups --- conf/cf-lex.l | 2 +- conf/conf.c | 6 +++--- conf/conf.h | 6 +++--- conf/confbase.Y | 2 +- filter/config.Y | 26 +++++++++++++------------- filter/filter.h | 24 ++++++++++++------------ filter/tree.c | 6 +++--- lib/buffer.h | 16 +++++++++++++++- lib/hash.h | 11 +++++++++++ proto/ospf/rt.c | 4 ++-- sysdep/linux/syspriv.h | 9 +++++++++ sysdep/unix/io.c | 5 ++++- sysdep/unix/main.c | 4 +++- 13 files changed, 80 insertions(+), 41 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index d2aa6402..e9e385a6 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -589,7 +589,7 @@ cf_lex_init(int is_cli, struct config *c) cf_lex_init_kh(); ifs_head = ifs = push_ifs(NULL); - if (!is_cli) + if (!is_cli) { ifs->file_name = c->file_name; ifs->fd = c->file_fd; diff --git a/conf/conf.c b/conf/conf.c index 029814c6..0a4e3f8c 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -85,7 +85,7 @@ int undo_available; /* Undo was not requested from last reconfiguration */ * further use. Returns a pointer to the structure. */ struct config * -config_alloc(byte *name) +config_alloc(const byte *name) { pool *p = rp_new(&root_pool, "Config"); linpool *l = lp_new(p, 4080); @@ -405,7 +405,7 @@ config_confirm(void) * if it's been queued due to another reconfiguration being in progress now, * %CONF_UNQUEUED if a scheduled reconfiguration is removed, %CONF_NOTHING * if there is no relevant configuration to undo (the previous config request - * was config_undo() too) or %CONF_SHUTDOWN if BIRD is in shutdown mode and + * was config_undo() too) or %CONF_SHUTDOWN if BIRD is in shutdown mode and * no new configuration changes are accepted. */ int @@ -530,7 +530,7 @@ cf_error(char *msg, ...) * and we want to preserve it for further use. */ char * -cfg_strdup(char *c) +cfg_strdup(const char *c) { int l = strlen(c) + 1; char *z = cfg_allocu(l); diff --git a/conf/conf.h b/conf/conf.h index 89a2c5b7..41cb434f 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -21,7 +21,7 @@ struct config { list protos; /* Configured protocol instances (struct proto_config) */ list tables; /* Configured routing tables (struct rtable_config) */ list roa_tables; /* Configured ROA tables (struct roa_table_config) */ - list logfiles; /* Configured log fils (sysdep) */ + list logfiles; /* Configured log files (sysdep) */ int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */ char *syslog_name; /* Name used for syslog (NULL -> no syslog) */ @@ -61,7 +61,7 @@ struct config { extern struct config *config; /* Currently active configuration */ extern struct config *new_config; /* Configuration being parsed */ -struct config *config_alloc(byte *name); +struct config *config_alloc(const byte *name); int config_parse(struct config *); int cli_parse(struct config *); void config_free(struct config *); @@ -95,7 +95,7 @@ extern linpool *cfg_mem; #define cfg_alloc(size) lp_alloc(cfg_mem, size) #define cfg_allocu(size) lp_allocu(cfg_mem, size) #define cfg_allocz(size) lp_allocz(cfg_mem, size) -char *cfg_strdup(char *c); +char *cfg_strdup(const char *c); void cfg_copy_list(list *dest, list *src, unsigned node_size); /* Lexer */ diff --git a/conf/confbase.Y b/conf/confbase.Y index c14c23c7..96b32028 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -138,7 +138,7 @@ expr_us: /* Switches */ bool: - expr {$$ = !!$1; } + expr { $$ = !!$1; } | ON { $$ = 1; } | YES { $$ = 1; } | OFF { $$ = 0; } diff --git a/filter/config.Y b/filter/config.Y index 29e3a734..5ea83f81 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -158,7 +158,7 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3) static inline struct f_inst * f_generate_empty(struct f_inst *dyn) -{ +{ struct f_inst *e = f_new_inst(); e->code = 'E'; @@ -261,7 +261,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) if (c1 && c2) { u64 ec; - + if (kind == EC_GENERIC) { ec = ec_generic(key, val2); } @@ -280,7 +280,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) NEW_F_VAL; rv = f_new_inst(); rv->code = 'C'; - rv->a1.p = val; + rv->a1.p = val; val->type = T_EC; val->val.ec = ec; } @@ -355,7 +355,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %type pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body %type fprefix_set %type set_atom switch_atom fprefix fprefix_s fipa -%type decls declsn one_decl function_params +%type decls declsn one_decl function_params %type bgp_path bgp_path_tail1 bgp_path_tail2 CF_GRAMMAR @@ -391,7 +391,7 @@ type: | CLIST { $$ = T_CLIST; } | ECLIST { $$ = T_ECLIST; } | LCLIST { $$ = T_LCLIST; } - | type SET { + | type SET { switch ($1) { case T_INT: case T_PAIR: @@ -506,7 +506,7 @@ function_def: } function_params function_body { $2->def = $5; $2->aux2 = $4; - DBG("Hmm, we've got one function here - %s\n", $2->name); + DBG("Hmm, we've got one function here - %s\n", $2->name); cf_pop_scope(); } ; @@ -652,7 +652,7 @@ fprefix: fprefix_s { $$ = $1; } | fprefix_s '+' { $$ = $1; $$.val.px.len |= LEN_PLUS; } | fprefix_s '-' { $$ = $1; $$.val.px.len |= LEN_MINUS; } - | fprefix_s '{' NUM ',' NUM '}' { + | fprefix_s '{' NUM ',' NUM '}' { if (! ((0 <= $3) && ($3 <= $5) && ($5 <= MAX_PREFIX_LENGTH))) cf_error("Invalid prefix pattern range: {%d, %d}.", $3, $5); $$ = $1; $$.val.px.len |= LEN_RANGE | ($3 << 16) | ($5 << 8); } @@ -671,7 +671,7 @@ switch_body: /* EMPTY */ { $$ = NULL; } t->data = $4; $$ = f_merge_items($1, $2); } - | switch_body ELSECOL cmds { + | switch_body ELSECOL cmds { struct f_tree *t = f_new_tree(); t->from.type = t->to.type = T_VOID; t->right = t; @@ -683,7 +683,7 @@ switch_body: /* EMPTY */ { $$ = NULL; } /* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */ bgp_path_expr: - symbol { $$ = $1; } + symbol { $$ = $1; } | '(' term ')' { $$ = $2; } ; @@ -836,8 +836,8 @@ term: | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; } | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; } - | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; } - | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } + | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; } + | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; } | FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; } @@ -892,7 +892,7 @@ print_list: /* EMPTY */ { $$ = NULL; } } ; -var_listn: term { +var_listn: term { $$ = f_new_inst(); $$->code = 's'; $$->a1.p = NULL; @@ -960,7 +960,7 @@ cmd: $$ = f_new_inst(); $$->code = P('P','S'); $$->a1.p = $3; - } + } | UNSET '(' rtadot dynamic_attr ')' ';' { $$ = $4; $$->aux = EAF_TYPE_UNDEF | EAF_TEMP; diff --git a/filter/filter.h b/filter/filter.h index e0f34e4f..049ceb76 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -35,7 +35,7 @@ struct f_inst { /* Instruction */ /* Not enough fields in f_inst for three args used by roa_check() */ struct f_inst_roa_check { struct f_inst i; - struct roa_table_config *rtc; + struct roa_table_config *rtc; }; struct f_inst3 { @@ -65,7 +65,7 @@ struct f_val { uint i; u64 ec; lcomm lc; - /* ip_addr ip; Folded into prefix */ + /* ip_addr ip; Folded into prefix */ struct f_prefix px; char *s; struct f_tree *t; @@ -190,16 +190,16 @@ void val_format(struct f_val v, buffer *buf); #define T_PREFIX_SET 0x81 -#define SA_FROM 1 -#define SA_GW 2 -#define SA_NET 3 -#define SA_PROTO 4 -#define SA_SOURCE 5 -#define SA_SCOPE 6 -#define SA_CAST 7 -#define SA_DEST 8 -#define SA_IFNAME 9 -#define SA_IFINDEX 10 +#define SA_FROM 1 +#define SA_GW 2 +#define SA_NET 3 +#define SA_PROTO 4 +#define SA_SOURCE 5 +#define SA_SCOPE 6 +#define SA_CAST 7 +#define SA_DEST 8 +#define SA_IFNAME 9 +#define SA_IFINDEX 10 struct f_tree { diff --git a/filter/tree.c b/filter/tree.c index 1196e630..f8379fa8 100644 --- a/filter/tree.c +++ b/filter/tree.c @@ -63,7 +63,7 @@ tree_compare(const void *p1, const void *p2) * build_tree * @from: degenerated tree (linked by @tree->left) to be transformed into form suitable for find_tree() * - * Transforms denerated tree into balanced tree. + * Transforms degenerated tree into balanced tree. */ struct f_tree * build_tree(struct f_tree *from) @@ -162,7 +162,7 @@ void tree_format(struct f_tree *t, buffer *buf) { buffer_puts(buf, "["); - + tree_node_format(t, buf); if (buf->pos == buf->end) @@ -171,6 +171,6 @@ tree_format(struct f_tree *t, buffer *buf) /* Undo last separator */ if (buf->pos[-1] != '[') buf->pos -= 2; - + buffer_puts(buf, "]"); } diff --git a/lib/buffer.h b/lib/buffer.h index cf073e88..2a53f211 100644 --- a/lib/buffer.h +++ b/lib/buffer.h @@ -1,3 +1,17 @@ +/* + * BIRD Library -- Generic Buffer Structure + * + * (c) 2013 Ondrej Zajicek + * (c) 2013 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_BUFFER_H_ +#define _BIRD_BUFFER_H_ + +#include "lib/resource.h" +#include "sysdep/config.h" #define BUFFER(type) struct { type *data; uint used, size; } @@ -32,4 +46,4 @@ #define BUFFER_FLUSH(v) ({ (v).used = 0; }) - +#endif /* _BIRD_BUFFER_H_ */ diff --git a/lib/hash.h b/lib/hash.h index fc5fea14..4239b1d8 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -1,4 +1,14 @@ +/* + * BIRD Library -- Generic Hash Table + * + * (c) 2013 Ondrej Zajicek + * (c) 2013 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ +#ifndef _BIRD_HASH_H_ +#define _BIRD_HASH_H_ #define HASH(type) struct { type **data; uint count, order; } #define HASH_TYPE(v) typeof(** (v).data) @@ -178,3 +188,4 @@ #define HASH_WALK_FILTER_END } while (0) +#endif diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 8b3feda9..19f2d074 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -678,7 +678,7 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry which may be later used as the next hop. */ /* In OSPFv2, en->lb is set here. In OSPFv3, en->lb is just cleared here, - it is set in process_prefixes() to any global addres in the area */ + it is set in process_prefixes() to any global address in the area */ en->lb = IPA_NONE; en->lb_id = 0; @@ -930,7 +930,7 @@ ospf_rt_sum_tr(struct ospf_area *oa) } } -/* Decide about originating or flushing summary LSAs for condended area networks */ +/* Decide about originating or flushing summary LSAs for condensed area networks */ static int decide_anet_lsa(struct ospf_area *oa, struct area_net *anet, struct ospf_area *anet_oa) { diff --git a/sysdep/linux/syspriv.h b/sysdep/linux/syspriv.h index d2ba95dd..8b210f06 100644 --- a/sysdep/linux/syspriv.h +++ b/sysdep/linux/syspriv.h @@ -1,4 +1,11 @@ +#ifndef _BIRD_SYSPRIV_H_ +#define _BIRD_SYSPRIV_H_ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include #include #include @@ -70,3 +77,5 @@ drop_uid(uid_t uid) if (setresuid(uid, uid, uid) < 0) die("setresuid: %m"); } + +#endif /* _BIRD_SYSPRIV_H_ */ diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 873b5805..644a4fcd 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -9,7 +9,9 @@ /* Unfortunately, some glibc versions hide parts of RFC 3542 API if _GNU_SOURCE is not defined. */ -#define _GNU_SOURCE 1 +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif #include #include @@ -2046,6 +2048,7 @@ watchdog_stop(void) volatile int async_config_flag; /* Asynchronous reconfiguration/dump scheduled */ volatile int async_dump_flag; +volatile int async_shutdown_flag; void io_init(void) diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 35bc3fd1..8aa19fce 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -8,7 +8,9 @@ #undef LOCAL_DEBUG -#define _GNU_SOURCE 1 +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif #include #include