From 77edab640925e8d1131f0d00b41de055129ff99f Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 1 May 2015 14:40:56 +0200 Subject: [PATCH 001/165] OSPF: Redesign LSA checksumming New LSA checksumming code separates generic Fletcher-16 and OSPF-specific code and avoids back and forth endianity conversions, making it much more readable and also several times faster. --- lib/fletcher16.h | 196 ++++++++++++++++++++++++++++++++++++++++++ proto/ospf/lsalib.c | 149 +++++--------------------------- proto/ospf/lsalib.h | 4 +- proto/ospf/lsupd.c | 4 +- proto/ospf/topology.c | 6 +- 5 files changed, 226 insertions(+), 133 deletions(-) create mode 100644 lib/fletcher16.h diff --git a/lib/fletcher16.h b/lib/fletcher16.h new file mode 100644 index 00000000..0b245a71 --- /dev/null +++ b/lib/fletcher16.h @@ -0,0 +1,196 @@ +/* + * BIRD Library -- Fletcher-16 checksum + * + * (c) 2015 Ondrej Zajicek + * (c) 2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +/** + * DOC: Fletcher-16 checksum + * + * Fletcher-16 checksum is a position-dependent checksum algorithm used for + * error-detection e.g. in OSPF LSAs. + * + * To generate Fletcher-16 checksum, zero the checksum field in data, initialize + * the context by fletcher16_init(), process the data by fletcher16_update(), + * compute the checksum value by fletcher16_final() and store it to the checksum + * field in data by put_u16() (or other means involving htons() conversion). + * + * To verify Fletcher-16 checksum, initialize the context by fletcher16_init(), + * process the data by fletcher16_update(), compute a passing checksum by + * fletcher16_compute() and check if it is zero. + */ + +#ifndef _BIRD_FLETCHER16_H_ +#define _BIRD_FLETCHER16_H_ + +#include "nest/bird.h" + + +struct fletcher16_context +{ + int c0, c1; +}; + + +/** + * fletcher16_init - initialize Fletcher-16 context + * @ctx: the context + */ +static inline void +fletcher16_init(struct fletcher16_context *ctx) +{ + ctx->c0 = ctx->c1 = 0; +} + +/** + * fletcher16_update - process data to Fletcher-16 context + * @ctx: the context + * @buf: data buffer + * @len: data length + * + * fletcher16_update() reads data from the buffer @buf and updates passing sums + * in the context @ctx. It may be used multiple times for multiple blocks of + * checksummed data. + */ +static inline void +fletcher16_update(struct fletcher16_context *ctx, const u8* buf, int len) +{ + /* + * The Fletcher-16 sum is essentially a sequence of + * ctx->c1 += ctx->c0 += *buf++, modulo 255. + * + * In the inner loop, we eliminate modulo operation and we do some loop + * unrolling. MODX is the maximal number of steps that can be done without + * modulo before overflow, see RFC 1008 for details. We use a bit smaller + * value to cover for initial steps due to loop unrolling. + */ + +#define MODX 4096 + + int blen, i; + + blen = len % 4; + len -= blen; + + for (i = 0; i < blen; i++) + ctx->c1 += ctx->c0 += *buf++; + + do { + blen = MIN(len, MODX); + len -= blen; + + for (i = 0; i < blen; i += 4) + { + ctx->c1 += ctx->c0 += *buf++; + ctx->c1 += ctx->c0 += *buf++; + ctx->c1 += ctx->c0 += *buf++; + ctx->c1 += ctx->c0 += *buf++; + } + + ctx->c0 %= 255; + ctx->c1 %= 255; + + } while (len); +} + + +/** + * fletcher16_update_n32 - process data to Fletcher-16 context, with endianity adjustment + * @ctx: the context + * @buf: data buffer + * @len: data length + * + * fletcher16_update_n32() works like fletcher16_update(), except it applies + * 32-bit host/network endianity swap to the data before they are processed. + * I.e., it assumes that the data is a sequence of u32 that must be converted by + * ntohl() or htonl() before processing. The @buf need not to be aligned, but + * its length (@len) must be multiple of 4. Note that on big endian systems the + * host endianity is the same as the network endianity, therefore there is no + * endianity swap. + */ +static inline void +fletcher16_update_n32(struct fletcher16_context *ctx, const u8* buf, int len) +{ + /* See fletcher16_update() for details */ + + int blen, i; + + do { + blen = MIN(len, MODX); + len -= blen; + + for (i = 0; i < blen; i += 4) + { +#ifdef CPU_BIG_ENDIAN + ctx->c1 += ctx->c0 += *buf++; + ctx->c1 += ctx->c0 += *buf++; + ctx->c1 += ctx->c0 += *buf++; + ctx->c1 += ctx->c0 += *buf++; +#else + ctx->c1 += ctx->c0 += buf[3]; + ctx->c1 += ctx->c0 += buf[2]; + ctx->c1 += ctx->c0 += buf[1]; + ctx->c1 += ctx->c0 += buf[0]; + buf += 4; +#endif + } + + ctx->c0 %= 255; + ctx->c1 %= 255; + + } while (len); +} + +/** + * fletcher16_final - compute final Fletcher-16 checksum value + * @ctx: the context + * @len: total data length + * @pos: offset in data where the checksum will be stored + * + * fletcher16_final() computes the final checksum value and returns it. + * The caller is responsible for storing it in the appropriate position. + * The checksum value depends on @len and @pos, but only their difference + * (i.e. the offset from the end) is significant. + * + * The checksum value is represented as u16, although it is defined as two + * consecutive bytes. We treat them as one u16 in big endian / network order. + * I.e., the returned value is in the form that would be returned by get_u16() + * from the checksum field in the data buffer, therefore the caller should use + * put_u16() or an explicit host-to-network conversion when storing it to the + * checksum field in the data buffer. + * + * Note that the returned checksum value is always nonzero. + */ +static inline u16 +fletcher16_final(struct fletcher16_context *ctx, int len, int pos) +{ + int x = ((len - pos - 1) * ctx->c0 - ctx->c1) % 255; + if (x <= 0) + x += 255; + + int y = 510 - ctx->c0 - x; + if (y > 255) + y -= 255; + + return (x << 8) | y; +} + + +/** + * fletcher16_compute - compute Fletcher-16 sum for verification + * @ctx: the context + * + * fletcher16_compute() returns a passing Fletcher-16 sum for processed data. + * If the data contains the proper Fletcher-16 checksum value, the returned + * value is zero. + */ +static inline u16 +fletcher16_compute(struct fletcher16_context *ctx) +{ + return (ctx->c0 << 8) | ctx->c1; +} + +#endif diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index ce6fb178..66a3a23d 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -2,14 +2,15 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip - * (c) 2009--2014 Ondrej Zajicek - * (c) 2009--2014 CZ.NIC z.s.p.o. + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" +#include "lib/fletcher16.h" #ifndef CPU_BIG_ENDIAN void @@ -150,145 +151,41 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain) } - -/* void -buf_dump(const char *hdr, const byte *buf, int blen) +lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body) { - char b2[1024]; - char *bp; - int first = 1; - int i; - - const char *lhdr = hdr; - - bp = b2; - for(i = 0; i < blen; i++) - { - if ((i > 0) && ((i % 16) == 0)) - { - *bp = 0; - log(L_WARN "%s\t%s", lhdr, b2); - lhdr = ""; - bp = b2; - } - - bp += snprintf(bp, 1022, "%02x ", buf[i]); - - } - - *bp = 0; - log(L_WARN "%s\t%s", lhdr, b2); -} -*/ - -#define MODX 4102 /* larges signed value without overflow */ - -/* Fletcher Checksum -- Refer to RFC1008. */ -#define MODX 4102 -#define LSA_CHECKSUM_OFFSET 15 - -/* FIXME This is VERY uneficient, I have huge endianity problems */ -void -lsasum_calculate(struct ospf_lsa_header *h, void *body) -{ - u16 length = h->length; - - // log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length); - lsa_hton_hdr(h, h); - lsa_hton_body1(body, length - sizeof(struct ospf_lsa_header)); + struct fletcher16_context ctx; + struct ospf_lsa_header hdr; + u16 len = lsa->length; /* - char buf[1024]; - memcpy(buf, h, sizeof(struct ospf_lsa_header)); - memcpy(buf + sizeof(struct ospf_lsa_header), body, length - sizeof(struct ospf_lsa_header)); - buf_dump("CALC", buf, length); - */ + * lsa and body are in the host order, we need to compute Fletcher-16 checksum + * for data in the network order. We also skip the initial age field. + */ - (void) lsasum_check(h, body, 1); + lsa_hton_hdr(lsa, &hdr); + hdr.checksum = 0; - // log(L_WARN "Checksum result %4x", h->checksum); - - lsa_ntoh_hdr(h, h); - lsa_ntoh_body1(body, length - sizeof(struct ospf_lsa_header)); + fletcher16_init(&ctx); + fletcher16_update(&ctx, (u8 *) &hdr + 2, sizeof(struct ospf_lsa_header) - 2); + fletcher16_update_n32(&ctx, body, len - sizeof(struct ospf_lsa_header)); + lsa->checksum = fletcher16_final(&ctx, len, OFFSETOF(struct ospf_lsa_header, checksum)); } -/* - * Calculates the Fletcher checksum of an OSPF LSA. - * - * If 'update' is non-zero, the checkbytes (X and Y in RFC905) are calculated - * and the checksum field in the header is updated. The return value is the - * checksum as placed in the header (in network byte order). - * - * If 'update' is zero, only C0 and C1 are calculated and the header is kept - * intact. The return value is a combination of C0 and C1; if the return value - * is exactly zero the checksum is considered valid, any non-zero value is - * invalid. - * - * Note that this function expects the input LSA to be in network byte order. - */ u16 -lsasum_check(struct ospf_lsa_header *h, void *body, int update) +lsa_verify_checksum(const void *lsa_n, int lsa_len) { - u8 *sp, *ep, *p, *q, *b; - int c0 = 0, c1 = 0; - int x, y; - u16 length; - - b = body; - sp = (char *) h; - sp += 2; /* Skip Age field */ - length = ntohs(h->length) - 2; - if (update) h->checksum = 0; - - for (ep = sp + length; sp < ep; sp = q) - { /* Actually MODX is very large, do we need the for-cyclus? */ - q = sp + MODX; - if (q > ep) - q = ep; - for (p = sp; p < q; p++) - { - /* - * I count with bytes from header and than from body - * but if there is no body, it's appended to header - * (probably checksum in update receiving) and I go on - * after header - */ - if ((b == NULL) || (p < (u8 *) (h + 1))) - { - c0 += *p; - } - else - { - c0 += *(b + (p - (u8 *) (h + 1))); - } - - c1 += c0; - } - c0 %= 255; - c1 %= 255; - } + struct fletcher16_context ctx; - if (!update) { - /* - * When testing the checksum, we don't need to calculate x and y. The - * checksum passes if c0 and c1 are both 0. - */ - return (c0 << 8) | (c1 & 0xff); - } + /* The whole LSA is at lsa_n in net order, we just skip initial age field */ - x = (int)((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255; - if (x <= 0) - x += 255; - y = 510 - c0 - x; - if (y > 255) - y -= 255; + fletcher16_init(&ctx); + fletcher16_update(&ctx, (u8 *) lsa_n + 2, lsa_len - 2); - ((u8 *) & h->checksum)[0] = x; - ((u8 *) & h->checksum)[1] = y; - return h->checksum; + return fletcher16_compute(&ctx) == 0; } + int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2) /* Return codes from point of view of l1 */ diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index 4ad770e8..ae6af044 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -46,9 +46,9 @@ static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p) int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa); +void lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body); +u16 lsa_verify_checksum(const void *lsa_n, int lsa_len); -void lsasum_calculate(struct ospf_lsa_header *header, void *body); -u16 lsasum_check(struct ospf_lsa_header *h, void *body, int update); #define CMP_NEWER 1 #define CMP_SAME 0 #define CMP_OLDER -1 diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 6e7dce26..c6a734ca 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -530,8 +530,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n", lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum); - /* RFC 2328 13. (1) - validate LSA checksum */ - if ((lsa_n->checksum == 0) || (lsasum_check(lsa_n, NULL, 0) != 0)) + /* RFC 2328 13. (1) - verify LSA checksum */ + if ((lsa_n->checksum == 0) || !lsa_verify_checksum(lsa_n, lsa_len)) SKIP("invalid checksum"); /* RFC 2328 13. (2) */ diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 0613d34d..8119cfa6 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -129,7 +129,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls en->lsa.age = 0; en->init_age = 0; en->inst_time = now; - lsasum_calculate(&en->lsa, en->lsa_body); + lsa_generate_checksum(&en->lsa, en->lsa_body); OSPF_TRACE(D_EVENTS, "Advancing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); @@ -238,7 +238,7 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa en->lsa.age = 0; en->init_age = 0; en->inst_time = now; - lsasum_calculate(&en->lsa, en->lsa_body); + lsa_generate_checksum(&en->lsa, en->lsa_body); OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x", en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn); @@ -382,7 +382,7 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en) en->lsa.age = 0; en->init_age = 0; en->inst_time = now; - lsasum_calculate(&en->lsa, en->lsa_body); + lsa_generate_checksum(&en->lsa, en->lsa_body); ospf_flood_lsa(p, en, NULL); } From 315f23a0470112ced04badbb117bc7854ee53e06 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 9 May 2015 18:50:15 +0200 Subject: [PATCH 002/165] Add bitfield route attribute type --- filter/filter.c | 82 ++++++++++++++++++++++++++++++++++--------------- nest/route.h | 5 ++- nest/rt-attr.c | 29 +++++++++++++++++ 3 files changed, 90 insertions(+), 26 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index a4f12330..3b14fc0c 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -462,7 +462,7 @@ static int f_flags; static inline void f_rte_cow(void) { - *f_rte = rte_cow(*f_rte); + *f_rte = rte_cow(*f_rte); } /* @@ -479,7 +479,7 @@ f_rta_cow(void) /* Store old rta to free it later */ f_old_rta = (*f_rte)->attrs; - /* + /* * Alloc new rta, do shallow copy and update rte. Fields eattrs * and nexthops of rta are shared with f_old_rta (they will be * copied when the cached rta will be obtained at the end of @@ -516,6 +516,9 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; #define ACCESS_RTE \ do { if (!f_rte) runtime("No route to access"); } while (0) +#define BITFIELD_MASK(what) \ + (1u << (what->a2.i >> 24)) + /** * interpret * @what: filter to interpret @@ -526,7 +529,7 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; * Each instruction has 4 fields: code (which is instruction code), * aux (which is extension to instruction code, typically type), * arg1 and arg2 - arguments. Depending on instruction, arguments - * are either integers, or pointers to instruction trees. Common + * are either integers, or pointers to instruction trees. Common * instructions like +, that have two expressions as arguments use * TWOARGS macro to get both of them evaluated. * @@ -585,7 +588,7 @@ interpret(struct f_inst *what) default: runtime( "Usage of unknown type" ); } break; - + case '&': case '|': ARG(v1, a1.p); @@ -625,7 +628,7 @@ interpret(struct f_inst *what) if (v1.type == T_INT) { ipv4_used = 0; key = v1.val.i; - } + } else if (v1.type == T_QUAD) { ipv4_used = 1; key = v1.val.i; } @@ -724,7 +727,7 @@ interpret(struct f_inst *what) #endif runtime( "Assigning to variable of incompatible type" ); } - *vp = v2; + *vp = v2; break; /* some constants have value in a2, some in *a1.p, strange. */ @@ -864,12 +867,14 @@ interpret(struct f_inst *what) ACCESS_RTE; { eattr *e = NULL; + u16 code = what->a2.i; + if (!(f_flags & FF_FORCE_TMPATTR)) - e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); - if (!e) - e = ea_find( (*f_tmp_attrs), what->a2.i ); + e = ea_find((*f_rte)->attrs->eattrs, code); + if (!e) + e = ea_find((*f_tmp_attrs), code); if ((!e) && (f_flags & FF_FORCE_TMPATTR)) - e = ea_find( (*f_rte)->attrs->eattrs, what->a2.i ); + e = ea_find((*f_rte)->attrs->eattrs, code); if (!e) { /* A special case: undefined int_set looks like empty int_set */ @@ -878,8 +883,9 @@ interpret(struct f_inst *what) res.val.ad = adata_empty(f_pool, 0); break; } + /* The same special case for ec_set */ - else if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) { + if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) { res.type = T_ECLIST; res.val.ad = adata_empty(f_pool, 0); break; @@ -912,6 +918,10 @@ interpret(struct f_inst *what) res.type = T_PATH; res.val.ad = e->u.ptr; break; + case EAF_TYPE_BITFIELD: + res.type = T_BOOL; + res.val.i = !!(e->u.data & BITFIELD_MASK(what)); + break; case EAF_TYPE_INT_SET: res.type = T_CLIST; res.val.ad = e->u.ptr; @@ -933,13 +943,15 @@ interpret(struct f_inst *what) ONEARG; { struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr)); + u16 code = what->a2.i; l->next = NULL; l->flags = EALF_SORTED; l->count = 1; - l->attrs[0].id = what->a2.i; + l->attrs[0].id = code; l->attrs[0].flags = 0; l->attrs[0].type = what->aux | EAF_ORIGINATED; + switch (what->aux & EAF_TYPE_MASK) { case EAF_TYPE_INT: if (v1.type != T_INT) @@ -978,6 +990,26 @@ interpret(struct f_inst *what) runtime( "Setting path attribute to non-path value" ); l->attrs[0].u.ptr = v1.val.ad; break; + case EAF_TYPE_BITFIELD: + if (v1.type != T_BOOL) + runtime( "Setting bit in bitfield attribute to non-bool value" ); + { + /* First, we have to find the old value */ + eattr *e = NULL; + if (!(f_flags & FF_FORCE_TMPATTR)) + e = ea_find((*f_rte)->attrs->eattrs, code); + if (!e) + e = ea_find((*f_tmp_attrs), code); + if ((!e) && (f_flags & FF_FORCE_TMPATTR)) + e = ea_find((*f_rte)->attrs->eattrs, code); + u32 data = e ? e->u.data : 0; + + if (v1.val.i) + l->attrs[0].u.data = data | BITFIELD_MASK(what); + else + l->attrs[0].u.data = data & ~BITFIELD_MASK(what);; + } + break; case EAF_TYPE_INT_SET: if (v1.type != T_CLIST) runtime( "Setting clist attribute to non-clist value" ); @@ -1073,7 +1105,7 @@ interpret(struct f_inst *what) res = interpret(what->a2.p); if (res.type == T_RETURN) return res; - res.type &= ~T_RETURN; + res.type &= ~T_RETURN; break; case P('c','v'): /* Clear local variables */ for (sym = what->a1.p; sym != NULL; sym = sym->aux2) @@ -1090,7 +1122,7 @@ interpret(struct f_inst *what) debug( "No else statement?\n"); break; } - } + } /* It is actually possible to have t->data NULL */ res = interpret(t->data); @@ -1184,10 +1216,10 @@ interpret(struct f_inst *what) runtime("Can't add set"); else if (!arg_set) res.val.ad = int_set_add(f_pool, v1.val.ad, n); - else + else res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad); break; - + case 'd': if (!arg_set) res.val.ad = int_set_del(f_pool, v1.val.ad, n); @@ -1209,7 +1241,7 @@ interpret(struct f_inst *what) { /* Extended community list */ int arg_set = 0; - + /* v2.val is either EC or EC-set */ if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) arg_set = 1; @@ -1226,10 +1258,10 @@ interpret(struct f_inst *what) runtime("Can't add set"); else if (!arg_set) res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec); - else + else res.val.ad = ec_set_union(f_pool, v1.val.ad, v2.val.ad); break; - + case 'd': if (!arg_set) res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec); @@ -1353,7 +1385,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) } break; - case 'c': + case 'c': switch (f1->aux) { case T_PREFIX_SET: @@ -1381,7 +1413,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) return 0; break; - case 'V': + case 'V': if (strcmp((char *) f1->a2.p, (char *) f2->a2.p)) return 0; break; @@ -1399,12 +1431,12 @@ i_same(struct f_inst *f1, struct f_inst *f2) case 'r': ONEARG; break; case P('c','p'): ONEARG; break; case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */ - ONEARG; + ONEARG; if (!i_same(f1->a2.p, f2->a2.p)) - return 0; + return 0; f2->a2.p = f1->a2.p; break; - case P('c','v'): break; /* internal instruction */ + case P('c','v'): break; /* internal instruction */ case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break; case P('i','M'): TWOARGS; break; case P('A','p'): TWOARGS; break; @@ -1414,7 +1446,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case P('R','C'): TWOARGS; /* Does not really make sense - ROA check resuls may change anyway */ - if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, + if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, ((struct f_inst_roa_check *) f2)->rtc->name)) return 0; break; diff --git a/nest/route.h b/nest/route.h index 5ee04a30..fccc571b 100644 --- a/nest/route.h +++ b/nest/route.h @@ -417,13 +417,15 @@ typedef struct eattr { #define EA_CODE_MASK 0xffff #define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */ +#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */ #define EAF_TYPE_MASK 0x0f /* Mask with this to get type */ -#define EAF_TYPE_INT 0x01 /* 32-bit signed integer number */ +#define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */ #define EAF_TYPE_OPAQUE 0x02 /* Opaque byte string (not filterable) */ #define EAF_TYPE_IP_ADDRESS 0x04 /* IP address */ #define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */ #define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */ +#define EAF_TYPE_BITFIELD 0x09 /* 32-bit embedded bitfield */ #define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */ #define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */ #define EAF_TYPE_UNDEF 0x0f /* `force undefined' entry */ @@ -469,6 +471,7 @@ void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffe int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */ unsigned int ea_hash(ea_list *e); /* Calculate 16-bit hash value */ ea_list *ea_append(ea_list *to, ea_list *what); +void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max); int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */ static inline int mpnh_same(struct mpnh *x, struct mpnh *y) diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 09691bf1..938b2b44 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -563,6 +563,32 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED) return GA_UNKNOWN; } +void +ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max) +{ + byte *bound = buf + bufsize - 32; + u32 data = a->u.data; + int i; + + for (i = min; i < max; i++) + if ((data & (1u << i)) && names[i]) + { + if (buf > bound) + { + strcpy(buf, " ..."); + return; + } + + buf += bsprintf(buf, " %s", names[i]); + data &= ~(1u << i); + } + + if (data) + bsprintf(buf, " %08x", data); + + return; +} + static inline void opaque_format(struct adata *ad, byte *buf, unsigned int size) { @@ -665,6 +691,9 @@ ea_show(struct cli *c, eattr *e) case EAF_TYPE_AS_PATH: as_path_format(ad, pos, end - pos); break; + case EAF_TYPE_BITFIELD: + bsprintf(pos, "%08x", e->u.data); + break; case EAF_TYPE_INT_SET: ea_show_int_set(c, ad, 1, pos, buf, end); return; From 9fdf9d29b6b570205c36934aab7e50539e042102 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 12 May 2015 16:42:22 +0200 Subject: [PATCH 003/165] KRT: Add support for plenty of kernel route metrics Linux kernel route metrics (RTA_METRICS netlink route attribute) are represented and accessible as new route attributes: krt_mtu, krt_window, krt_rtt, krt_rttvar, krt_sstresh, krt_cwnd, krt_advmss, krt_reordering, krt_hoplimit, krt_initcwnd, krt_rto_min, krt_initrwnd, krt_quickack, krt_lock_mtu, krt_lock_window, krt_lock_rtt, krt_lock_rttvar, krt_lock_sstresh, krt_lock_cwnd, krt_lock_advmss, krt_lock_reordering, krt_lock_hoplimit, krt_lock_rto_min, krt_feature_ecn, krt_feature_allfrag --- doc/bird.sgml | 2 +- nest/route.h | 6 ++ nest/rt-attr.c | 76 +++++++++++++ sysdep/bsd/krt-sys.h | 2 + sysdep/linux/krt-sys.h | 53 ++++++++++ sysdep/linux/netlink.Y | 40 ++++++- sysdep/linux/netlink.c | 235 ++++++++++++++++++++++++++++++++--------- sysdep/unix/krt.c | 12 +-- sysdep/unix/krt.h | 3 +- 9 files changed, 366 insertions(+), 63 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index fc5fc9ae..1c2dda4b 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2248,7 +2248,7 @@ these attributes: ip (Linux) The preferred source address. Used in source address selection for - outgoing packets. Have to be one of IP addresses of the router. + outgoing packets. Has to be one of the IP addresses of the router. int (Linux) The realm of the route. Can be used for traffic classification. diff --git a/nest/route.h b/nest/route.h index fccc571b..87f10ae3 100644 --- a/nest/route.h +++ b/nest/route.h @@ -461,8 +461,14 @@ static inline void rt_lock_source(struct rte_src *src) { src->uc++; } static inline void rt_unlock_source(struct rte_src *src) { src->uc--; } void rt_prune_sources(void); +struct ea_walk_state { + ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */ + eattr *ea; /* Current eattr, initially NULL */ + u32 visited[4]; /* Bitfield, limiting max to 128 */ +}; eattr *ea_find(ea_list *, unsigned ea); +eattr *ea_walk(struct ea_walk_state *s, uint id, uint max); int ea_get_int(ea_list *, unsigned ea, int def); void ea_dump(ea_list *); void ea_sort(ea_list *); /* Sort entries in all sub-lists */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 938b2b44..c5537208 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -307,6 +307,82 @@ ea_find(ea_list *e, unsigned id) return a; } +/** + * ea_walk - walk through extended attributes + * @s: walk state structure + * @id: start of attribute ID interval + * @max: length of attribute ID interval + * + * Given an extended attribute list, ea_walk() walks through the list looking + * for first occurrences of attributes with ID in specified interval from @id to + * (@id + @max - 1), returning pointers to found &eattr structures, storing its + * walk state in @s for subsequent calls. + + * The function ea_walk() is supposed to be called in a loop, with initially + * zeroed walk state structure @s with filled the initial extended attribute + * list, returning one found attribute in each call or %NULL when no other + * attribute exists. The extended attribute list or the arguments should not be + * modified between calls. The maximum value of @max is 128. + */ +eattr * +ea_walk(struct ea_walk_state *s, uint id, uint max) +{ + ea_list *e = s->eattrs; + eattr *a = s->ea; + eattr *a_max; + + max = id + max; + + if (a) + goto step; + + for (; e; e = e->next) + { + if (e->flags & EALF_BISECT) + { + int l, r, m; + + l = 0; + r = e->count - 1; + while (l < r) + { + m = (l+r) / 2; + if (e->attrs[m].id < id) + l = m + 1; + else + r = m; + } + a = e->attrs + l; + } + else + a = e->attrs; + + step: + a_max = e->attrs + e->count; + for (; a < a_max; a++) + if ((a->id >= id) && (a->id < max)) + { + int n = a->id - id; + + if (BIT32_TEST(s->visited, n)) + continue; + + BIT32_SET(s->visited, n); + + if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF) + continue; + + s->eattrs = e; + s->ea = a; + return a; + } + else if (e->flags & EALF_BISECT) + break; + } + + return NULL; +} + /** * ea_get_int - fetch an integer attribute * @e: attribute list diff --git a/sysdep/bsd/krt-sys.h b/sysdep/bsd/krt-sys.h index 9c0d4972..2c6e35c5 100644 --- a/sysdep/bsd/krt-sys.h +++ b/sysdep/bsd/krt-sys.h @@ -44,5 +44,7 @@ struct krt_state { static inline void krt_sys_init(struct krt_proto *p UNUSED) { } +static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { } + #endif diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index 7e97968a..e32e4fe1 100644 --- a/sysdep/linux/krt-sys.h +++ b/sysdep/linux/krt-sys.h @@ -32,6 +32,59 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; } /* Kernel routes */ +#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0x10) +#define EA_KRT_REALM EA_CODE(EAP_KRT, 0x11) + + +#define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */ +#define KRT_METRICS_OFFSET 0x20 /* Offset of EA_KRT_* vs RTAX_* */ + +#define KRT_FEATURES_MAX 4 + +/* + * Following attributes are parts of RTA_METRICS kernel route attribute, their + * ids must be consistent with their RTAX_* constants (+ KRT_METRICS_OFFSET) + */ +#define EA_KRT_METRICS EA_CODE(EAP_KRT, 0x20) /* Dummy one */ +#define EA_KRT_LOCK EA_CODE(EAP_KRT, 0x21) +#define EA_KRT_MTU EA_CODE(EAP_KRT, 0x22) +#define EA_KRT_WINDOW EA_CODE(EAP_KRT, 0x23) +#define EA_KRT_RTT EA_CODE(EAP_KRT, 0x24) +#define EA_KRT_RTTVAR EA_CODE(EAP_KRT, 0x25) +#define EA_KRT_SSTRESH EA_CODE(EAP_KRT, 0x26) +#define EA_KRT_CWND EA_CODE(EAP_KRT, 0x27) +#define EA_KRT_ADVMSS EA_CODE(EAP_KRT, 0x28) +#define EA_KRT_REORDERING EA_CODE(EAP_KRT, 0x29) +#define EA_KRT_HOPLIMIT EA_CODE(EAP_KRT, 0x2a) +#define EA_KRT_INITCWND EA_CODE(EAP_KRT, 0x2b) +#define EA_KRT_FEATURES EA_CODE(EAP_KRT, 0x2c) +#define EA_KRT_RTO_MIN EA_CODE(EAP_KRT, 0x2d) +#define EA_KRT_INITRWND EA_CODE(EAP_KRT, 0x2e) +#define EA_KRT_QUICKACK EA_CODE(EAP_KRT, 0x2f) + +/* Bits of EA_KRT_LOCK, also based on RTAX_* constants */ +#define EA_KRT_LOCK_MTU EA_KRT_LOCK | EA_BIT(0x2) +#define EA_KRT_LOCK_WINDOW EA_KRT_LOCK | EA_BIT(0x3) +#define EA_KRT_LOCK_RTT EA_KRT_LOCK | EA_BIT(0x4) +#define EA_KRT_LOCK_RTTVAR EA_KRT_LOCK | EA_BIT(0x5) +#define EA_KRT_LOCK_SSTHRESH EA_KRT_LOCK | EA_BIT(0x6) +#define EA_KRT_LOCK_CWND EA_KRT_LOCK | EA_BIT(0x7) +#define EA_KRT_LOCK_ADVMSS EA_KRT_LOCK | EA_BIT(0x8) +#define EA_KRT_LOCK_REORDERING EA_KRT_LOCK | EA_BIT(0x9) +#define EA_KRT_LOCK_HOPLIMIT EA_KRT_LOCK | EA_BIT(0xa) +// define EA_KRT_LOCK_INITCWND EA_KRT_LOCK | EA_BIT(0xb) +// define EA_KRT_LOCK_FEATURES EA_KRT_LOCK | EA_BIT(0xc) +#define EA_KRT_LOCK_RTO_MIN EA_KRT_LOCK | EA_BIT(0xd) +// define EA_KRT_LOCK_INITRWND EA_KRT_LOCK | EA_BIT(0xe) + +/* Bits of EA_KRT_FEATURES, based on RTAX_FEATURE_* constants */ +#define EA_KRT_FEATURE_ECN EA_KRT_FEATURES | EA_BIT(0x0) +// define EA_KRT_FEATURE_SACK EA_KRT_FEATURES | EA_BIT(0x1) +// define EA_KRT_FEATURE_TSTAMP EA_KRT_FEATURES | EA_BIT(0x2) +#define EA_KRT_FEATURE_ALLFRAG EA_KRT_FEATURES | EA_BIT(0x3) + + + #define NL_NUM_TABLES 256 struct krt_params { diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index b0e35151..f8137e23 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -10,7 +10,12 @@ CF_HDR CF_DECLS -CF_KEYWORDS(KERNEL, TABLE, KRT_PREFSRC, KRT_REALM) +CF_KEYWORDS(KERNEL, TABLE, KRT_PREFSRC, KRT_REALM, KRT_MTU, KRT_WINDOW, KRT_RTT, + KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING, + KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK, + KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR, + KRT_LOCK_SSTRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING, + KRT_LOCK_HOPLIMIT, KRT_LOCK_RTO_MIN, KRT_FEATURE_ECN, KRT_FEATURE_ALLFRAG) CF_GRAMMAR @@ -24,8 +29,37 @@ kern_sys_item: } ; -CF_ADDTO(dynamic_attr, KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); }) -CF_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); }) +CF_ADDTO(dynamic_attr, KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); }) +CF_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); }) + +CF_ADDTO(dynamic_attr, KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU); }) +CF_ADDTO(dynamic_attr, KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); }) +CF_ADDTO(dynamic_attr, KRT_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTT); }) +CF_ADDTO(dynamic_attr, KRT_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTTVAR); }) +CF_ADDTO(dynamic_attr, KRT_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SSTRESH); }) +CF_ADDTO(dynamic_attr, KRT_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_CWND); }) +CF_ADDTO(dynamic_attr, KRT_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_ADVMSS); }) +CF_ADDTO(dynamic_attr, KRT_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REORDERING); }) +CF_ADDTO(dynamic_attr, KRT_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_HOPLIMIT); }) +CF_ADDTO(dynamic_attr, KRT_INITCWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITCWND); }) +CF_ADDTO(dynamic_attr, KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTO_MIN); }) +CF_ADDTO(dynamic_attr, KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITRWND); }) +CF_ADDTO(dynamic_attr, KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_QUICKACK); }) + +CF_ADDTO(dynamic_attr, KRT_LOCK_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_MTU); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_WINDOW); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTT); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTTVAR); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_SSTHRESH); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_CWND); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_ADVMSS); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_REORDERING); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_HOPLIMIT); }) +CF_ADDTO(dynamic_attr, KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTO_MIN); }) + +CF_ADDTO(dynamic_attr, KRT_FEATURE_ECN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ECN); }) +CF_ADDTO(dynamic_attr, KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ALLFRAG); }) + CF_CODE diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 48dd8bab..72837ce0 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -238,21 +238,24 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) return 1; } -void -nl_add_attr(struct nlmsghdr *h, unsigned bufsize, unsigned code, - void *data, unsigned dlen) +struct rtattr * +nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen) { - unsigned len = RTA_LENGTH(dlen); - unsigned pos = NLMSG_ALIGN(h->nlmsg_len); - struct rtattr *a; + uint pos = NLMSG_ALIGN(h->nlmsg_len); + uint len = RTA_LENGTH(dlen); if (pos + len > bufsize) bug("nl_add_attr: packet buffer overflow"); - a = (struct rtattr *)((char *)h + pos); + + struct rtattr *a = (struct rtattr *)((char *)h + pos); a->rta_type = code; a->rta_len = len; h->nlmsg_len = pos + len; - memcpy(RTA_DATA(a), data, dlen); + + if (dlen > 0) + memcpy(RTA_DATA(a), data, dlen); + + return a; } static inline void @@ -268,48 +271,58 @@ nl_add_attr_ipa(struct nlmsghdr *h, unsigned bufsize, int code, ip_addr ipa) nl_add_attr(h, bufsize, code, &ipa, sizeof(ipa)); } -#define RTNH_SIZE (sizeof(struct rtnexthop) + sizeof(struct rtattr) + sizeof(ip_addr)) +static inline struct rtattr * +nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code) +{ + return nl_add_attr(h, bufsize, code, NULL, 0); +} static inline void -add_mpnexthop(char *buf, ip_addr ipa, unsigned iface, unsigned char weight) +nl_close_attr(struct nlmsghdr *h, struct rtattr *a) { - struct rtnexthop *nh = (void *) buf; - struct rtattr *rt = (void *) (buf + sizeof(*nh)); - nh->rtnh_len = RTNH_SIZE; - nh->rtnh_flags = 0; - nh->rtnh_hops = weight; - nh->rtnh_ifindex = iface; - rt->rta_len = sizeof(*rt) + sizeof(ipa); - rt->rta_type = RTA_GATEWAY; - ipa_hton(ipa); - memcpy(buf + sizeof(*nh) + sizeof(*rt), &ipa, sizeof(ipa)); + a->rta_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)a; } +static inline struct rtnexthop * +nl_open_nexthop(struct nlmsghdr *h, uint bufsize) +{ + uint pos = NLMSG_ALIGN(h->nlmsg_len); + uint len = RTNH_LENGTH(0); + + if (pos + len > bufsize) + bug("nl_open_nexthop: packet buffer overflow"); + + h->nlmsg_len = pos + len; + + return (void *)h + pos; +} + +static inline void +nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh) +{ + nh->rtnh_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)nh; +} static void nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh) { - unsigned len = sizeof(struct rtattr); - unsigned pos = NLMSG_ALIGN(h->nlmsg_len); - char *buf = (char *)h + pos; - struct rtattr *rt = (void *) buf; - buf += len; - + struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH); + for (; nh; nh = nh->next) - { - len += RTNH_SIZE; - if (pos + len > bufsize) - bug("nl_add_multipath: packet buffer overflow"); + { + struct rtnexthop *rtnh = nl_open_nexthop(h, bufsize); - add_mpnexthop(buf, nh->gw, nh->iface->index, nh->weight); - buf += RTNH_SIZE; - } + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = nh->weight; + rtnh->rtnh_ifindex = nh->iface->index; - rt->rta_type = RTA_MULTIPATH; - rt->rta_len = len; - h->nlmsg_len = pos + len; -} + nl_add_attr_u32(h, bufsize, RTA_GATEWAY, nh->gw); + nl_close_nexthop(h, rtnh); + } + + nl_close_attr(h, a); +} static struct mpnh * nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) @@ -374,6 +387,47 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) return first; } +static void +nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max) +{ + struct rtattr *a = nl_open_attr(h, bufsize, RTA_METRICS); + int t; + + for (t = 1; t < max; t++) + if (metrics[0] & (1 << t)) + nl_add_attr_u32(h, bufsize, t, metrics[t]); + + nl_close_attr(h, a); +} + +static int +nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) +{ + struct rtattr *a = RTA_DATA(hdr); + int len = RTA_PAYLOAD(hdr); + + metrics[0] = 0; + for (; RTA_OK(a, len); a = RTA_NEXT(a, len)) + { + if (a->rta_type == RTA_UNSPEC) + continue; + + if (a->rta_type >= max) + continue; + + if (RTA_PAYLOAD(a) != 4) + return -1; + + metrics[0] |= 1 << a->rta_type; + metrics[a->rta_type] = *(u32 *)RTA_DATA(a); + } + + if (len > 0) + return -1; + + return 0; +} + /* * Scanning of interfaces @@ -617,7 +671,7 @@ nh_bufsize(struct mpnh *nh) { int rv = 0; for (; nh != NULL; nh = nh->next) - rv += RTNH_SIZE; + rv += RTNH_LENGTH(RTA_LENGTH(sizeof(ip_addr))); return rv; } @@ -630,7 +684,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) struct { struct nlmsghdr h; struct rtmsg r; - char buf[128 + nh_bufsize(a->nexthops)]; + char buf[128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops)]; } r; DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new); @@ -649,13 +703,8 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); - u32 metric = 0; - if (new && e->attrs->source == RTS_INHERIT) - metric = e->u.krt.metric; if (ea = ea_find(eattrs, EA_KRT_METRIC)) - metric = ea->u.data; - if (metric != 0) - nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric); + nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data); if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); @@ -663,6 +712,22 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) if (ea = ea_find(eattrs, EA_KRT_REALM)) nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data); + + u32 metrics[KRT_METRICS_MAX]; + metrics[0] = 0; + + struct ea_walk_state ews = { .eattrs = eattrs }; + while (ea = ea_walk(&ews, EA_KRT_METRICS, KRT_METRICS_MAX)) + { + int id = ea->id - EA_KRT_METRICS; + metrics[0] |= 1 << id; + metrics[id] = ea->u.data; + } + + if (metrics[0]) + nl_add_metrics(&r.h, sizeof(r), metrics, KRT_METRICS_MAX); + + /* a->iface != NULL checked in krt_capable() for router and device routes */ switch (a->dest) @@ -834,7 +899,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) net->n.prefix, net->n.pxlen); return; } - + break; } @@ -896,7 +961,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) e->u.krt.type = i->rtm_type; if (a[RTA_PRIORITY]) - memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); + memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); else e->u.krt.metric = 0; @@ -932,6 +997,38 @@ nl_parse_route(struct nlmsghdr *h, int scan) memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4); } + if (a[RTA_METRICS]) + { + u32 metrics[KRT_METRICS_MAX]; + ea_list *ea = alloca(sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr)); + int t, n = 0; + + if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0) + { + log(L_ERR "KRT: Received route %I/%d with strange RTA_METRICS attribute", + net->n.prefix, net->n.pxlen); + return; + } + + for (t = 1; t < KRT_METRICS_MAX; t++) + if (metrics[0] & (1 << t)) + { + ea->attrs[n].id = EA_CODE(EAP_KRT, KRT_METRICS_OFFSET + t); + ea->attrs[n].flags = 0; + ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */ + ea->attrs[n].u.data = metrics[t]; + n++; + } + + if (n > 0) + { + ea->next = ra.eattrs; + ea->flags = EALF_SORTED; + ea->count = n; + ra.eattrs = ea; + } + } + if (scan) krt_got_route(p, e); else @@ -1130,6 +1227,50 @@ krt_sys_copy_config(struct krt_config *d, struct krt_config *s) d->sys.table_id = s->sys.table_id; } +static const char *krt_metrics_names[KRT_METRICS_MAX] = { + NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss", + "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack" +}; + +static const char *krt_features_names[KRT_FEATURES_MAX] = { + "ecn", NULL, NULL, "allfrag" +}; + +int +krt_sys_get_attr(eattr *a, byte *buf, int buflen UNUSED) +{ + switch (a->id) + { + case EA_KRT_PREFSRC: + bsprintf(buf, "prefsrc"); + return GA_NAME; + + case EA_KRT_REALM: + bsprintf(buf, "realm"); + return GA_NAME; + + case EA_KRT_LOCK: + buf += bsprintf(buf, "lock:"); + ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX); + return GA_FULL; + + case EA_KRT_FEATURES: + buf += bsprintf(buf, "features:"); + ea_format_bitfield(a, buf, buflen, krt_features_names, 0, KRT_FEATURES_MAX); + return GA_FULL; + + default:; + int id = (int)EA_ID(a->id) - KRT_METRICS_OFFSET; + if (id > 0 && id < KRT_METRICS_MAX) + { + bsprintf(buf, "%s", krt_metrics_names[id]); + return GA_NAME; + } + + return GA_UNKNOWN; + } +} + void diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index efdf4bdd..cfb623ce 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -1191,7 +1191,7 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src) } static int -krt_get_attr(eattr * a, byte * buf, int buflen UNUSED) +krt_get_attr(eattr *a, byte *buf, int buflen) { switch (a->id) { @@ -1203,16 +1203,8 @@ krt_get_attr(eattr * a, byte * buf, int buflen UNUSED) bsprintf(buf, "metric"); return GA_NAME; - case EA_KRT_PREFSRC: - bsprintf(buf, "prefsrc"); - return GA_NAME; - - case EA_KRT_REALM: - bsprintf(buf, "realm"); - return GA_NAME; - default: - return GA_UNKNOWN; + return krt_sys_get_attr(a, buf, buflen); } } diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index f0fd6261..1940cbcd 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -28,8 +28,6 @@ struct kif_proto; #define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0) #define EA_KRT_METRIC EA_CODE(EAP_KRT, 1) -#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 2) -#define EA_KRT_REALM EA_CODE(EAP_KRT, 3) /* Whenever we recognize our own routes, we allow learing of foreign routes */ @@ -131,6 +129,7 @@ void krt_sys_copy_config(struct krt_config *, struct krt_config *); int krt_capable(rte *e); void krt_do_scan(struct krt_proto *); void krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs); +int krt_sys_get_attr(eattr *a, byte *buf, int buflen); /* kif sysdep */ From 38e835dede88158d97c3039ed22faabed79c7181 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 13 May 2015 13:19:26 +0200 Subject: [PATCH 004/165] Fix in the last commit --- sysdep/linux/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 72837ce0..71f58554 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -316,7 +316,7 @@ nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh) rtnh->rtnh_hops = nh->weight; rtnh->rtnh_ifindex = nh->iface->index; - nl_add_attr_u32(h, bufsize, RTA_GATEWAY, nh->gw); + nl_add_attr_ipa(h, bufsize, RTA_GATEWAY, nh->gw); nl_close_nexthop(h, rtnh); } From 86f567e13c2202fc3c3a1ce49f9a35220a50f117 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 16 May 2015 20:17:59 +0200 Subject: [PATCH 005/165] Fix minor issue in pipe route propagation In some circumstances during reconfiguration, routes propagated by pipes to other tables may hang there even after the primary routes are removed. There is already a workaround for this issue in the code which removes these stale routes by flush process when source protocols are shut down. This patch is a cleaner fix and allows to simplify the flush process --- nest/rt-table.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index dbe0c50d..a8fe137c 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -347,11 +347,13 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm } static void -rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) +rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, ea_list *tmpa, int refeed) { - // struct proto *p = ah->proto; + struct proto *p = ah->proto; struct proto_stats *stats = ah->stats; + rte *new = new0; + rte *old = old0; rte *new_free = NULL; rte *old_free = NULL; @@ -369,7 +371,7 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list * FIXME - this is broken because 'configure soft' may change * filters but keep routes. Refeed is expected to be called after * change of the filters and with old == new, therefore we do not - * even try to run the filter on an old route, This may lead to + * even try to run the filter on an old route, This may lead to * 'spurious withdraws' but ensure that there are no 'missing * withdraws'. * @@ -386,9 +388,26 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list if (old && !refeed) old = export_filter(ah, old, &old_free, NULL, 1); - /* FIXME - This is broken because of incorrect 'old' value (see above) */ if (!new && !old) + { + /* + * As mentioned above, 'old' value may be incorrect in some race conditions. + * We generally ignore it with the exception of withdraw to pipe protocol. + * In that case we rather propagate unfiltered withdraws regardless of + * export filters to ensure that when a protocol is flushed, its routes are + * removed from all tables. Possible spurious unfiltered withdraws are not + * problem here as they are ignored if there is no corresponding route at + * the other end of the pipe. We directly call rt_notify() hook instead of + * do_rt_notify() to avoid logging and stat counters. + */ + +#ifdef CONFIG_PIPE + if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto)) + p->rt_notify(p, ah->table, net, NULL, old0, NULL); +#endif + return; + } do_rt_notify(ah, net, new, old, tmpa, refeed); From d0e23d42de133de706151411d8d4091d07904d29 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 17 May 2015 00:54:33 +0200 Subject: [PATCH 006/165] Simplify flushing process Related to changes from previous patch. --- nest/rt-table.c | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index a8fe137c..24517e2c 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1384,9 +1384,8 @@ rt_init(void) static int -rt_prune_step(rtable *tab, int step, int *limit) +rt_prune_step(rtable *tab, int *limit) { - static struct tbf rl_flush = TBF_DEFAULT_LOG_LIMITS; struct fib_iterator *fit = &tab->prune_fit; DBG("Pruning route table %s\n", tab->name); @@ -1411,9 +1410,7 @@ rt_prune_step(rtable *tab, int step, int *limit) rescan: for (e=n->routes; e; e=e->next) - if (e->sender->proto->flushing || - (e->flags & REF_DISCARD) || - (step && e->attrs->src->proto->flushing)) + if (e->sender->proto->flushing || (e->flags & REF_DISCARD)) { if (*limit <= 0) { @@ -1421,10 +1418,6 @@ rt_prune_step(rtable *tab, int step, int *limit) return 0; } - if (step) - log_rl(&rl_flush, L_WARN "Route %I/%d from %s still in %s after flush", - n->n.prefix, n->n.pxlen, e->attrs->src->proto->name, tab->name); - rte_discard(tab, e); (*limit)--; @@ -1464,7 +1457,7 @@ static inline int rt_prune_table(rtable *tab) { int limit = 512; - return rt_prune_step(tab, 0, &limit); + return rt_prune_step(tab, &limit); } /** @@ -1473,37 +1466,17 @@ rt_prune_table(rtable *tab) * The prune loop scans routing tables and removes routes belonging to flushing * protocols, discarded routes and also stale network entries. Returns 1 when * all such routes are pruned. It is a part of the protocol flushing loop. - * - * The prune loop runs in two steps. In the first step it prunes just the routes - * with flushing senders (in explicitly marked tables) so the route removal is - * propagated as usual. In the second step, all remaining relevant routes are - * removed. Ideally, there shouldn't be any, but it happens when pipe filters - * are changed. */ int rt_prune_loop(void) { - static int step = 0; int limit = 512; rtable *t; - again: WALK_LIST(t, routing_tables) - if (! rt_prune_step(t, step, &limit)) + if (! rt_prune_step(t, &limit)) return 0; - if (step == 0) - { - /* Prepare for the second step */ - WALK_LIST(t, routing_tables) - t->prune_state = RPS_SCHEDULED; - - step = 1; - goto again; - } - - /* Done */ - step = 0; return 1; } From 9fe1d3ca8a6420b4bdaf15a54ab7b13be6cc07eb Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 22 May 2015 11:12:48 +0200 Subject: [PATCH 007/165] Fixes unnamed protocols from templates --- nest/config.Y | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nest/config.Y b/nest/config.Y index 939bed6a..37551802 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -183,16 +183,18 @@ proto_name: } | FROM SYM { struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter); + s->class = this_proto->class; + s->def = this_proto; this_proto->name = s->name; + if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("Template or protocol name expected"); proto_copy_config(this_proto, $2->def); } | SYM FROM SYM { - if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected"); - cf_define_symbol($1, this_proto->class, this_proto); this_proto->name = $1->name; + if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected"); proto_copy_config(this_proto, $3->def); } ; From 398f92253186d70eb3f2e910c7f8dd2636ee5fa1 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Mon, 1 Jun 2015 10:41:17 +0200 Subject: [PATCH 008/165] Typo fix by Hans van Kranenburg --- proto/ospf/iface.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 656184c6..9b0f7797 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -1344,9 +1344,9 @@ ospf_iface_info(struct ospf_iface *ifa) cli_msg(-1015, "\tRetransmit timer: %u", ifa->rxmtint); if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) { - cli_msg(-1015, "\tDesigned router (ID): %R", ifa->drid); - cli_msg(-1015, "\tDesigned router (IP): %I", ifa->drip); - cli_msg(-1015, "\tBackup designed router (ID): %R", ifa->bdrid); - cli_msg(-1015, "\tBackup designed router (IP): %I", ifa->bdrip); + cli_msg(-1015, "\tDesignated router (ID): %R", ifa->drid); + cli_msg(-1015, "\tDesignated router (IP): %I", ifa->drip); + cli_msg(-1015, "\tBackup designated router (ID): %R", ifa->bdrid); + cli_msg(-1015, "\tBackup designated router (IP): %I", ifa->bdrip); } } From e348ef01b433e06888310c1098a05291034a856c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Tue, 19 May 2015 08:14:04 +0200 Subject: [PATCH 009/165] unsgined char -> byte --- lib/printf.c | 2 +- nest/route.h | 2 +- proto/ospf/rt.c | 2 +- sysdep/bsd/sysio.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/printf.c b/lib/printf.c index 3eb988fa..a1c36129 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -215,7 +215,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) if (!(flags & LEFT)) while (--field_width > 0) *str++ = ' '; - *str++ = (unsigned char) va_arg(args, int); + *str++ = (byte) va_arg(args, int); while (--field_width > 0) *str++ = ' '; continue; diff --git a/nest/route.h b/nest/route.h index 87f10ae3..2e6bf741 100644 --- a/nest/route.h +++ b/nest/route.h @@ -320,7 +320,7 @@ struct mpnh { ip_addr gw; /* Next hop */ struct iface *iface; /* Outgoing interface */ struct mpnh *next; - unsigned char weight; + byte weight; }; struct rte_src { diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 74d10c7b..e74bcae6 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -43,7 +43,7 @@ unresolved_vlink(ort *ort) } static inline struct mpnh * -new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, unsigned char weight) +new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight) { struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh)); nh->gw = gw; diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index df5e0236..c82d7a1e 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -130,7 +130,7 @@ static inline void sk_process_cmsg4_ttl(sock *s, struct cmsghdr *cm) { if (cm->cmsg_type == IP_RECVTTL) - s->rcv_ttl = * (unsigned char *) CMSG_DATA(cm); + s->rcv_ttl = * (byte *) CMSG_DATA(cm); } static inline void From ae80a2de95d3d3c153ce20b90c9d8757d02cb33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Tue, 19 May 2015 08:53:34 +0200 Subject: [PATCH 010/165] unsigned [int] -> uint --- client/commands.c | 2 +- conf/conf.h | 2 +- lib/bitops.c | 2 +- lib/bitops.h | 2 +- lib/checksum.c | 10 +++++----- lib/checksum.h | 4 ++-- lib/ip.h | 4 ++-- lib/mempool.c | 12 ++++++------ lib/printf.c | 2 +- lib/slab.c | 12 ++++++------ lib/socket.h | 14 +++++++------- lib/xmalloc.c | 4 ++-- misc/ips.c | 4 ++-- nest/a-path.c | 2 +- nest/a-set.c | 4 ++-- nest/attrs.h | 6 +++--- nest/cli.c | 10 +++++----- nest/cli.h | 12 ++++++------ nest/neighbor.c | 4 ++-- nest/proto.c | 22 +++++++++++----------- nest/protocol.h | 18 +++++++++--------- nest/route.h | 22 +++++++++++----------- nest/rt-attr.c | 30 +++++++++++++++--------------- nest/rt-fib.c | 10 +++++----- nest/rt-table.c | 10 +++++----- proto/bgp/attrs.c | 14 +++++++------- proto/bgp/bgp.h | 8 ++++---- proto/bgp/packets.c | 8 ++++---- sysdep/bsd/krt-sock.c | 4 ++-- sysdep/linux/netlink.c | 6 +++--- sysdep/unix/main.c | 2 +- sysdep/unix/unix.h | 2 +- 32 files changed, 134 insertions(+), 134 deletions(-) diff --git a/client/commands.c b/client/commands.c index 08e7949a..50fcba40 100644 --- a/client/commands.c +++ b/client/commands.c @@ -38,7 +38,7 @@ static struct cmd_node cmd_root; void cmd_build_tree(void) { - unsigned int i; + uint i; cmd_root.plastson = &cmd_root.son; diff --git a/conf/conf.h b/conf/conf.h index 6ab53e25..515efbb3 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -100,7 +100,7 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size); /* Lexer */ -extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd); +extern int (*cf_read_hook)(byte *buf, uint max, int fd); struct symbol { struct symbol *next; diff --git a/lib/bitops.c b/lib/bitops.c index b63274b8..81586e87 100644 --- a/lib/bitops.c +++ b/lib/bitops.c @@ -17,7 +17,7 @@ * representation consists of @n ones followed by zeroes. */ u32 -u32_mkmask(unsigned n) +u32_mkmask(uint n) { return n ? ~((1 << (32 - n)) - 1) : 0; } diff --git a/lib/bitops.h b/lib/bitops.h index a12f6b60..c0ad1a70 100644 --- a/lib/bitops.h +++ b/lib/bitops.h @@ -18,7 +18,7 @@ * u32_masklen Inverse operation to u32_mkmask, -1 if not a bitmask. */ -u32 u32_mkmask(unsigned n); +u32 u32_mkmask(uint n); int u32_masklen(u32 x); u32 u32_log2(u32 v); diff --git a/lib/checksum.c b/lib/checksum.c index 18b1f92c..b61306b3 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -28,7 +28,7 @@ add32(u32 sum, u32 x) } static u16 -ipsum_calc_block(u32 *buf, unsigned len, u16 isum) +ipsum_calc_block(u32 *buf, uint len, u16 isum) { /* * A few simple facts about the IP checksum (see RFC 1071 for detailed @@ -57,7 +57,7 @@ ipsum_calc_block(u32 *buf, unsigned len, u16 isum) } static u16 -ipsum_calc(void *frag, unsigned len, va_list args) +ipsum_calc(void *frag, uint len, va_list args) { u16 sum = 0; @@ -67,7 +67,7 @@ ipsum_calc(void *frag, unsigned len, va_list args) frag = va_arg(args, void *); if (!frag) break; - len = va_arg(args, unsigned); + len = va_arg(args, uint); } return sum; } @@ -87,7 +87,7 @@ ipsum_calc(void *frag, unsigned len, va_list args) * Result: 1 if the checksum is correct, 0 else. */ int -ipsum_verify(void *frag, unsigned len, ...) +ipsum_verify(void *frag, uint len, ...) { va_list args; u16 sum; @@ -110,7 +110,7 @@ ipsum_verify(void *frag, unsigned len, ...) * up checksum calculation as much as possible. */ u16 -ipsum_calculate(void *frag, unsigned len, ...) +ipsum_calculate(void *frag, uint len, ...) { va_list args; u16 sum; diff --git a/lib/checksum.h b/lib/checksum.h index 81515543..16cbcce5 100644 --- a/lib/checksum.h +++ b/lib/checksum.h @@ -14,7 +14,7 @@ * fragments finished by NULL pointer. */ -int ipsum_verify(void *frag, unsigned len, ...); -u16 ipsum_calculate(void *frag, unsigned len, ...); +int ipsum_verify(void *frag, uint len, ...); +u16 ipsum_calculate(void *frag, uint len, ...); #endif diff --git a/lib/ip.h b/lib/ip.h index 45e073d9..90bb7f8a 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -471,11 +471,11 @@ int ip6_pton(char *a, ip6_addr *o); #define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p)))) #define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2))))) -char *ip_scope_text(unsigned); +char *ip_scope_text(uint); struct prefix { ip_addr addr; - unsigned int len; + uint len; }; diff --git a/lib/mempool.c b/lib/mempool.c index ec9854a9..a8281041 100644 --- a/lib/mempool.c +++ b/lib/mempool.c @@ -27,7 +27,7 @@ struct lp_chunk { struct lp_chunk *next; - unsigned int size; + uint size; uintptr_t data_align[0]; byte data[0]; }; @@ -37,7 +37,7 @@ struct linpool { byte *ptr, *end; struct lp_chunk *first, *current, **plast; /* Normal (reusable) chunks */ struct lp_chunk *first_large; /* Large chunks */ - unsigned chunk_size, threshold, total, total_large; + uint chunk_size, threshold, total, total_large; }; static void lp_free(resource *); @@ -64,7 +64,7 @@ static struct resclass lp_class = { * @blk. */ linpool -*lp_new(pool *p, unsigned blk) +*lp_new(pool *p, uint blk) { linpool *m = ralloc(p, &lp_class); m->plast = &m->first; @@ -88,7 +88,7 @@ linpool * size chunk, an "overflow" chunk is created for it instead. */ void * -lp_alloc(linpool *m, unsigned size) +lp_alloc(linpool *m, uint size) { byte *a = (byte *) BIRD_ALIGN((unsigned long) m->ptr, CPU_STRUCT_ALIGN); byte *e = a + size; @@ -146,7 +146,7 @@ lp_alloc(linpool *m, unsigned size) * how to allocate strings without any space overhead. */ void * -lp_allocu(linpool *m, unsigned size) +lp_allocu(linpool *m, uint size) { byte *a = m->ptr; byte *e = a + size; @@ -168,7 +168,7 @@ lp_allocu(linpool *m, unsigned size) * clears the allocated memory block. */ void * -lp_allocz(linpool *m, unsigned size) +lp_allocz(linpool *m, uint size) { void *z = lp_alloc(m, size); diff --git a/lib/printf.c b/lib/printf.c index a1c36129..e4cc3006 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -355,7 +355,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) } else if (flags & SIGN) num = va_arg(args, int); else - num = va_arg(args, unsigned int); + num = va_arg(args, uint); str = number(str, num, base, field_width, precision, flags, size); if (!str) return -1; diff --git a/lib/slab.c b/lib/slab.c index 31529c30..5c414f9e 100644 --- a/lib/slab.c +++ b/lib/slab.c @@ -51,7 +51,7 @@ static size_t slab_memsize(resource *r); struct slab { resource r; - unsigned size; + uint size; list objs; }; @@ -71,7 +71,7 @@ struct sl_obj { }; slab * -sl_new(pool *p, unsigned size) +sl_new(pool *p, uint size) { slab *s = ralloc(p, &sl_class); s->size = size; @@ -144,7 +144,7 @@ slab_memsize(resource *r) struct slab { resource r; - unsigned obj_size, head_size, objs_per_slab, num_empty_heads, data_size; + uint obj_size, head_size, objs_per_slab, num_empty_heads, data_size; list empty_heads, partial_heads, full_heads; }; @@ -185,10 +185,10 @@ struct sl_alignment { /* Magic structure for testing of alignment */ * objects of size @size can be allocated. */ slab * -sl_new(pool *p, unsigned size) +sl_new(pool *p, uint size) { slab *s = ralloc(p, &sl_class); - unsigned int align = sizeof(struct sl_alignment); + uint align = sizeof(struct sl_alignment); if (align < sizeof(int)) align = sizeof(int); s->data_size = size; @@ -214,7 +214,7 @@ sl_new_head(slab *s) struct sl_head *h = xmalloc(SLAB_SIZE); struct sl_obj *o = (struct sl_obj *)((byte *)h+s->head_size); struct sl_obj *no; - unsigned int n = s->objs_per_slab; + uint n = s->objs_per_slab; h->first_free = o; h->num_full = 0; diff --git a/lib/socket.h b/lib/socket.h index 683cdde3..fbea92aa 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -20,7 +20,7 @@ typedef struct birdsock { int type; /* Socket type */ void *data; /* User data */ ip_addr saddr, daddr; /* IPA_NONE = unspecified */ - unsigned sport, dport; /* 0 = unspecified (for IP: protocol type) */ + uint sport, dport; /* 0 = unspecified (for IP: protocol type) */ int tos; /* TOS / traffic class, -1 = default */ int priority; /* Local socket priority, -1 = default */ int ttl; /* Time To Live, -1 = default */ @@ -28,20 +28,20 @@ typedef struct birdsock { struct iface *iface; /* Interface; specify this for broad/multicast sockets */ byte *rbuf, *rpos; /* NULL=allocate automatically */ - unsigned rbsize; + uint rbsize; int (*rx_hook)(struct birdsock *, int size); /* NULL=receiving turned off, returns 1 to clear rx buffer */ byte *tbuf, *tpos; /* NULL=allocate automatically */ byte *ttx; /* Internal */ - unsigned tbsize; + uint tbsize; void (*tx_hook)(struct birdsock *); void (*err_hook)(struct birdsock *, int); /* errno or zero if EOF */ /* Information about received datagrams (UDP, RAW), valid in rx_hook */ ip_addr faddr, laddr; /* src (From) and dst (Local) address of the datagram */ - unsigned fport; /* src port of the datagram */ - unsigned lifindex; /* local interface that received the datagram */ + uint fport; /* src port of the datagram */ + uint lifindex; /* local interface that received the datagram */ /* laddr and lifindex are valid only if SKF_LADDR_RX flag is set to request it */ int af; /* Address family (AF_INET, AF_INET6 or 0 for non-IP) of fd */ @@ -59,8 +59,8 @@ sock *sock_new(pool *); /* Allocate new socket */ int sk_open(sock *); /* Open socket */ int sk_rx_ready(sock *s); -int sk_send(sock *, unsigned len); /* Send data, <0=err, >0=ok, 0=sleep */ -int sk_send_to(sock *, unsigned len, ip_addr to, unsigned port); /* sk_send to given destination */ +int sk_send(sock *, uint len); /* Send data, <0=err, >0=ok, 0=sleep */ +int sk_send_to(sock *, uint len, ip_addr to, uint port); /* sk_send to given destination */ void sk_reallocate(sock *); /* Free and allocate tbuf & rbuf */ void sk_set_rbsize(sock *s, uint val); /* Resize RX buffer */ void sk_set_tbsize(sock *s, uint val); /* Resize TX buffer, keeping content */ diff --git a/lib/xmalloc.c b/lib/xmalloc.c index da2f0941..10bf28cf 100644 --- a/lib/xmalloc.c +++ b/lib/xmalloc.c @@ -24,7 +24,7 @@ * Wherever possible, please use the memory resources instead. */ void * -xmalloc(unsigned size) +xmalloc(uint size) { void *p = malloc(size); if (p) @@ -44,7 +44,7 @@ xmalloc(unsigned size) * Wherever possible, please use the memory resources instead. */ void * -xrealloc(void *ptr, unsigned size) +xrealloc(void *ptr, uint size) { void *p = realloc(ptr, size); if (p) diff --git a/misc/ips.c b/misc/ips.c index 7ea6c71b..467cc25d 100644 --- a/misc/ips.c +++ b/misc/ips.c @@ -23,7 +23,7 @@ int h[65536]; * = ((1-1/k)^k)^a which we can approximate by e^-a. */ -unsigned int hf(unsigned int n) +uint hf(uint n) { #if 0 n = (n ^ (n >> 16)) & 0xffff; @@ -58,7 +58,7 @@ main(int argc, char **argv) while (max--) { - unsigned int i, e; + uint i, e; if (scanf("%x/%d", &i, &e) != 2) if (feof(stdin)) break; diff --git a/nest/a-path.c b/nest/a-path.c index dc36e653..c9c5aefb 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -124,7 +124,7 @@ as_path_convert_to_new(struct adata *path, byte *dst, int req_as) } void -as_path_format(struct adata *path, byte *buf, unsigned int size) +as_path_format(struct adata *path, byte *buf, uint size) { byte *p = path->data; byte *e = p + path->length; diff --git a/nest/a-set.c b/nest/a-set.c index 42ef9b06..a6116022 100644 --- a/nest/a-set.c +++ b/nest/a-set.c @@ -32,7 +32,7 @@ * the buffer to indicate truncation. */ int -int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int size) +int_set_format(struct adata *set, int way, int from, byte *buf, uint size) { u32 *z = (u32 *) set->data; byte *end = buf + size - 24; @@ -113,7 +113,7 @@ ec_format(byte *buf, u64 ec) } int -ec_set_format(struct adata *set, int from, byte *buf, unsigned int size) +ec_set_format(struct adata *set, int from, byte *buf, uint size) { u32 *z = int_set_get_data(set); byte *end = buf + size - 24; diff --git a/nest/attrs.h b/nest/attrs.h index b6e067cb..1d005a6a 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -30,7 +30,7 @@ struct f_tree; struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as); int as_path_convert_to_old(struct adata *path, byte *dst, int *new_used); int as_path_convert_to_new(struct adata *path, byte *dst, int req_as); -void as_path_format(struct adata *path, byte *buf, unsigned int size); +void as_path_format(struct adata *path, byte *buf, uint size); int as_path_getlen(struct adata *path); int as_path_getlen_int(struct adata *path, int bs); int as_path_get_first(struct adata *path, u32 *orig_as); @@ -95,9 +95,9 @@ static inline u64 ec_ip4(u64 kind, u64 key, u64 val) static inline u64 ec_generic(u64 key, u64 val) { return (key << 32) | val; } -int int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int size); +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, unsigned int size); +int ec_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); struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val); diff --git a/nest/cli.c b/nest/cli.c index 11f98794..83e79616 100644 --- a/nest/cli.c +++ b/nest/cli.c @@ -163,7 +163,7 @@ static void cli_copy_message(cli *c) { byte *p, *q; - unsigned int cnt = 2; + uint cnt = 2; if (c->ring_overflow) { @@ -230,12 +230,12 @@ cli_written(cli *c) static byte *cli_rh_pos; -static unsigned int cli_rh_len; +static uint cli_rh_len; static int cli_rh_trick_flag; struct cli *this_cli; static int -cli_cmd_read_hook(byte *buf, unsigned int max, UNUSED int fd) +cli_cmd_read_hook(byte *buf, uint max, UNUSED int fd) { if (!cli_rh_trick_flag) { @@ -330,7 +330,7 @@ static list cli_log_hooks; static int cli_log_inited; void -cli_set_log_echo(cli *c, unsigned int mask, unsigned int size) +cli_set_log_echo(cli *c, uint mask, uint size) { if (c->ring_buf) { @@ -351,7 +351,7 @@ cli_set_log_echo(cli *c, unsigned int mask, unsigned int size) } void -cli_echo(unsigned int class, byte *msg) +cli_echo(uint class, byte *msg) { unsigned len, free, i, l; cli *c; diff --git a/nest/cli.h b/nest/cli.h index 396656e8..92f3c3d7 100644 --- a/nest/cli.h +++ b/nest/cli.h @@ -40,10 +40,10 @@ typedef struct cli { struct linpool *parser_pool; /* Pool used during parsing */ byte *ring_buf; /* Ring buffer for asynchronous messages */ byte *ring_end, *ring_read, *ring_write; /* Pointers to the ring buffer */ - unsigned int ring_overflow; /* Counter of ring overflows */ - unsigned int log_mask; /* Mask of allowed message levels */ - unsigned int log_threshold; /* When free < log_threshold, store only important messages */ - unsigned int async_msg_size; /* Total size of async messages queued in tx_buf */ + uint ring_overflow; /* Counter of ring overflows */ + uint log_mask; /* Mask of allowed message levels */ + uint log_threshold; /* When free < log_threshold, store only important messages */ + uint async_msg_size; /* Total size of async messages queued in tx_buf */ } cli; extern pool *cli_pool; @@ -55,7 +55,7 @@ extern struct cli *this_cli; /* Used during parsing */ void cli_printf(cli *, int, char *, ...); #define cli_msg(x...) cli_printf(this_cli, x) -void cli_set_log_echo(cli *, unsigned int mask, unsigned int size); +void cli_set_log_echo(cli *, uint mask, uint size); /* Functions provided to sysdep layer */ @@ -64,7 +64,7 @@ void cli_init(void); void cli_free(cli *); void cli_kick(cli *); void cli_written(cli *); -void cli_echo(unsigned int class, byte *msg); +void cli_echo(uint class, byte *msg); static inline int cli_access_restricted(void) { diff --git a/nest/neighbor.c b/nest/neighbor.c index 48b6b6ac..1685d67e 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -49,7 +49,7 @@ static slab *neigh_slab; static list sticky_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE]; -static inline unsigned int +static inline uint neigh_hash(struct proto *p, ip_addr *a) { return (p->hash_key ^ ipa_hash(*a)) & (NEIGH_HASH_SIZE-1); @@ -126,7 +126,7 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags) { neighbor *n; int class, scope = -1; - unsigned int h = neigh_hash(p, a); + uint h = neigh_hash(p, a); struct iface *i; struct ifa *addr; diff --git a/nest/proto.c b/nest/proto.c index 44cfb637..6531083c 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -1488,7 +1488,7 @@ proto_show_basic_info(struct proto *p) } void -proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) +proto_cmd_show(struct proto *p, uint verbose, int cnt) { byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE]; @@ -1524,7 +1524,7 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) } void -proto_cmd_disable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) +proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED) { if (p->disabled) { @@ -1540,7 +1540,7 @@ proto_cmd_disable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) } void -proto_cmd_enable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) +proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED) { if (!p->disabled) { @@ -1555,7 +1555,7 @@ proto_cmd_enable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) } void -proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) +proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED) { if (p->disabled) { @@ -1573,7 +1573,7 @@ proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) } void -proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED) +proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED) { if (p->disabled) { @@ -1615,19 +1615,19 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED) } void -proto_cmd_debug(struct proto *p, unsigned int mask, int cnt UNUSED) +proto_cmd_debug(struct proto *p, uint mask, int cnt UNUSED) { p->debug = mask; } void -proto_cmd_mrtdump(struct proto *p, unsigned int mask, int cnt UNUSED) +proto_cmd_mrtdump(struct proto *p, uint mask, int cnt UNUSED) { p->mrtdump = mask; } static void -proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg) +proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg) { if (s->class != SYM_PROTO) { @@ -1640,7 +1640,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, unsigned i } static void -proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg) +proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg) { int cnt = 0; @@ -1660,8 +1660,8 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, unsigned int, int) } void -proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int), - int restricted, unsigned int arg) +proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), + int restricted, uint arg) { if (restricted && cli_access_restricted()) return; diff --git a/nest/protocol.h b/nest/protocol.h index 8660cc2c..a51e9afd 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -261,15 +261,15 @@ void proto_graceful_restart_unlock(struct proto *p); void proto_show_limit(struct proto_limit *l, const char *dsc); void proto_show_basic_info(struct proto *p); -void proto_cmd_show(struct proto *, unsigned int, int); -void proto_cmd_disable(struct proto *, unsigned int, int); -void proto_cmd_enable(struct proto *, unsigned int, int); -void proto_cmd_restart(struct proto *, unsigned int, int); -void proto_cmd_reload(struct proto *, unsigned int, int); -void proto_cmd_debug(struct proto *, unsigned int, int); -void proto_cmd_mrtdump(struct proto *, unsigned int, int); - -void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int), int restricted, unsigned int arg); +void proto_cmd_show(struct proto *, uint, int); +void proto_cmd_disable(struct proto *, uint, int); +void proto_cmd_enable(struct proto *, uint, int); +void proto_cmd_restart(struct proto *, uint, int); +void proto_cmd_reload(struct proto *, uint, int); +void proto_cmd_debug(struct proto *, uint, int); +void proto_cmd_mrtdump(struct proto *, uint, int); + +void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), int restricted, uint arg); struct proto *proto_get_named(struct symbol *, struct protocol *); #define CMD_RELOAD 0 diff --git a/nest/route.h b/nest/route.h index 2e6bf741..8f3c7c9a 100644 --- a/nest/route.h +++ b/nest/route.h @@ -47,7 +47,7 @@ struct fib_iterator { /* See lib/slists.h for an explanation */ byte efef; /* 0xff to distinguish between iterator and node */ byte pad[3]; struct fib_node *node; /* Or NULL if freshly merged */ - unsigned int hash; + uint hash; }; typedef void (*fib_init_func)(struct fib_node *); @@ -56,11 +56,11 @@ struct fib { pool *fib_pool; /* Pool holding all our data */ slab *fib_slab; /* Slab holding all fib nodes */ struct fib_node **hash_table; /* Node hash table */ - unsigned int hash_size; /* Number of hash table entries (a power of two) */ - unsigned int hash_order; /* Binary logarithm of hash_size */ - unsigned int hash_shift; /* 16 - hash_log */ - unsigned int entries; /* Number of entries */ - unsigned int entries_min, entries_max;/* Entry count limits (else start rehashing) */ + uint hash_size; /* Number of hash table entries (a power of two) */ + uint hash_order; /* Binary logarithm of hash_size */ + uint hash_shift; /* 16 - hash_log */ + uint entries; /* Number of entries */ + uint entries_min, entries_max; /* Entry count limits (else start rehashing) */ fib_init_func init; /* Constructor */ }; @@ -78,7 +78,7 @@ void fit_put(struct fib_iterator *, struct fib_node *); #define FIB_WALK(fib, z) do { \ struct fib_node *z, **ff = (fib)->hash_table; \ - unsigned int count = (fib)->hash_size; \ + uint count = (fib)->hash_size; \ while (count--) \ for(z = *ff++; z; z=z->next) @@ -88,8 +88,8 @@ void fit_put(struct fib_iterator *, struct fib_node *); #define FIB_ITERATE_START(fib, it, z) do { \ struct fib_node *z = fit_get(fib, it); \ - unsigned int count = (fib)->hash_size; \ - unsigned int hpos = (it)->hash; \ + uint count = (fib)->hash_size; \ + uint hpos = (it)->hash; \ for(;;) { \ if (!z) \ { \ @@ -435,7 +435,7 @@ typedef struct eattr { #define EAF_TEMP 0x80 /* A temporary attribute (the one stored in the tmp attr list) */ struct adata { - unsigned int length; /* Length of data */ + uint length; /* Length of data */ byte data[0]; }; @@ -475,7 +475,7 @@ void ea_sort(ea_list *); /* Sort entries in all sub-lists */ unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */ void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffer */ int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */ -unsigned int ea_hash(ea_list *e); /* Calculate 16-bit hash value */ +uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */ ea_list *ea_append(ea_list *to, ea_list *what); void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max); diff --git a/nest/rt-attr.c b/nest/rt-attr.c index c5537208..85a192c8 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -98,7 +98,7 @@ rte_src_init(void) HASH_INIT(src_hash, rta_pool, RSH_INIT_ORDER); } -static inline int u32_cto(unsigned int x) { return ffs(~x) - 1; } +static inline int u32_cto(uint x) { return ffs(~x) - 1; } static inline u32 rte_src_alloc_id(void) @@ -195,10 +195,10 @@ rt_prune_sources(void) * Multipath Next Hop */ -static inline unsigned int +static inline uint mpnh_hash(struct mpnh *x) { - unsigned int h = 0; + uint h = 0; for (; x; x = x->next) h ^= ipa_hash(x->gw); @@ -666,7 +666,7 @@ ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, } static inline void -opaque_format(struct adata *ad, byte *buf, unsigned int size) +opaque_format(struct adata *ad, byte *buf, uint size) { byte *bound = buf + size - 10; int i; @@ -838,7 +838,7 @@ ea_dump(ea_list *e) * ea_hash() takes an extended attribute list and calculated a hopefully * uniformly distributed hash value from its contents. */ -inline unsigned int +inline uint ea_hash(ea_list *e) { u32 h = 0; @@ -900,10 +900,10 @@ ea_append(ea_list *to, ea_list *what) * rta's */ -static unsigned int rta_cache_count; -static unsigned int rta_cache_size = 32; -static unsigned int rta_cache_limit; -static unsigned int rta_cache_mask; +static uint rta_cache_count; +static uint rta_cache_size = 32; +static uint rta_cache_limit; +static uint rta_cache_mask; static rta **rta_hash_table; static void @@ -917,7 +917,7 @@ rta_alloc_hash(void) rta_cache_mask = rta_cache_size - 1; } -static inline unsigned int +static inline uint rta_hash(rta *a) { return (((uint) (uintptr_t) a->src) ^ ipa_hash(a->gw) ^ @@ -957,7 +957,7 @@ rta_copy(rta *o) static inline void rta_insert(rta *r) { - unsigned int h = r->hash_key & rta_cache_mask; + uint h = r->hash_key & rta_cache_mask; r->next = rta_hash_table[h]; if (r->next) r->next->pprev = &r->next; @@ -968,8 +968,8 @@ rta_insert(rta *r) static void rta_rehash(void) { - unsigned int ohs = rta_cache_size; - unsigned int h; + uint ohs = rta_cache_size; + uint h; rta *r, *n; rta **oht = rta_hash_table; @@ -1002,7 +1002,7 @@ rta * rta_lookup(rta *o) { rta *r; - unsigned int h; + uint h; ASSERT(!(o->aflags & RTAF_CACHED)); if (o->eattrs) @@ -1093,7 +1093,7 @@ void rta_dump_all(void) { rta *a; - unsigned int h; + uint h; debug("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit); for(h=0; hhash_table + (h >> f->hash_shift); struct fib_node *g, *e = *ee; u32 uid = h << 16; @@ -321,7 +321,7 @@ void fib_delete(struct fib *f, void *E) { struct fib_node *e = E; - unsigned int h = fib_hash(f, &e->prefix); + uint h = fib_hash(f, &e->prefix); struct fib_node **ee = f->hash_table + h; struct fib_iterator *it; @@ -442,7 +442,7 @@ fit_put(struct fib_iterator *i, struct fib_node *n) void fib_check(struct fib *f) { - unsigned int i, ec, lo, nulls; + uint i, ec, lo, nulls; ec = 0; for(i=0; ihash_size; i++) @@ -452,7 +452,7 @@ fib_check(struct fib *f) for(n=f->hash_table[i]; n; n=n->next) { struct fib_iterator *j, *j0; - unsigned int h0 = ipa_hash(n->prefix); + uint h0 = ipa_hash(n->prefix); if (h0 < lo) bug("fib_check: discord in hash chains"); lo = h0; @@ -489,7 +489,7 @@ struct fib f; void dump(char *m) { - unsigned int i; + uint i; debug("%s ... order=%d, size=%d, entries=%d\n", m, f.hash_order, f.hash_size, f.hash_size); for(i=0; idebug & flag) rte_trace(p, e, '>', msg); } static inline void -rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) +rte_trace_out(uint flag, struct proto *p, rte *e, char *msg) { if (p->debug & flag) rte_trace(p, e, '<', msg); @@ -1880,7 +1880,7 @@ hc_hash(ip_addr a, rtable *dep) static inline void hc_insert(struct hostcache *hc, struct hostentry *he) { - unsigned int k = he->hash_key >> hc->hash_shift; + uint k = he->hash_key >> hc->hash_shift; he->next = hc->hash_table[k]; hc->hash_table[k] = he; } @@ -1889,7 +1889,7 @@ static inline void hc_remove(struct hostcache *hc, struct hostentry *he) { struct hostentry **hep; - unsigned int k = he->hash_key >> hc->hash_shift; + uint k = he->hash_key >> hc->hash_shift; for (hep = &hc->hash_table[k]; *hep != he; hep = &(*hep)->next); *hep = he->next; @@ -2154,7 +2154,7 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) if (!tab->hostcache) rt_init_hostcache(tab); - unsigned int k = hc_hash(a, dep); + uint k = hc_hash(a, dep); struct hostcache *hc = tab->hostcache; for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next) if (ipa_equal(he->addr, a) && (he->tab == dep)) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 72b45d47..d56c017d 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -114,7 +114,7 @@ path_segment_contains(byte *p, int bs, u32 asn) /* Validates path attribute, removes AS_CONFED_* segments, and also returns path length */ static int -validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, unsigned int *ilength) +validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ilength) { int res = 0; u8 *a, *dst; @@ -381,7 +381,7 @@ bgp_attach_attr_wa(ea_list **to, struct linpool *pool, unsigned attr, unsigned l } static int -bgp_encode_attr_hdr(byte *dst, unsigned int flags, unsigned code, int len) +bgp_encode_attr_hdr(byte *dst, uint flags, unsigned code, int len) { int wlen; @@ -473,10 +473,10 @@ bgp_get_attr_len(eattr *a) * * Result: Length of the attribute block generated or -1 if not enough space. */ -unsigned int +uint bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains) { - unsigned int i, code, type, flags; + uint i, code, type, flags; byte *start = w; int len, rv; @@ -1593,11 +1593,11 @@ bgp_remove_as4_attrs(struct bgp_proto *p, rta *a) * by a &rta. */ struct rta * -bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool, int mandatory) +bgp_decode_attrs(struct bgp_conn *conn, byte *attr, uint len, struct linpool *pool, int mandatory) { struct bgp_proto *bgp = conn->bgp; rta *a = lp_alloc(pool, sizeof(struct rta)); - unsigned int flags, code, l, i, type; + uint flags, code, l, i, type; int errcode; byte *z, *attr_start; byte seen[256/8]; @@ -1791,7 +1791,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin int bgp_get_attr(eattr *a, byte *buf, int buflen) { - unsigned int i = EA_ID(a->id); + uint i = EA_ID(a->id); struct attr_desc *d; int len; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index f4f21226..446fc857 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -90,7 +90,7 @@ struct bgp_config { struct bgp_conn { struct bgp_proto *bgp; struct birdsock *sk; - unsigned int state; /* State of connection state machine */ + uint state; /* State of connection state machine */ struct timer *connect_retry_timer; struct timer *hold_timer; struct timer *keepalive_timer; @@ -142,7 +142,7 @@ struct bgp_proto { struct timer *startup_timer; /* Timer used to delay protocol startup due to previous errors (startup_delay) */ struct timer *gr_timer; /* Timer waiting for reestablishment after graceful restart */ struct bgp_bucket **bucket_hash; /* Hash table of attribute buckets */ - unsigned int hash_size, hash_count, hash_limit; + uint hash_size, hash_count, hash_limit; HASH(struct bgp_prefix) prefix_hash; /* Prefixes to be sent */ slab *prefix_slab; /* Slab holding prefix nodes */ list bucket_queue; /* Queue of buckets to send */ @@ -235,7 +235,7 @@ static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = ad void bgp_attach_attr(struct ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val); byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned attr, unsigned len); -struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool, int mandatory); +struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, uint len, struct linpool *pool, int mandatory); int bgp_get_attr(struct eattr *e, byte *buf, int buflen); int bgp_rte_better(struct rte *, struct rte *); int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best); @@ -245,7 +245,7 @@ void bgp_init_bucket_table(struct bgp_proto *); void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck); void bgp_init_prefix_table(struct bgp_proto *p, u32 order); void bgp_free_prefix(struct bgp_proto *p, struct bgp_prefix *bp); -unsigned int bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains); +uint bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains); void bgp_get_route_info(struct rte *, byte *buf, struct ea_list *attrs); inline static void bgp_attach_attr_ip(struct ea_list **to, struct linpool *pool, unsigned attr, ip_addr a) diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 4bd68f52..378f5ab1 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -289,8 +289,8 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) } } -static unsigned int -bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsigned int remains) +static uint +bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, uint remains) { byte *start = w; ip_addr a; @@ -648,7 +648,7 @@ bgp_create_end_refresh(struct bgp_conn *conn, byte *buf) static void -bgp_create_header(byte *buf, unsigned int len, unsigned int type) +bgp_create_header(byte *buf, uint len, uint type) { memset(buf, 0xff, 16); /* Marker */ put_u16(buf+16, len); @@ -669,7 +669,7 @@ static int bgp_fire_tx(struct bgp_conn *conn) { struct bgp_proto *p = conn->bgp; - unsigned int s = conn->packets_to_send; + uint s = conn->packets_to_send; sock *sk = conn->sk; byte *buf, *pkt, *end; int type; diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index aaf3b23c..064bae18 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -181,7 +181,7 @@ struct ks_msg #define GETADDR(p, F) \ bzero(p, sizeof(*p));\ if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\ - unsigned int l = ROUNDUP(((struct sockaddr *)body)->sa_len);\ + uint l = ROUNDUP(((struct sockaddr *)body)->sa_len);\ memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\ body += l;} @@ -537,7 +537,7 @@ krt_read_ifinfo(struct ks_msg *msg, int scan) struct if_msghdr *ifm = (struct if_msghdr *)&msg->rtm; void *body = (void *)(ifm + 1); struct sockaddr_dl *dl = NULL; - unsigned int i; + uint i; struct iface *iface = NULL, f = {}; int fl = ifm->ifm_flags; int nlen = 0; diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 71f58554..c4d52255 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -50,7 +50,7 @@ struct nl_sock u32 seq; byte *rx_buffer; /* Receive buffer */ struct nlmsghdr *last_hdr; /* Recently received packet */ - unsigned int last_size; + uint last_size; }; #define NL_RX_SIZE 8192 @@ -443,7 +443,7 @@ nl_parse_link(struct nlmsghdr *h, int scan) struct iface *ifi; char *name; u32 mtu; - unsigned int fl; + uint fl; if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), a, sizeof(a))) return; @@ -1088,7 +1088,7 @@ nl_async_hook(sock *sk, int size UNUSED) struct msghdr m = { (struct sockaddr *) &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; struct nlmsghdr *h; int x; - unsigned int len; + uint len; x = recvmsg(sk->fd, &m, 0); if (x < 0) diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 05f7560d..e31471da 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -155,7 +155,7 @@ read_iproute_table(char *file, char *prefix, int max) static char *config_name = PATH_CONFIG_FILE; static int -cf_read(byte *dest, unsigned int len, int fd) +cf_read(byte *dest, uint len, int fd) { int l = read(fd, dest, len); if (l < 0) diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 593978cc..4e0ff841 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -119,7 +119,7 @@ void log_switch(int debug, list *l, char *); /* Use l=NULL for initial switch */ struct log_config { node n; - unsigned int mask; /* Classes to log */ + uint mask; /* Classes to log */ void *fh; /* FILE to log to, NULL=syslog */ int terminal_flag; }; From ca34698ca62d979c281ed517f040619e31c3ada3 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 31 May 2015 11:29:53 +0200 Subject: [PATCH 011/165] Fixes bug in pipe feeding when filtered routes are kept in table --- nest/rt-table.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index d5af41c7..27c6cb76 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1816,15 +1816,20 @@ rt_feed_baby(struct proto *p) { if (p->export_state != ES_FEEDING) return 1; /* In the meantime, the protocol fell down. */ + do_feed_baby(p, p->accept_ra_types, h, n, e); max_feed--; } if (p->accept_ra_types == RA_ANY) - for(e = n->routes; rte_is_valid(e); e = e->next) + for(e = n->routes; e; e = e->next) { if (p->export_state != ES_FEEDING) return 1; /* In the meantime, the protocol fell down. */ + + if (!rte_is_valid(e)) + continue; + do_feed_baby(p, RA_ANY, h, n, e); max_feed--; } From d217ba5111a80a629e408961b902d7759c4b46f5 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 31 May 2015 23:25:33 +0200 Subject: [PATCH 012/165] Moving of mulipath merging code from OSPF to nest --- nest/route.h | 1 + nest/rt-attr.c | 94 ++++++++++++++++++++++++++++++++++++-- proto/ospf/rt.c | 104 ++++++------------------------------------ proto/ospf/rt.h | 3 +- proto/ospf/topology.h | 2 +- 5 files changed, 110 insertions(+), 94 deletions(-) diff --git a/nest/route.h b/nest/route.h index 8f3c7c9a..e22f950b 100644 --- a/nest/route.h +++ b/nest/route.h @@ -482,6 +482,7 @@ void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **na int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */ static inline int mpnh_same(struct mpnh *x, struct mpnh *y) { return (x == y) || mpnh__same(x, y); } +struct mpnh *mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp); void rta_init(void); rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 85a192c8..32090b52 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -167,7 +167,7 @@ rt_get_source(struct proto *p, u32 id) src->private_id = id; src->global_id = rte_src_alloc_id(); src->uc = 0; - + HASH_INSERT2(src_hash, RSH, rta_pool, src); return src; @@ -215,6 +215,94 @@ mpnh__same(struct mpnh *x, struct mpnh *y) return x == y; } +static int +mpnh_compare_node(struct mpnh *x, struct mpnh *y) +{ + int r; + + if (!x) + return 1; + + if (!y) + return -1; + + r = ((int) y->weight) - ((int) x->weight); + if (r) + return r; + + r = ipa_compare(x->gw, y->gw); + if (r) + return r; + + return ((int) x->iface->index) - ((int) y->iface->index); +} + +static inline struct mpnh * +mpnh_copy_node(const struct mpnh *src, linpool *lp) +{ + struct mpnh *n = lp_alloc(lp, sizeof(struct mpnh)); + n->gw = src->gw; + n->iface = src->iface; + n->next = NULL; + n->weight = src->weight; + return n; +} + +/** + * mpnh_merge - merge nexthop lists + * @x: list 1 + * @y: list 2 + * @rx: reusability of list @x + * @ry: reusability of list @y + * @max: max number of nexthops + * @lp: linpool for allocating nexthops + * + * The mpnh_merge() function takes two nexthop lists @x and @y and merges them, + * eliminating possible duplicates. The input lists must be sorted and the + * result is sorted too. The number of nexthops in result is limited by @max. + * New nodes are allocated from linpool @lp. + * + * The arguments @rx and @ry specify whether corresponding input lists may be + * consumed by the function (i.e. their nodes reused in the resulting list), in + * that case the caller should not access these lists after that. To eliminate + * issues with deallocation of these lists, the caller should use some form of + * bulk deallocation (e.g. stack or linpool) to free these nodes when the + * resulting list is no longer needed. When reusability is not set, the + * corresponding lists are not modified nor linked from the resulting list. + */ +struct mpnh * +mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp) +{ + struct mpnh *root = NULL; + struct mpnh **n = &root; + + while ((x || y) && max--) + { + int cmp = mpnh_compare_node(x, y); + if (cmp < 0) + { + *n = rx ? x : mpnh_copy_node(x, lp); + x = x->next; + } + else if (cmp > 0) + { + *n = ry ? y : mpnh_copy_node(y, lp); + y = y->next; + } + else + { + *n = rx ? x : (ry ? y : mpnh_copy_node(x, lp)); + x = x->next; + y = y->next; + } + n = &((*n)->next); + } + *n = NULL; + + return root; +} + + static struct mpnh * mpnh_copy(struct mpnh *o) { @@ -635,7 +723,7 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED) *buf += bsprintf(*buf, "igp_metric"); return GA_NAME; } - + return GA_UNKNOWN; } @@ -741,7 +829,7 @@ ea_show(struct cli *c, eattr *e) } else if (EA_PROTO(e->id)) pos += bsprintf(pos, "%02x.", EA_PROTO(e->id)); - else + else status = get_generic_attr(e, &pos, end - pos); if (status < GA_NAME) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index e74bcae6..cdf8012a 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -53,88 +53,6 @@ new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight) return nh; } -static inline struct mpnh * -copy_nexthop(struct ospf_proto *p, const struct mpnh *src) -{ - struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh)); - nh->gw = src->gw; - nh->iface = src->iface; - nh->next = NULL; - nh->weight = src->weight; - return nh; -} - -/* Compare nexthops during merge. - We need to maintain nhs sorted to eliminate duplicities */ -static int -cmp_nhs(struct mpnh *s1, struct mpnh *s2) -{ - int r; - - if (!s1) - return 1; - - if (!s2) - return -1; - - r = ((int) s2->weight) - ((int) s1->weight); - if (r) - return r; - - r = ipa_compare(s1->gw, s2->gw); - if (r) - return r; - - return ((int) s1->iface->index) - ((int) s2->iface->index); -} - -static struct mpnh * -merge_nexthops(struct ospf_proto *p, struct mpnh *s1, struct mpnh *s2, int r1, int r2) -{ - struct mpnh *root = NULL; - struct mpnh **n = &root; - int count = p->ecmp; - - ASSERT(p->ecmp); - - /* - * r1, r2 signalize whether we can reuse nexthops from s1, s2. - * New nexthops (s2, new) can be reused if they are not inherited - * from the parent (i.e. it is allocated in calc_next_hop()). - * Current nexthops (s1, en->nhs) can be reused if they weren't - * inherited in previous steps (that is stored in nhs_reuse, - * i.e. created by merging or allocalted in calc_next_hop()). - * - * Generally, a node first inherits shared nexthops from its - * parent and later possibly gets reusable copy during merging. - */ - - while ((s1 || s2) && count--) - { - int cmp = cmp_nhs(s1, s2); - if (cmp < 0) - { - *n = r1 ? s1 : copy_nexthop(p, s1); - s1 = s1->next; - } - else if (cmp > 0) - { - *n = r2 ? s2 : copy_nexthop(p, s2); - s2 = s2->next; - } - else - { - *n = r1 ? s1 : (r2 ? s2 : copy_nexthop(p, s1)); - s1 = s1->next; - s2 = s2->next; - } - n = &((*n)->next); - } - *n = NULL; - - return root; -} - /* Returns true if there are device nexthops in n */ static inline int has_device_nexthops(const struct mpnh *n) @@ -178,7 +96,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw) } } - return merge_nexthops(p, root1, root2, 1, 1); + return mpnh_merge(root1, root2, 1, 1, p->ecmp, p->nhpool); } @@ -374,7 +292,8 @@ ort_merge(struct ospf_proto *p, ort *o, const orta *new) if (old->nhs != new->nhs) { - old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse); + old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse, + p->ecmp, p->nhpool); old->nhs_reuse = 1; } @@ -389,7 +308,8 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new) if (old->nhs != new->nhs) { - old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse); + old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse, + p->ecmp, p->nhpool); old->nhs_reuse = 1; } @@ -1885,8 +1805,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, return; } - /* We know that en->color == CANDIDATE and en->nhs is defined. */ - + /* If en->dist > 0, we know that en->color == CANDIDATE and en->nhs is defined. */ if ((dist == en->dist) && !nh_is_vlink(en->nhs)) { /* @@ -1900,7 +1819,14 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, * allocated in calc_next_hop()). * * Generally, a node first inherits shared nexthops from its parent and - * later possibly gets reusable copy during merging. + * later possibly gets reusable (private) copy during merging. This is more + * or less same for both top_hash_entry nodes and orta nodes. + * + * Note that when a child inherits a private nexthop from its parent, it + * should make the nexthop shared for both parent and child, while we only + * update nhs_reuse for the child node. This makes nhs_reuse field for the + * parent technically incorrect, but it is not a problem as parent's nhs + * will not be modified (and nhs_reuse examined) afterwards. */ /* Keep old ones */ @@ -1909,7 +1835,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, /* Merge old and new */ int new_reuse = (par->nhs != nhs); - en->nhs = merge_nexthops(p, en->nhs, nhs, en->nhs_reuse, new_reuse); + en->nhs = mpnh_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool); en->nhs_reuse = 1; return; } diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index 61936f3c..30332f3b 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -18,7 +18,8 @@ typedef struct orta { u8 type; /* RTS_OSPF_* */ - u8 nhs_reuse; /* Whether nhs nodes can be reused during merging */ + u8 nhs_reuse; /* Whether nhs nodes can be reused during merging. + See a note in rt.c:add_cand() */ u32 options; /* * For ORT_ROUTER routes, options field are router-LSA style diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index e2d6c773..5652ced0 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -39,7 +39,7 @@ struct top_hash_entry #define INSPF 2 u8 mode; /* LSA generated during RT calculation (LSA_RTCALC or LSA_STALE)*/ u8 nhs_reuse; /* Whether nhs nodes can be reused during merging. - See a note in rt.c:merge_nexthops() */ + See a note in rt.c:add_cand() */ }; From 78a2cc289fbe11d5b5783220e2cc61d119a78ec3 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 3 Jun 2015 11:58:46 +0200 Subject: [PATCH 013/165] KRT: Fixes some minor bugs in kernel protocol --- sysdep/linux/netlink.c | 5 ++ sysdep/unix/krt.c | 107 +++++++++++++++++++++++++---------------- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index c4d52255..9f206e1c 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -703,6 +703,11 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); + /* For route delete, we do not specify route attributes */ + if (!new) + return nl_exchange(&r.h); + + if (ea = ea_find(eattrs, EA_KRT_METRIC)) nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index cfb623ce..d8d28c7c 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -592,6 +592,44 @@ krt_flush_routes(struct krt_proto *p) FIB_WALK_END; } +static struct rte * +krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa) +{ + struct filter *filter = p->p.main_ahook->out_filter; + rte *rt; + + rt = net->routes; + *rt_free = NULL; + + if (!rte_is_valid(rt)) + return NULL; + + if (filter == FILTER_REJECT) + return NULL; + + struct proto *src = rt->attrs->src->proto; + *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, krt_filter_lp) : NULL; + + /* We could run krt_import_control() here, but it is already handled by KRF_INSTALLED */ + + if (filter == FILTER_ACCEPT) + goto accept; + + if (f_run(filter, &rt, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) > F_ACCEPT) + goto reject; + + +accept: + if (rt != net->routes) + *rt_free = rt; + return rt; + +reject: + if (rt != net->routes) + rte_free(rt); + return NULL; +} + static int krt_same_dest(rte *k, rte *e) { @@ -620,7 +658,6 @@ krt_same_dest(rte *k, rte *e) void krt_got_route(struct krt_proto *p, rte *e) { - rte *old; net *net = e->net; int verdict; @@ -663,15 +700,26 @@ krt_got_route(struct krt_proto *p, rte *e) goto sentenced; } - old = net->routes; - if ((net->n.flags & KRF_INSTALLED) && rte_is_valid(old)) + if (net->n.flags & KRF_INSTALLED) { - /* There may be changes in route attributes, we ignore that. - Also, this does not work well if gw is changed in export filter */ - if ((net->n.flags & KRF_SYNC_ERROR) || ! krt_same_dest(e, old)) + rte *new, *rt_free; + ea_list *tmpa; + + new = krt_export_net(p, net, &rt_free, &tmpa); + + /* TODO: There also may be changes in route eattrs, we ignore that for now. */ + + if (!new) + verdict = KRF_DELETE; + else if ((net->n.flags & KRF_SYNC_ERROR) || !krt_same_dest(e, new)) verdict = KRF_UPDATE; else verdict = KRF_SEEN; + + if (rt_free) + rte_free(rt_free); + + lp_flush(krt_filter_lp); } else verdict = KRF_DELETE; @@ -692,25 +740,6 @@ krt_got_route(struct krt_proto *p, rte *e) rte_free(e); } -static inline int -krt_export_rte(struct krt_proto *p, rte **new, ea_list **tmpa) -{ - struct filter *filter = p->p.main_ahook->out_filter; - - if (! *new) - return 0; - - if (filter == FILTER_REJECT) - return 0; - - if (filter == FILTER_ACCEPT) - return 1; - - struct proto *src = (*new)->attrs->src->proto; - *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(*new, krt_filter_lp) : NULL; - return f_run(filter, new, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) <= F_ACCEPT; -} - static void krt_prune(struct krt_proto *p) { @@ -721,7 +750,7 @@ krt_prune(struct krt_proto *p) { net *n = (net *) f; int verdict = f->flags & KRF_VERDICT_MASK; - rte *new, *new0, *old; + rte *new, *old, *rt_free = NULL; ea_list *tmpa = NULL; if (verdict == KRF_UPDATE || verdict == KRF_DELETE) @@ -733,23 +762,18 @@ krt_prune(struct krt_proto *p) else old = NULL; - new = new0 = n->routes; if (verdict == KRF_CREATE || verdict == KRF_UPDATE) { /* We have to run export filter to get proper 'new' route */ - if (! krt_export_rte(p, &new, &tmpa)) - { - /* Route rejected, should not happen (KRF_INSTALLED) but to be sure .. */ - verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE; - } + new = krt_export_net(p, n, &rt_free, &tmpa); + + if (!new) + verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE; else - { - ea_list **x = &tmpa; - while (*x) - x = &((*x)->next); - *x = new ? new->attrs->eattrs : NULL; - } + tmpa = ea_append(tmpa, new->attrs->eattrs); } + else + new = NULL; switch (verdict) { @@ -778,8 +802,8 @@ krt_prune(struct krt_proto *p) if (old) rte_free(old); - if (new != new0) - rte_free(new); + if (rt_free) + rte_free(rt_free); lp_flush(krt_filter_lp); f->flags &= ~KRF_VERDICT_MASK; } @@ -974,7 +998,8 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool * * We will remove KRT_INSTALLED flag, which stops such withdraw to be * processed in krt_rt_notify() and krt_replace_rte(). */ - e->net->n.flags &= ~KRF_INSTALLED; + if (e == e->net->routes) + e->net->n.flags &= ~KRF_INSTALLED; #endif return -1; } From db027a41d47b8fc52b65067ccabe2024554e53ca Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 4 Jun 2015 11:35:26 +0200 Subject: [PATCH 014/165] Fixes subtle bug in temporary attribute handling In some cases, export filter accessed attributes of a different route. --- nest/rt-table.c | 49 ++++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index 27c6cb76..22e1c489 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -208,12 +208,10 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, rt = rt0; *rt_free = NULL; - /* If called does not care for eattrs, we prepare one internally */ if (!tmpa) - { - tmpb = make_tmp_attrs(rt, rte_update_pool); - tmpa = &tmpb; - } + tmpa = &tmpb; + + *tmpa = make_tmp_attrs(rt, rte_update_pool); v = p->import_control ? p->import_control(p, &rt, tmpa, rte_update_pool) : 0; if (v < 0) @@ -347,7 +345,7 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm } static void -rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, ea_list *tmpa, int refeed) +rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int refeed) { struct proto *p = ah->proto; struct proto_stats *stats = ah->stats; @@ -356,6 +354,7 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, ea_lis rte *old = old0; rte *new_free = NULL; rte *old_free = NULL; + ea_list *tmpa = NULL; if (new) stats->exp_updates_received++; @@ -419,17 +418,17 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, ea_lis } static void -rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old, - ea_list *tmpa, int feed) +rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed) { // struct proto *p = ah->proto; struct proto_stats *stats = ah->stats; + rte *r; rte *new_best = NULL; rte *old_best = NULL; rte *new_free = NULL; rte *old_free = NULL; - rte *r; + ea_list *tmpa = NULL; /* Used to track whether we met old_changed position. If before_old is NULL old_changed was the first and we met it implicitly before current best route. */ @@ -543,7 +542,6 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol * @net: network in question * @new: the new route to be announced * @old: the previous route for the same network - * @tmpa: a list of temporary attributes belonging to the new route * * This function gets a routing table update and announces it * to all protocols that acccepts given type of route announcement @@ -566,7 +564,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol * the protocol gets called. */ static void -rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa) +rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old) { if (!rte_is_valid(old)) old = before_old = NULL; @@ -594,9 +592,9 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo ASSERT(a->proto->export_state != ES_DOWN); if (a->proto->accept_ra_types == type) if (type == RA_ACCEPTED) - rt_notify_accepted(a, net, new, old, before_old, tmpa, 0); + rt_notify_accepted(a, net, new, old, before_old, 0); else - rt_notify_basic(a, net, new, old, tmpa, 0); + rt_notify_basic(a, net, new, old, 0); } } @@ -659,7 +657,7 @@ rte_same(rte *x, rte *y) static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); } static void -rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct rte_src *src) +rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) { struct proto *p = ah->proto; struct rtable *table = ah->table; @@ -900,11 +898,11 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str } /* Propagate the route change */ - rte_announce(table, RA_ANY, net, new, old, NULL, tmpa); + rte_announce(table, RA_ANY, net, new, old, NULL); if (net->routes != old_best) - rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL, tmpa); + rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL); if (table->config->sorted) - rte_announce(table, RA_ACCEPTED, net, new, old, before_old, tmpa); + rte_announce(table, RA_ACCEPTED, net, new, old, before_old); if (!net->routes && (table->gc_counter++ >= table->config->gc_max_ops) && @@ -1069,7 +1067,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) recalc: rte_hide_dummy_routes(net, &dummy); - rte_recalculate(ah, net, new, tmpa, src); + rte_recalculate(ah, net, new, src); rte_unhide_dummy_routes(net, &dummy); rte_update_unlock(); return; @@ -1077,7 +1075,6 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) drop: rte_free(new); new = NULL; - tmpa = NULL; goto recalc; } @@ -1086,11 +1083,8 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) static inline void rte_announce_i(rtable *tab, unsigned type, net *n, rte *new, rte *old) { - ea_list *tmpa; - rte_update_lock(); - tmpa = make_tmp_attrs(new, rte_update_pool); - rte_announce(tab, type, n, new, old, NULL, tmpa); + rte_announce(tab, type, n, new, old, NULL); rte_update_unlock(); } @@ -1098,7 +1092,7 @@ void rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */ { rte_update_lock(); - rte_recalculate(old->sender, old->net, NULL, NULL, old->attrs->src); + rte_recalculate(old->sender, old->net, NULL, old->attrs->src); rte_update_unlock(); } @@ -1758,14 +1752,11 @@ rt_commit(struct config *new, struct config *old) static inline void do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) { - ea_list *tmpa; - rte_update_lock(); - tmpa = make_tmp_attrs(e, rte_update_pool); if (type == RA_ACCEPTED) - rt_notify_accepted(h, n, e, NULL, NULL, tmpa, p->refeeding ? 2 : 1); + rt_notify_accepted(h, n, e, NULL, NULL, p->refeeding ? 2 : 1); else - rt_notify_basic(h, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding); + rt_notify_basic(h, n, e, p->refeeding ? e : NULL, p->refeeding); rte_update_unlock(); } From 8d9eef17713a9b38cd42bd59c4ce76c3ef6c2fc2 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 8 Jun 2015 02:20:43 +0200 Subject: [PATCH 015/165] BGP multipath support Kernel option 'merge paths' allows to merge routes exported to kernel protocol (currently BGP and static routes) to multipath routes. --- doc/bird.sgml | 12 +++ filter/filter.c | 36 +++---- lib/birdlib.h | 1 + nest/protocol.h | 3 + nest/route.h | 11 ++ nest/rt-attr.c | 10 ++ nest/rt-table.c | 228 +++++++++++++++++++++++++++++++++++++++--- proto/bgp/attrs.c | 76 ++++++++++++++ proto/bgp/bgp.c | 1 + proto/bgp/bgp.h | 1 + proto/static/static.c | 7 ++ sysdep/unix/krt.Y | 4 +- sysdep/unix/krt.c | 17 +++- sysdep/unix/krt.h | 3 + 14 files changed, 371 insertions(+), 39 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 1c2dda4b..752465b9 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2227,6 +2227,18 @@ limitations can be overcome using another routing table and the pipe protocol. a graceful restart recovery is active, the Kernel protocol will defer synchronization of routing tables until the end of the recovery. Note that import of kernel routes to BIRD is not affected. + + merge paths switch [limit number] + Usually, only best routes are exported to the kernel protocol. With path + merging enabled, both best routes and equivalent non-best routes are + merged during export to generate one ECMP (equal-cost multipath) route + for each network. This is useful e.g. for BGP multipath. Note that best + routes are still pivotal for route export (responsible for most + properties of resulting ECMP routes), while exported non-best routes are + responsible just for additional multipath next hops. This option also + allows to specify a limit on maximal number of nexthops in one route. By + default, multipath merging is disabled. If enabled, default value of the + limit is 16. Attributes diff --git a/filter/filter.c b/filter/filter.c index 3b14fc0c..3f8968aa 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -471,26 +471,22 @@ static inline void f_rte_cow(void) static void f_rta_cow(void) { - if ((*f_rte)->attrs->aflags & RTAF_CACHED) { - - /* Prepare to modify rte */ - f_rte_cow(); - - /* Store old rta to free it later */ - f_old_rta = (*f_rte)->attrs; - - /* - * Alloc new rta, do shallow copy and update rte. Fields eattrs - * and nexthops of rta are shared with f_old_rta (they will be - * copied when the cached rta will be obtained at the end of - * f_run()), also the lock of hostentry is inherited (we suppose - * hostentry is not changed by filters). - */ - rta *ra = lp_alloc(f_pool, sizeof(rta)); - memcpy(ra, f_old_rta, sizeof(rta)); - ra->aflags = 0; - (*f_rte)->attrs = ra; - } + if (!rta_is_cached((*f_rte)->attrs)) + return; + + /* Prepare to modify rte */ + f_rte_cow(); + + /* Store old rta to free it later, it stores reference from rte_cow() */ + f_old_rta = (*f_rte)->attrs; + + /* + * Get shallow copy of rta. Fields eattrs and nexthops of rta are shared + * with f_old_rta (they will be copied when the cached rta will be obtained + * at the end of f_run()), also the lock of hostentry is inherited (we + * suppose hostentry is not changed by filters). + */ + (*f_rte)->attrs = rta_do_cow((*f_rte)->attrs, f_pool); } static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; diff --git a/lib/birdlib.h b/lib/birdlib.h index 94054769..ad41dca3 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -31,6 +31,7 @@ #endif #define ABS(a) ((a)>=0 ? (a) : -(a)) +#define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a)) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) diff --git a/nest/protocol.h b/nest/protocol.h index a51e9afd..8c49154f 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -158,6 +158,7 @@ struct proto { byte gr_wait; /* Route export to protocol is postponed until graceful restart */ byte down_sched; /* Shutdown is scheduled for later (PDS_*) */ byte down_code; /* Reason for shutdown (PDC_* codes) */ + byte merge_limit; /* Maximal number of nexthops for RA_MERGED */ u32 hash_key; /* Random key used for hashing of neighbors */ bird_clock_t last_state_change; /* Time of last state transition */ char *last_state_name_announced; /* Last state name we've announced to the user */ @@ -200,6 +201,7 @@ struct proto { * rte_recalculate Called at the beginning of the best route selection * rte_better Compare two rte's and decide which one is better (1=first, 0=second). * rte_same Compare two rte's and decide whether they are identical (1=yes, 0=no). + * rte_mergable Compare two rte's and decide whether they could be merged (1=yes, 0=no). * rte_insert Called whenever a rte is inserted to a routing table. * rte_remove Called whenever a rte is removed from the routing table. */ @@ -207,6 +209,7 @@ struct proto { int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *); int (*rte_better)(struct rte *, struct rte *); int (*rte_same)(struct rte *, struct rte *); + int (*rte_mergable)(struct rte *, struct rte *); void (*rte_insert)(struct network *, struct rte *); void (*rte_remove)(struct network *, struct rte *); diff --git a/nest/route.h b/nest/route.h index e22f950b..6067526d 100644 --- a/nest/route.h +++ b/nest/route.h @@ -240,6 +240,7 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); #define RA_OPTIMAL 1 /* Announcement of optimal route change */ #define RA_ACCEPTED 2 /* Announcement of first accepted route */ #define RA_ANY 3 /* Announcement of any route change */ +#define RA_MERGED 4 /* Announcement of optimal route merged with next ones */ /* Return value of import_control() callback */ #define RIC_ACCEPT 1 /* Accepted by protocol */ @@ -263,12 +264,14 @@ void rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *s static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_ahook, net, new, p->main_source); } void rte_discard(rtable *tab, rte *old); int rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter); +rte *rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, struct ea_list **tmpa, int silent); void rt_refresh_begin(rtable *t, struct announce_hook *ah); void rt_refresh_end(rtable *t, struct announce_hook *ah); void rte_dump(rte *); void rte_free(rte *); rte *rte_do_cow(rte *); static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; } +rte *rte_cow_rta(rte *r, linpool *lp); void rt_dump(rtable *); void rt_dump_all(void); int rt_feed_baby(struct proto *p); @@ -388,6 +391,12 @@ typedef struct rta { #define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other protocol-specific metric is availabe */ + +/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */ +static inline int rte_is_reachable(rte *r) +{ uint d = r->attrs->dest; return (d == RTD_ROUTER) || (d == RTD_DEVICE) || (d == RTD_MULTIPATH); } + + /* * Extended Route Attributes */ @@ -490,6 +499,8 @@ static inline int rta_is_cached(rta *r) { return r->aflags & RTAF_CACHED; } static inline rta *rta_clone(rta *r) { r->uc++; return r; } void rta__free(rta *r); static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); } +rta *rta_do_cow(rta *o, linpool *lp); +static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; } void rta_dump(rta *); void rta_dump_all(void); void rta_show(struct cli *, rta *, ea_list *); diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 32090b52..7fa05d6d 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -1138,6 +1138,16 @@ rta__free(rta *a) sl_free(rta_slab, a); } +rta * +rta_do_cow(rta *o, linpool *lp) +{ + rta *r = lp_alloc(lp, sizeof(rta)); + memcpy(r, o, sizeof(rta)); + r->aflags = 0; + r->uc = 0; + return r; +} + /** * rta_dump - dump route attributes * @a: attribute structure to dump diff --git a/nest/rt-table.c b/nest/rt-table.c index 22e1c489..9e2c4e0d 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -144,6 +144,38 @@ rte_do_cow(rte *r) return e; } +/** + * rte_cow_rta - get a private writable copy of &rte with writable &rta + * @r: a route entry to be copied + * @lp: a linpool from which to allocate &rta + * + * rte_cow_rta() takes a &rte and prepares it and associated &rta for + * modification. There are three possibilities: First, both &rte and &rta are + * private copies, in that case they are returned unchanged. Second, &rte is + * private copy, but &rta is cached, in that case &rta is duplicated using + * rta_do_cow(). Third, both &rte is shared and &rta is cached, in that case + * both structures are duplicated by rte_do_cow() and rta_do_cow(). + * + * Note that in the second case, cached &rta loses one reference, while private + * copy created by rta_do_cow() is a shallow copy sharing indirect data (eattrs, + * nexthops, ...) with it. To work properly, original shared &rta should have + * another reference during the life of created private copy. + * + * Result: a pointer to the new writable &rte with writable &rta. + */ +rte * +rte_cow_rta(rte *r, linpool *lp) +{ + if (!rta_is_cached(r->attrs)) + return r; + + rte *e = rte_cow(r); + rta *a = rta_do_cow(r->attrs, lp); + rta_free(e->attrs); + e->attrs = a; + return e; +} + static int /* Actually better or at least as good as */ rte_better(rte *new, rte *old) { @@ -172,6 +204,26 @@ rte_better(rte *new, rte *old) return 0; } +static int +rte_mergable(rte *pri, rte *sec) +{ + int (*mergable)(rte *, rte *); + + if (!rte_is_valid(pri) || !rte_is_valid(sec)) + return 0; + + if (pri->pref != sec->pref) + return 0; + + if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto) + return 0; + + if (mergable = pri->attrs->src->proto->rte_mergable) + return mergable(pri, sec); + + return 0; +} + static void rte_trace(struct proto *p, rte *e, int dir, char *msg) { @@ -535,6 +587,129 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol rte_free(old_free); } + +static struct mpnh * +mpnh_merge_rta(struct mpnh *nhs, rta *a, int max) +{ + struct mpnh nh = { .gw = a->gw, .iface = a->iface }; + struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh; + return mpnh_merge(nhs, nh2, 1, 0, max, rte_update_pool); +} + +rte * +rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tmpa, int silent) +{ + // struct proto *p = ah->proto; + struct mpnh *nhs = NULL; + rte *best0, *best, *rt0, *rt, *tmp; + + best0 = net->routes; + *rt_free = NULL; + + if (!rte_is_valid(best0)) + return NULL; + + best = export_filter(ah, best0, rt_free, tmpa, silent); + + if (!best || !rte_is_reachable(best)) + return best; + + for (rt0 = best0->next; rt0; rt0 = rt0->next) + { + if (!rte_mergable(best0, rt0)) + continue; + + rt = export_filter(ah, rt0, &tmp, NULL, 1); + + if (!rt) + continue; + + if (rte_is_reachable(rt)) + nhs = mpnh_merge_rta(nhs, rt->attrs, ah->proto->merge_limit); + + if (tmp) + rte_free(tmp); + } + + if (nhs) + { + nhs = mpnh_merge_rta(nhs, best->attrs, ah->proto->merge_limit); + + if (nhs->next) + { + best = rte_cow_rta(best, rte_update_pool); + best->attrs->dest = RTD_MULTIPATH; + best->attrs->nexthops = nhs; + } + } + + if (best != best0) + *rt_free = best; + + return best; +} + + +static void +rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, + rte *new_best, rte*old_best, int refeed) +{ + // struct proto *p = ah->proto; + + rte *new_best_free = NULL; + rte *old_best_free = NULL; + rte *new_changed_free = NULL; + rte *old_changed_free = NULL; + ea_list *tmpa = NULL; + + /* We assume that all rte arguments are either NULL or rte_is_valid() */ + + /* This check should be done by the caller */ + if (!new_best && !old_best) + return; + + /* Check whether the change is relevant to the merged route */ + if ((new_best == old_best) && !refeed) + { + new_changed = rte_mergable(new_best, new_changed) ? + export_filter(ah, new_changed, &new_changed_free, NULL, 1) : NULL; + + old_changed = rte_mergable(old_best, old_changed) ? + export_filter(ah, old_changed, &old_changed_free, NULL, 1) : NULL; + + if (!new_changed && !old_changed) + return; + } + + if (new_best) + ah->stats->exp_updates_received++; + else + ah->stats->exp_withdraws_received++; + + /* Prepare new merged route */ + if (new_best) + new_best = rt_export_merged(ah, net, &new_best_free, &tmpa, 0); + + /* Prepare old merged route (without proper merged next hops) */ + /* There are some issues with running filter on old route - see rt_notify_basic() */ + if (old_best && !refeed) + old_best = export_filter(ah, old_best, &old_best_free, NULL, 1); + + if (new_best || old_best) + do_rt_notify(ah, net, new_best, old_best, tmpa, refeed); + + /* Discard temporary rte's */ + if (new_best_free) + rte_free(new_best_free); + if (old_best_free) + rte_free(old_best_free); + if (new_changed_free) + rte_free(new_changed_free); + if (old_changed_free) + rte_free(old_changed_free); +} + + /** * rte_announce - announce a routing table change * @tab: table the route has been added to @@ -564,13 +739,20 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol * the protocol gets called. */ static void -rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old) +rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, + rte *new_best, rte *old_best, rte *before_old) { + if (!rte_is_valid(new)) + new = NULL; + if (!rte_is_valid(old)) old = before_old = NULL; - if (!rte_is_valid(new)) - new = NULL; + if (!rte_is_valid(new_best)) + new_best = NULL; + + if (!rte_is_valid(old_best)) + old_best = NULL; if (!old && !new) return; @@ -593,6 +775,8 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo if (a->proto->accept_ra_types == type) if (type == RA_ACCEPTED) rt_notify_accepted(a, net, new, old, before_old, 0); + else if (type == RA_MERGED) + rt_notify_merged(a, net, new, old, new_best, old_best, 0); else rt_notify_basic(a, net, new, old, 0); } @@ -898,11 +1082,12 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr } /* Propagate the route change */ - rte_announce(table, RA_ANY, net, new, old, NULL); + rte_announce(table, RA_ANY, net, new, old, NULL, NULL, NULL); if (net->routes != old_best) - rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL); + rte_announce(table, RA_OPTIMAL, net, net->routes, old_best, NULL, NULL, NULL); if (table->config->sorted) - rte_announce(table, RA_ACCEPTED, net, new, old, before_old); + rte_announce(table, RA_ACCEPTED, net, new, old, NULL, NULL, before_old); + rte_announce(table, RA_MERGED, net, new, old, net->routes, old_best, NULL); if (!net->routes && (table->gc_counter++ >= table->config->gc_max_ops) && @@ -1081,10 +1266,11 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) /* Independent call to rte_announce(), used from next hop recalculation, outside of rte_update(). new must be non-NULL */ static inline void -rte_announce_i(rtable *tab, unsigned type, net *n, rte *new, rte *old) +rte_announce_i(rtable *tab, unsigned type, net *net, rte *new, rte *old, + rte *new_best, rte *old_best) { rte_update_lock(); - rte_announce(tab, type, n, new, old, NULL); + rte_announce(tab, type, net, new, old, new_best, old_best, NULL); rte_update_unlock(); } @@ -1548,7 +1734,7 @@ rt_next_hop_update_net(rtable *tab, net *n) new = rt_next_hop_update_rte(tab, e); *k = new; - rte_announce_i(tab, RA_ANY, n, new, e); + rte_announce_i(tab, RA_ANY, n, new, e, NULL, NULL); rte_trace_in(D_ROUTES, new->sender->proto, new, "updated"); /* Call a pre-comparison hook */ @@ -1588,10 +1774,13 @@ rt_next_hop_update_net(rtable *tab, net *n) /* Announce the new best route */ if (new != old_best) { - rte_announce_i(tab, RA_OPTIMAL, n, new, old_best); + rte_announce_i(tab, RA_OPTIMAL, n, new, old_best, NULL, NULL); rte_trace_in(D_ROUTES, new->sender->proto, new, "updated [best]"); } + /* FIXME: Better announcement of merged routes */ + rte_announce_i(tab, RA_MERGED, n, new, old_best, new, old_best); + if (free_old_best) rte_free_quick(old_best); @@ -1755,6 +1944,8 @@ do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) rte_update_lock(); if (type == RA_ACCEPTED) rt_notify_accepted(h, n, e, NULL, NULL, p->refeeding ? 2 : 1); + else if (type == RA_MERGED) + rt_notify_merged(h, n, NULL, NULL, e, p->refeeding ? e : NULL, p->refeeding); else rt_notify_basic(h, n, e, p->refeeding ? e : NULL, p->refeeding); rte_update_unlock(); @@ -1802,7 +1993,8 @@ rt_feed_baby(struct proto *p) /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */ if ((p->accept_ra_types == RA_OPTIMAL) || - (p->accept_ra_types == RA_ACCEPTED)) + (p->accept_ra_types == RA_ACCEPTED) || + (p->accept_ra_types == RA_MERGED)) if (rte_is_valid(e)) { if (p->export_state != ES_FEEDING) @@ -2267,12 +2459,22 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) rte_update_lock(); /* We use the update buffer for filtering */ tmpa = make_tmp_attrs(e, rte_update_pool); - if (d->export_mode) + /* Special case for merged export */ + if ((d->export_mode == RSEM_EXPORT) && (d->export_protocol->accept_ra_types == RA_MERGED)) + { + rte *rt_free; + e = rt_export_merged(a, n, &rt_free, &tmpa, 1); + pass = 1; + + if (!e) + { e = ee; goto skip; } + } + else if (d->export_mode) { struct proto *ep = d->export_protocol; int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, rte_update_pool) : 0; - if (ep->accept_ra_types == RA_OPTIMAL) + if (ep->accept_ra_types == RA_OPTIMAL || ep->accept_ra_types == RA_MERGED) pass = 1; if (ic < 0) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index d56c017d..d85afa8f 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1312,6 +1312,82 @@ bgp_rte_better(rte *new, rte *old) } +int +bgp_rte_mergable(rte *pri, rte *sec) +{ + struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto; + struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto; + eattr *x, *y; + u32 p, s; + + /* Skip suppressed routes (see bgp_rte_recalculate()) */ + if (pri->u.bgp.suppressed != sec->u.bgp.suppressed) + return 0; + + /* RFC 4271 9.1.2.1. Route resolvability test */ + if (!rte_resolvable(sec)) + return 0; + + /* Start with local preferences */ + x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); + y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF)); + p = x ? x->u.data : pri_bgp->cf->default_local_pref; + s = y ? y->u.data : sec_bgp->cf->default_local_pref; + if (p != s) + return 0; + + /* RFC 4271 9.1.2.2. a) Use AS path lengths */ + if (pri_bgp->cf->compare_path_lengths || sec_bgp->cf->compare_path_lengths) + { + x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); + y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH)); + p = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN; + s = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN; + + if (p != s) + return 0; + +// if (DELTA(p, s) > pri_bgp->cf->relax_multipath) +// return 0; + } + + /* RFC 4271 9.1.2.2. b) Use origins */ + x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)); + y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN)); + p = x ? x->u.data : ORIGIN_INCOMPLETE; + s = y ? y->u.data : ORIGIN_INCOMPLETE; + if (p != s) + return 0; + + /* RFC 4271 9.1.2.2. c) Compare MED's */ + if (pri_bgp->cf->med_metric || sec_bgp->cf->med_metric || + (bgp_get_neighbor(pri) == bgp_get_neighbor(sec))) + { + x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); + y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); + p = x ? x->u.data : pri_bgp->cf->default_med; + s = y ? y->u.data : sec_bgp->cf->default_med; + if (p != s) + return 0; + } + + /* RFC 4271 9.1.2.2. d) Prefer external peers */ + if (pri_bgp->is_internal != sec_bgp->is_internal) + return 0; + + /* RFC 4271 9.1.2.2. e) Compare IGP metrics */ + p = pri_bgp->cf->igp_metric ? pri->attrs->igp_metric : 0; + s = sec_bgp->cf->igp_metric ? sec->attrs->igp_metric : 0; + if (p != s) + return 0; + + /* Remaining criteria are ignored */ + + return 1; +} + + + static inline int same_group(rte *r, u32 lpref, u32 lasn) { diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index e48b643b..9e28b278 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1243,6 +1243,7 @@ bgp_init(struct proto_config *C) P->feed_begin = bgp_feed_begin; P->feed_end = bgp_feed_end; P->rte_better = bgp_rte_better; + P->rte_mergable = bgp_rte_mergable; P->rte_recalculate = c->deterministic_med ? bgp_rte_recalculate : NULL; p->cf = c; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 446fc857..b6e80fe5 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -238,6 +238,7 @@ byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned att struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, uint len, struct linpool *pool, int mandatory); int bgp_get_attr(struct eattr *e, byte *buf, int buflen); int bgp_rte_better(struct rte *, struct rte *); +int bgp_rte_mergable(rte *pri, rte *sec); int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best); void bgp_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs); int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *); diff --git a/proto/static/static.c b/proto/static/static.c index 4b72fa9d..e7e7ab15 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -352,6 +352,12 @@ static_if_notify(struct proto *p, unsigned flags, struct iface *i) } } +int +static_rte_mergable(rte *pri, rte *sec) +{ + return 1; +} + void static_init_config(struct static_config *c) { @@ -366,6 +372,7 @@ static_init(struct proto_config *c) p->neigh_notify = static_neigh_notify; p->if_notify = static_if_notify; + p->rte_mergable = static_rte_mergable; return p; } diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y index 630cda38..e036081d 100644 --- a/sysdep/unix/krt.Y +++ b/sysdep/unix/krt.Y @@ -17,7 +17,7 @@ CF_DEFINES CF_DECLS -CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC) +CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS) CF_GRAMMAR @@ -47,6 +47,8 @@ kern_item: } | DEVICE ROUTES bool { THIS_KRT->devroutes = $3; } | GRACEFUL RESTART bool { THIS_KRT->graceful_restart = $3; } + | MERGE PATHS bool { THIS_KRT->merge_paths = $3 ? KRT_DEFAULT_ECMP_LIMIT : 0; } + | MERGE PATHS bool LIMIT expr { THIS_KRT->merge_paths = $3 ? $5 : 0; if (($5 <= 0) || ($5 > 255)) cf_error("Merge paths limit must be in range 1-255"); } ; /* Kernel interface protocol */ diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index d8d28c7c..2eab5cb2 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -595,9 +595,13 @@ krt_flush_routes(struct krt_proto *p) static struct rte * krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa) { - struct filter *filter = p->p.main_ahook->out_filter; + struct announce_hook *ah = p->p.main_ahook; + struct filter *filter = ah->out_filter; rte *rt; + if (p->p.accept_ra_types == RA_MERGED) + return rt_export_merged(ah, net, rt_free, tmpa, 1); + rt = net->routes; *rt_free = NULL; @@ -1091,11 +1095,13 @@ krt_rte_same(rte *a, rte *b) struct krt_config *krt_cf; static struct proto * -krt_init(struct proto_config *c) +krt_init(struct proto_config *C) { - struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); + struct krt_proto *p = proto_new(C, sizeof(struct krt_proto)); + struct krt_config *c = (struct krt_config *) C; - p->p.accept_ra_types = RA_OPTIMAL; + p->p.accept_ra_types = c->merge_paths ? RA_MERGED : RA_OPTIMAL; + p->p.merge_limit = c->merge_paths; p->p.import_control = krt_import_control; p->p.rt_notify = krt_rt_notify; p->p.if_notify = krt_if_notify; @@ -1161,7 +1167,8 @@ krt_reconfigure(struct proto *p, struct proto_config *new) return 0; /* persist, graceful restart need not be the same */ - return o->scan_time == n->scan_time && o->learn == n->learn && o->devroutes == n->devroutes; + return o->scan_time == n->scan_time && o->learn == n->learn && + o->devroutes == n->devroutes && o->merge_paths == n->merge_paths; } static void diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index 1940cbcd..9d5d4e8c 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -26,6 +26,8 @@ struct kif_proto; #define KRF_DELETE 3 /* Should be deleted */ #define KRF_IGNORE 4 /* To be ignored */ +#define KRT_DEFAULT_ECMP_LIMIT 16 + #define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0) #define EA_KRT_METRIC EA_CODE(EAP_KRT, 1) @@ -47,6 +49,7 @@ struct krt_config { int learn; /* Learn routes from other sources */ int devroutes; /* Allow export of device routes */ int graceful_restart; /* Regard graceful restart recovery */ + int merge_paths; /* Exported routes are merged for ECMP */ }; struct krt_proto { From ab4da3423d89fb6c60a4137f19c189a8716ecab6 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 18 Jul 2015 13:05:05 +0200 Subject: [PATCH 016/165] Direct: Fixes behavior for the same routes on different interfaces Thanks to Andrew (seti.kr.ua) for the bug report. --- nest/rt-dev.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nest/rt-dev.c b/nest/rt-dev.c index 87ffc5ec..f6bc1432 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -51,7 +51,10 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) DBG("dev_if_notify: device shutdown: prefix not found\n"); return; } - rte_update(p, n, NULL); + + /* Use iface ID as local source ID */ + struct rte_src *src = rt_get_source(p, ad->iface->index); + rte_update2(p->main_ahook, n, NULL, src); } else if (c & IF_CHANGE_UP) { @@ -61,8 +64,11 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip); + /* Use iface ID as local source ID */ + struct rte_src *src = rt_get_source(p, ad->iface->index); + rta a0 = { - .src = p->main_source, + .src = src, .source = RTS_DEVICE, .scope = SCOPE_UNIVERSE, .cast = RTC_UNICAST, @@ -75,7 +81,7 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) e = rte_get_temp(a); e->net = n; e->pflags = 0; - rte_update(p, n, e); + rte_update2(p->main_ahook, n, e, src); } } From 06e0d1b692d8a190c3f1d073c5c557d8efe78b17 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 18 Jul 2015 13:38:21 +0200 Subject: [PATCH 017/165] BGP: Extended messages support Implements draft-ietf-idr-bgp-extended-messages-10, for now undocumented and with temporary private capability number. --- doc/bird.sgml | 2 +- proto/bgp/bgp.c | 22 ++++++++++++++++------ proto/bgp/bgp.h | 13 +++++++++++-- proto/bgp/config.Y | 3 ++- proto/bgp/packets.c | 29 ++++++++++++++++++++++++----- 5 files changed, 54 insertions(+), 15 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 752465b9..2ae9f649 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1571,7 +1571,7 @@ RFC 4271 It also supports the community attributes (RFC 1997), capability negotiation -(RFC 3392), +(RFC 5492), MD5 password authentication (RFC 2385), extended communities diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 9e28b278..f549b0ed 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -569,6 +569,7 @@ bgp_send_open(struct bgp_conn *conn) conn->peer_gr_time = 0; conn->peer_gr_flags = 0; conn->peer_gr_aflags = 0; + conn->peer_ext_messages_support = 0; DBG("BGP: Sending open\n"); conn->sk->rx_hook = bgp_rx; @@ -733,8 +734,8 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c s->dport = p->cf->remote_port; s->iface = p->neigh ? p->neigh->iface : NULL; s->ttl = p->cf->ttl_security ? 255 : hops; - s->rbsize = BGP_RX_BUFFER_SIZE; - s->tbsize = BGP_TX_BUFFER_SIZE; + s->rbsize = p->cf->enable_extended_messages ? BGP_RX_BUFFER_EXT_SIZE : BGP_RX_BUFFER_SIZE; + s->tbsize = p->cf->enable_extended_messages ? BGP_TX_BUFFER_EXT_SIZE : BGP_TX_BUFFER_SIZE; s->tos = IP_PREC_INTERNET_CONTROL; s->password = p->cf->password; s->tx_hook = bgp_connected; @@ -843,6 +844,13 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) if (sk_set_min_ttl(sk, 256 - hops) < 0) goto err; + if (p->cf->enable_extended_messages) + { + sk->rbsize = BGP_RX_BUFFER_EXT_SIZE; + sk->tbsize = BGP_TX_BUFFER_EXT_SIZE; + sk_reallocate(sk); + } + bgp_setup_conn(p, &p->incoming_conn); bgp_setup_sk(&p->incoming_conn, sk); bgp_send_open(&p->incoming_conn); @@ -1518,21 +1526,23 @@ bgp_show_proto_info(struct proto *P) else if (P->proto_state == PS_UP) { cli_msg(-1006, " Neighbor ID: %R", p->remote_id); - cli_msg(-1006, " Neighbor caps: %s%s%s%s%s%s", + cli_msg(-1006, " Neighbor caps: %s%s%s%s%s%s%s", c->peer_refresh_support ? " refresh" : "", c->peer_enhanced_refresh_support ? " enhanced-refresh" : "", c->peer_gr_able ? " restart-able" : (c->peer_gr_aware ? " restart-aware" : ""), c->peer_as4_support ? " AS4" : "", (c->peer_add_path & ADD_PATH_RX) ? " add-path-rx" : "", - (c->peer_add_path & ADD_PATH_TX) ? " add-path-tx" : ""); - cli_msg(-1006, " Session: %s%s%s%s%s%s%s", + (c->peer_add_path & ADD_PATH_TX) ? " add-path-tx" : "", + c->peer_ext_messages_support ? " ext-messages" : ""); + cli_msg(-1006, " Session: %s%s%s%s%s%s%s%s", p->is_internal ? "internal" : "external", p->cf->multihop ? " multihop" : "", p->rr_client ? " route-reflector" : "", p->rs_client ? " route-server" : "", p->as4_session ? " AS4" : "", p->add_path_rx ? " add-path-rx" : "", - p->add_path_tx ? " add-path-tx" : ""); + p->add_path_tx ? " add-path-tx" : "", + p->ext_messages ? " ext-messages" : ""); cli_msg(-1006, " Source address: %I", p->source_addr); if (P->cf->in_limit) cli_msg(-1006, " Route limit: %d/%d", diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index b6e80fe5..274794f1 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -40,6 +40,7 @@ struct bgp_config { int capabilities; /* Enable capability handshake [RFC3392] */ int enable_refresh; /* Enable local support for route refresh [RFC2918] */ int enable_as4; /* Enable local support for 4B AS numbers [RFC4893] */ + int enable_extended_messages; /* Enable local support for extended messages [draft] */ u32 rr_cluster_id; /* Route reflector cluster ID, if different from local ID */ int rr_client; /* Whether neighbor is RR client of me */ int rs_client; /* Whether neighbor is RS client of me */ @@ -109,6 +110,7 @@ struct bgp_conn { u16 peer_gr_time; u8 peer_gr_flags; u8 peer_gr_aflags; + u8 peer_ext_messages_support; /* Peer supports extended message length [draft] */ unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */ }; @@ -121,6 +123,7 @@ struct bgp_proto { u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */ u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */ u8 add_path_tx; /* Session expects transmit of ADD-PATH extended NLRI */ + u8 ext_messages; /* Session allows to use extended messages (both sides support it) */ u32 local_id; /* BGP identifier of this router */ u32 remote_id; /* BGP identifier of the neighbor */ u32 rr_cluster_id; /* Route reflector cluster ID */ @@ -180,9 +183,15 @@ struct bgp_bucket { #define BGP_PORT 179 #define BGP_VERSION 4 #define BGP_HEADER_LENGTH 19 -#define BGP_MAX_PACKET_LENGTH 4096 +#define BGP_MAX_MESSAGE_LENGTH 4096 +#define BGP_MAX_EXT_MSG_LENGTH 65535 #define BGP_RX_BUFFER_SIZE 4096 -#define BGP_TX_BUFFER_SIZE BGP_MAX_PACKET_LENGTH +#define BGP_TX_BUFFER_SIZE 4096 +#define BGP_RX_BUFFER_EXT_SIZE 65535 +#define BGP_TX_BUFFER_EXT_SIZE 65535 + +static inline int bgp_max_packet_length(struct bgp_proto *p) +{ return p->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; } extern struct linpool *bgp_linpool; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 49afe5ae..85b93a6b 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) + CHECK, LINK, PORT, EXTENDED, MESSAGES) CF_GRAMMAR @@ -108,6 +108,7 @@ bgp_proto: | bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; } | bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; } | bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; } + | bgp_proto ENABLE EXTENDED MESSAGES bool ';' { BGP_CFG->enable_extended_messages = $5; } | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } | bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 378f5ab1..ed99f623 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -84,7 +84,7 @@ mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4) static void mrt_dump_bgp_packet(struct bgp_conn *conn, byte *pkt, unsigned len) { - byte buf[BGP_MAX_PACKET_LENGTH + 128]; + byte *buf = alloca(128+len); /* 128 is enough for MRT headers */ byte *bp = buf + MRTDUMP_HDR_LENGTH; int as4 = conn->bgp->as4_session; @@ -223,6 +223,14 @@ bgp_put_cap_err(struct bgp_proto *p UNUSED, byte *buf) return buf; } +static byte * +bgp_put_cap_ext_msg(struct bgp_proto *p UNUSED, byte *buf) +{ + *buf++ = 230; /* Capability TBD: Support for extended messages */ + *buf++ = 0; /* Capability data length */ + return buf; +} + static byte * bgp_create_open(struct bgp_conn *conn, byte *buf) @@ -274,6 +282,9 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) if (p->cf->enable_refresh) cap = bgp_put_cap_err(p, cap); + if (p->cf->enable_extended_messages) + cap = bgp_put_cap_ext_msg(p, cap); + cap_len = cap - buf - 12; if (cap_len > 0) { @@ -342,7 +353,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) { struct bgp_proto *p = conn->bgp; struct bgp_bucket *buck; - int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; + int remains = bgp_max_packet_length(p) - BGP_HEADER_LENGTH - 4; byte *w; int wd_size = 0; int r_size = 0; @@ -428,7 +439,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) struct bgp_proto *p = conn->bgp; struct bgp_bucket *buck; int size, second, rem_stored; - int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; + int remains = bgp_max_packet_length(p) - BGP_HEADER_LENGTH - 4; byte *w, *w_stored, *tmp, *tstart; ip_addr *ipp, ip, ip_ll; ea_list *ea; @@ -856,6 +867,12 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) conn->peer_enhanced_refresh_support = 1; break; + case 230: /* Extended message length capability, draft, cap number TBD */ + if (cl != 0) + goto err; + conn->peer_ext_messages_support = 1; + break; + /* We can safely ignore all other capabilities */ } len -= 2 + cl; @@ -1019,6 +1036,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) p->add_path_rx = (p->cf->add_path & ADD_PATH_RX) && (conn->peer_add_path & ADD_PATH_TX); p->add_path_tx = (p->cf->add_path & ADD_PATH_TX) && (conn->peer_add_path & ADD_PATH_RX); p->gr_ready = p->cf->gr_mode && conn->peer_gr_able; + p->ext_messages = p->cf->enable_extended_messages && conn->peer_ext_messages_support; if (p->add_path_tx) p->p.accept_ra_types = RA_ANY; @@ -1418,7 +1436,7 @@ static struct { { 2, 4, "Unsupported optional parameter" }, { 2, 5, "Authentication failure" }, { 2, 6, "Unacceptable hold time" }, - { 2, 7, "Required capability missing" }, /* [RFC3392] */ + { 2, 7, "Required capability missing" }, /* [RFC5492] */ { 2, 8, "No supported AFI/SAFI" }, /* This error msg is nonstandard */ { 3, 0, "Invalid UPDATE message" }, { 3, 1, "Malformed attribute list" }, @@ -1666,6 +1684,7 @@ int bgp_rx(sock *sk, int size) { struct bgp_conn *conn = sk->data; + struct bgp_proto *p = conn->bgp; byte *pkt_start = sk->rbuf; byte *end = pkt_start + size; unsigned i, len; @@ -1682,7 +1701,7 @@ bgp_rx(sock *sk, int size) break; } len = get_u16(pkt_start+16); - if (len < BGP_HEADER_LENGTH || len > BGP_MAX_PACKET_LENGTH) + if (len < BGP_HEADER_LENGTH || len > bgp_max_packet_length(p)) { bgp_error(conn, 1, 2, pkt_start+16, 2); break; From 6683d42d5b560c8805b977736b2a769ea2d9aa8b Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 18 Jul 2015 19:05:11 +0200 Subject: [PATCH 018/165] Documentation update --- doc/bird.sgml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 2ae9f649..df83aacd 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1707,7 +1707,11 @@ using the following configuration parameters: scan time - Time in seconds between two scans of the network interface list. On systems where we are notified about interface status changes asynchronously (such as newer versions of Linux), we need to scan the @@ -2266,6 +2269,20 @@ these attributes: The realm of the route. Can be used for traffic classification. +

In Linux, there is also a plenty of obscure route attributes mostly focused +on tuning TCP performance of local connections. BIRD supports most of these +attributes, see Linux or iproute2 documentation for their meaning. Attributes +Example

A simple configuration can look this way: @@ -3394,7 +3411,9 @@ of the protocol contains mainly a list of static routes: route - Static route through a neighboring router. + Static route through a neighboring router. For link-local next hops, + interface can be specified as a part of the address (e.g., + route Static multipath route. Contains several nexthops (gateways), possibly From 17661ff934a80d517284c96756357d4ed5af9a64 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 18 Jul 2015 19:30:35 +0200 Subject: [PATCH 019/165] Nest: Fixes symbols in router id Thanks to Peter Hudec for noticing the problem. --- nest/config.Y | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nest/config.Y b/nest/config.Y index 37551802..7ad6c712 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -94,6 +94,7 @@ rtrid: idval: NUM { $$ = $1; } + | '(' term ')' { $$ = f_eval_int($2); } | RTRID | IPA { #ifndef IPV6 @@ -102,6 +103,16 @@ idval: cf_error("Router IDs must be entered as hexadecimal numbers or IPv4 addresses in IPv6 version"); #endif } + | SYM { + if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD)) + $$ = SYM_VAL($1).i; +#ifndef IPV6 + else if ($1->class == (SYM_CONSTANT | T_IP)) + $$ = SYM_VAL($1).px.ip; +#endif + else + cf_error("Number of IPv4 address constant expected"); + } ; From a8ad8fd6491d04620fe4fdebc50f0da2927f9b21 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 18 Jul 2015 23:15:04 +0200 Subject: [PATCH 020/165] Simplify build messages Thanks to Christian Tacke for the original patch. --- tools/Makefile.in | 9 ++++++--- tools/Rules.in | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/Makefile.in b/tools/Makefile.in index 062ba916..01bb7a7c 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -37,13 +37,16 @@ subdir: sysdep/paths.h .dir-stamp .dep-stamp set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done $(exedir)/bird: $(bird-dep) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + @echo LD $(LDFLAGS) -o $@ $^ $(LIBS) + @$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(exedir)/birdc: $(birdc-dep) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CLIENT_LIBS) + @echo LD $(LDFLAGS) -o $@ $^ $(LIBS) $(CLIENT_LIBS) + @$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CLIENT_LIBS) $(exedir)/birdcl: $(birdcl-dep) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + @echo LD $(LDFLAGS) -o $@ $^ $(LIBS) + @$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) .dir-stamp: sysdep/paths.h mkdir -p $(static-dirs) $(client-dirs) $(doc-dirs) diff --git a/tools/Rules.in b/tools/Rules.in index ca930ec8..f00c85d1 100644 --- a/tools/Rules.in +++ b/tools/Rules.in @@ -68,12 +68,14 @@ subdir: all.o all.o: $(objs) # $(LD) -r -o $@ $^ # Changed to $(CC) because $(LD) has problems with crosscompiling - $(CC) -nostdlib -r -o $@ $^ + @echo LD -r -o $@ $^ + @$(CC) -nostdlib -r -o $@ $^ endif %.o: $(src-path)%.c - $(CC) $(CFLAGS) -o $@ -c $< + @echo CC -o $@ -c $< + @$(CC) $(CFLAGS) -o $@ -c $< ifndef source-dep source-dep := $(source) From ffa398b8d8bac4cf6368fe700466cad4ff12fee8 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 19 Jul 2015 11:39:24 +0200 Subject: [PATCH 021/165] BFD: Fixes crash after socket error Thanks to Thomas King for the bugreport. --- proto/bfd/bfd.c | 28 ++++++++++++++++------------ proto/bfd/packets.c | 8 ++++++-- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 5f089846..7a085791 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -43,7 +43,7 @@ * the needs of BFD sessions. When a new session is created, it requests a * proper BFD interface by function bfd_get_iface(), which either finds an * existing one in &iface_list (from &bfd_proto) or allocates a new one. When a - * session is removed, an associated iface is dicharged by bfd_free_iface(). + * session is removed, an associated iface is discharged by bfd_free_iface(). * * BFD requests are the external API for the other protocols. When a protocol * wants a BFD session, it calls bfd_request_session(), which creates a @@ -62,7 +62,7 @@ * configuration (like static routes in the static protocol). BFD neighbors are * handled by BFD protocol like it is a BFD client -- when a BFD neighbor is * ready, the protocol just creates a BFD request like any other protocol. - * + * * The protocol uses a new generic event loop (structure &birdloop) from |io.c|, * which supports sockets, timers and events like the main loop. Timers * (structure &timer2) are new microsecond based timers, while sockets and @@ -129,11 +129,11 @@ static inline void bfd_notify_kick(struct bfd_proto *p); * BFD sessions */ -static void +static void bfd_session_update_state(struct bfd_session *s, uint state, uint diag) { struct bfd_proto *p = s->ifa->bfd; - uint old_state = s->loc_state; + uint old_state = s->loc_state; int notify; if (state == old_state) @@ -201,8 +201,8 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset) if (s->passive && (s->rem_id == 0)) goto stop; - if (s->rem_demand_mode && - !s->poll_active && + if (s->rem_demand_mode && + !s->poll_active && (s->loc_state == BFD_STATE_UP) && (s->rem_state == BFD_STATE_UP)) goto stop; @@ -303,7 +303,7 @@ bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old bfd_send_ctl(s->ifa->bfd, s, 1); } -static void +static void bfd_session_timeout(struct bfd_session *s) { struct bfd_proto *p = s->ifa->bfd; @@ -353,7 +353,7 @@ bfd_session_set_min_rx(struct bfd_session *s, u32 val) if (val == s->req_min_rx_new) return; - s->req_min_rx_new = val; + s->req_min_rx_new = val; /* Postpone timer update if req_min_rx_int decreases and the session is up */ if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int)) @@ -575,9 +575,13 @@ bfd_free_iface(struct bfd_iface *ifa) if (!ifa || --ifa->uc) return; + if (ifa->sk) + { + sk_stop(ifa->sk); + rfree(ifa->sk); + } + rem_node(&ifa->n); - sk_stop(ifa->sk); - rfree(ifa->sk); mb_free(ifa); } @@ -873,7 +877,7 @@ bfd_notify_hook(sock *sk, int len) diag = s->loc_diag; bfd_unlock_sessions(p); - /* FIXME: convert to btime and move to bfd_session_update_state() */ + /* FIXME: convert to btime and move to bfd_session_update_state() */ s->last_state_change = now; s->notify_running = 1; @@ -1092,7 +1096,7 @@ bfd_show_sessions(struct proto *P) /* FIXME: this is thread-unsafe, but perhaps harmless */ state = s->loc_state; diag = s->loc_diag; - ifname = (s->ifa && s->ifa->sk->iface) ? s->ifa->sk->iface->name : "---"; + ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---"; tx_int = s->last_tx ? (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS) : 0; timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult; diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c index b5fd6782..cb40bcda 100644 --- a/proto/bfd/packets.c +++ b/proto/bfd/packets.c @@ -63,9 +63,13 @@ void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final) { sock *sk = s->ifa->sk; - struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->tbuf; + struct bfd_ctl_packet *pkt; char fb[8]; + if (!sk) + return; + + pkt = (struct bfd_ctl_packet *) sk->tbuf; pkt->vdiag = bfd_pack_vdiag(1, s->loc_diag); pkt->flags = bfd_pack_flags(s->loc_state, 0); pkt->detect_mult = s->detect_mult; @@ -139,7 +143,7 @@ bfd_rx_hook(sock *sk, int len) u8 ps = bfd_pkt_get_state(pkt); if (ps > BFD_STATE_DOWN) DROP("invalid init state", ps); - + s = bfd_find_session_by_addr(p, sk->faddr); /* FIXME: better session matching and message */ From 1321e12ac460bd542d3946a0c4a4dacd71157cfa Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 20 Jul 2015 11:12:02 +0200 Subject: [PATCH 022/165] Static: Allows to specify attributes for static routes The patch adds suport for specifying route attributes together with static routes, e.g.: route 10.1.1.0/24 via 10.0.0.1 { krt_advmss = 1200; ospf_metric1 = 100; }; --- filter/filter.c | 24 ++++++++++++++++++++++++ filter/filter.h | 1 + proto/ospf/ospf.c | 17 ++++++++++++++--- proto/static/config.Y | 19 ++++++++++++++++++- proto/static/static.c | 30 +++++++++++++++++++++++++----- proto/static/static.h | 1 + 6 files changed, 83 insertions(+), 9 deletions(-) diff --git a/filter/filter.c b/filter/filter.c index 3f8968aa..55062aca 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1527,6 +1527,30 @@ f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struc return res.val.i; } +/* TODO: perhaps we could integrate f_eval(), f_eval_rte() and f_run() */ + +struct f_val +f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool) +{ + struct ea_list *tmp_attrs = NULL; + + f_rte = rte; + f_old_rta = NULL; + f_tmp_attrs = &tmp_attrs; + f_pool = tmp_pool; + f_flags = 0; + + LOG_BUFFER_INIT(f_buf); + + /* Note that in this function we assume that rte->attrs is private / uncached */ + struct f_val res = interpret(expr); + + /* Hack to include EAF_TEMP attributes to the main list */ + (*rte)->attrs->eattrs = ea_append(tmp_attrs, (*rte)->attrs->eattrs); + + return res; +} + struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool) { diff --git a/filter/filter.h b/filter/filter.h index 2b2d23c2..e59c8226 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -107,6 +107,7 @@ struct ea_list; struct rte; int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags); +struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool); struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool); uint f_eval_int(struct f_inst *expr); u32 f_eval_asn(struct f_inst *expr); diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 1bc4e077..d5d5d354 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -450,10 +450,21 @@ ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool if (oa_is_stub(oa)) return -1; /* Do not export routes to stub areas */ - eattr *ea = ea_find(e->attrs->eattrs, EA_GEN_IGP_METRIC); - u32 m1 = (ea && (ea->u.data < LSINFINITY)) ? ea->u.data : LSINFINITY; + ea_list *ea = e->attrs->eattrs; + u32 m0 = ea_get_int(ea, EA_GEN_IGP_METRIC, LSINFINITY); + u32 m1 = MIN(m0, LSINFINITY); + u32 m2 = 10000; + u32 tag = 0; + + /* Hack for setting attributes directly in static protocol */ + if (e->attrs->source == RTS_STATIC) + { + m1 = ea_get_int(ea, EA_OSPF_METRIC1, m1); + m2 = ea_get_int(ea, EA_OSPF_METRIC2, 10000); + tag = ea_get_int(ea, EA_OSPF_TAG, 0); + } - *attrs = ospf_build_attrs(*attrs, pool, m1, 10000, 0, 0); + *attrs = ospf_build_attrs(*attrs, pool, m1, m2, tag, 0); return 0; /* Leave decision to the filters */ } diff --git a/proto/static/config.Y b/proto/static/config.Y index a8bfa36f..d1b62af9 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -14,6 +14,7 @@ CF_DEFINES #define STATIC_CFG ((struct static_config *) this_proto) static struct static_route *this_srt, *this_srt_nh, *last_srt_nh; +static struct f_inst **this_srt_last_cmd; CF_DECLS @@ -36,7 +37,7 @@ static_proto: | static_proto proto_item ';' | static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; } | static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; } - | static_proto stat_route ';' + | static_proto stat_route stat_route_opt_list ';' ; stat_route0: ROUTE prefix { @@ -44,6 +45,7 @@ stat_route0: ROUTE prefix { add_tail(&STATIC_CFG->other_routes, &this_srt->n); this_srt->net = $2.addr; this_srt->masklen = $2.len; + this_srt_last_cmd = &(this_srt->cmds); } ; @@ -94,6 +96,21 @@ stat_route: | stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; } ; +stat_route_item: + cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); } + ; + +stat_route_opts: + /* empty */ + | stat_route_opts stat_route_item + ; + +stat_route_opt_list: + /* empty */ + | '{' stat_route_opts '}' + ; + + CF_CLI(SHOW STATIC, optsym, [], [[Show details of static protocol]]) { static_show(proto_get_named($3, &proto_static)); } ; diff --git a/proto/static/static.c b/proto/static/static.c index e7e7ab15..57c44885 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -42,11 +42,14 @@ #include "nest/route.h" #include "nest/cli.h" #include "conf/conf.h" +#include "filter/filter.h" #include "lib/string.h" #include "lib/alloca.h" #include "static.h" +static linpool *static_lp; + static inline rtable * p_igp_table(struct proto *p) { @@ -54,12 +57,11 @@ p_igp_table(struct proto *p) return cf->igp_table ? cf->igp_table->table : p->table; } - static void static_install(struct proto *p, struct static_route *r, struct iface *ifa) { net *n; - rta a, *aa; + rta a; rte *e; if (r->installed > 0) @@ -108,13 +110,21 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa) if (r->dest == RTDX_RECURSIVE) rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via); - aa = rta_lookup(&a); + /* We skip rta_lookup() here */ + n = net_get(p->table, r->net, r->masklen); - e = rte_get_temp(aa); + e = rte_get_temp(&a); e->net = n; e->pflags = 0; + + if (r->cmds) + f_eval_rte(r->cmds, &e, static_lp); + rte_update(p, n, e); r->installed = 1; + + if (r->cmds) + lp_flush(static_lp); } static void @@ -220,6 +230,9 @@ static_start(struct proto *p) DBG("Static: take off!\n"); + if (!static_lp) + static_lp = lp_new(&root_pool, 1008); + if (cf->igp_table) rt_lock_table(cf->igp_table->table); @@ -413,6 +426,13 @@ static_same_dest(struct static_route *x, struct static_route *y) } } +static inline int +static_same_rte(struct static_route *x, struct static_route *y) +{ + return static_same_dest(x, y) && i_same(x->cmds, y->cmds); +} + + static void static_match(struct proto *p, struct static_route *r, struct static_config *n) { @@ -441,7 +461,7 @@ static_match(struct proto *p, struct static_route *r, struct static_config *n) found: /* If destination is different, force reinstall */ - if ((r->installed > 0) && !static_same_dest(r, t)) + if ((r->installed > 0) && !static_same_rte(r, t)) t->installed = -1; else t->installed = r->installed; diff --git a/proto/static/static.h b/proto/static/static.h index 99a0e68b..f197d352 100644 --- a/proto/static/static.h +++ b/proto/static/static.h @@ -31,6 +31,7 @@ struct static_route { struct neighbor *neigh; byte *if_name; /* Name for RTD_DEVICE routes */ struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */ + struct f_inst *cmds; /* List of commands for setting attributes */ int installed; /* Installed in rt table, -1 for reinstall */ }; From 538264cf1a7690d90b2953aebff21958c2b55c44 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 24 Jul 2015 18:02:07 +0200 Subject: [PATCH 023/165] Static: Support for BFD controlled static routes --- nest/bfd.h | 6 ++ proto/static/config.Y | 21 +++++- proto/static/static.c | 148 ++++++++++++++++++++++++++++++++---------- proto/static/static.h | 5 ++ 4 files changed, 142 insertions(+), 38 deletions(-) diff --git a/nest/bfd.h b/nest/bfd.h index 79c3c921..f1e95cb2 100644 --- a/nest/bfd.h +++ b/nest/bfd.h @@ -32,6 +32,12 @@ struct bfd_request { }; +#define BFD_STATE_ADMIN_DOWN 0 +#define BFD_STATE_DOWN 1 +#define BFD_STATE_INIT 2 +#define BFD_STATE_UP 3 + + #ifdef CONFIG_BFD struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, void (*hook)(struct bfd_request *), void *data); diff --git a/proto/static/config.Y b/proto/static/config.Y index d1b62af9..182721b3 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -16,10 +16,22 @@ CF_DEFINES static struct static_route *this_srt, *this_srt_nh, *last_srt_nh; static struct f_inst **this_srt_last_cmd; +static void +static_route_finish(void) +{ + struct static_route *r; + + /* Update undefined use_bfd entries in multipath nexthops */ + if (this_srt->dest == RTD_MULTIPATH) + for (r = this_srt->mp_next; r; r = r->mp_next) + if (r->use_bfd < 0) + r->use_bfd = this_srt->use_bfd; +} + CF_DECLS CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK) -CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE) +CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD) CF_GRAMMAR @@ -37,7 +49,7 @@ static_proto: | static_proto proto_item ';' | static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; } | static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; } - | static_proto stat_route stat_route_opt_list ';' + | static_proto stat_route stat_route_opt_list ';' { static_route_finish(); } ; stat_route0: ROUTE prefix { @@ -57,11 +69,15 @@ stat_multipath1: this_srt_nh->via = $2; this_srt_nh->via_if = $3; this_srt_nh->if_name = (void *) this_srt; /* really */ + this_srt_nh->use_bfd = -1; /* undefined */ } | stat_multipath1 WEIGHT expr { this_srt_nh->masklen = $3 - 1; /* really */ if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256"); } + | stat_multipath1 BFD bool { + this_srt_nh->use_bfd = $3; cf_check_bfd($3); + } ; stat_multipath: @@ -98,6 +114,7 @@ stat_route: stat_route_item: cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); } + | BFD bool ';' { this_srt->use_bfd = $2; cf_check_bfd($2); } ; stat_route_opts: diff --git a/proto/static/static.c b/proto/static/static.c index 57c44885..be808593 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -141,6 +141,29 @@ static_remove(struct proto *p, struct static_route *r) r->installed = 0; } +static void +static_bfd_notify(struct bfd_request *req); + +static void +static_update_bfd(struct proto *p, struct static_route *r) +{ + struct neighbor *nb = r->neigh; + int bfd_up = (nb->scope > 0) && r->use_bfd; + + if (bfd_up && !r->bfd_req) + { + // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip; + r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface, + static_bfd_notify, r); + } + + if (!bfd_up && r->bfd_req) + { + rfree(r->bfd_req); + r->bfd_req = NULL; + } +} + static int static_decide(struct static_config *cf, struct static_route *r) { @@ -153,6 +176,9 @@ static_decide(struct static_config *cf, struct static_route *r) if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP)) return 0; + if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP) + return 0; + return 1; } @@ -171,6 +197,8 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r) r->chain = n->data; n->data = r; r->neigh = n; + + static_update_bfd(p, r); if (static_decide(cf, r)) static_install(p, r, n->iface); else @@ -200,6 +228,8 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r) r2->chain = n->data; n->data = r2; r2->neigh = n; + + static_update_bfd(p, r2); r2->installed = static_decide(cf, r2); count += r2->installed; } @@ -222,6 +252,26 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r) } } +static void +static_rte_cleanup(struct proto *p, struct static_route *r) +{ + struct static_route *r2; + + if (r->bfd_req) + { + rfree(r->bfd_req); + r->bfd_req = NULL; + } + + if (r->dest == RTD_MULTIPATH) + for (r2 = r->mp_next; r2; r2 = r2->mp_next) + if (r2->bfd_req) + { + rfree(r2->bfd_req); + r2->bfd_req = NULL; + } +} + static int static_start(struct proto *p) { @@ -254,7 +304,10 @@ static_shutdown(struct proto *p) WALK_LIST(r, cf->iface_routes) r->installed = 0; WALK_LIST(r, cf->other_routes) + { + static_rte_cleanup(p, r); r->installed = 0; + } return PS_DOWN; } @@ -268,6 +321,44 @@ static_cleanup(struct proto *p) rt_unlock_table(cf->igp_table->table); } +static void +static_update_rte(struct proto *p, struct static_route *r) +{ + switch (r->dest) + { + case RTD_ROUTER: + if (static_decide((struct static_config *) p->cf, r)) + static_install(p, r, r->neigh->iface); + else + static_remove(p, r); + break; + + case RTD_NONE: /* a part of multipath route */ + { + int decision = static_decide((struct static_config *) p->cf, r); + if (decision == r->installed) + break; /* no change */ + r->installed = decision; + + struct static_route *r1, *r2; + int count = 0; + r1 = (void *) r->if_name; /* really */ + for (r2 = r1->mp_next; r2; r2 = r2->mp_next) + count += r2->installed; + + if (count) + { + /* Set of nexthops changed - force reinstall */ + r1->installed = 0; + static_install(p, r1, NULL); + } + else + static_remove(p, r1); + + break; + } + } +} static void static_neigh_notify(struct neighbor *n) @@ -277,40 +368,21 @@ static_neigh_notify(struct neighbor *n) DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface); for(r=n->data; r; r=r->chain) - switch (r->dest) - { - case RTD_ROUTER: - if (static_decide((struct static_config *) p->cf, r)) - static_install(p, r, n->iface); - else - static_remove(p, r); - break; + { + static_update_bfd(p, r); + static_update_rte(p, r); + } +} - case RTD_NONE: /* a part of multipath route */ - { - int decision = static_decide((struct static_config *) p->cf, r); - if (decision == r->installed) - break; /* no change */ - r->installed = decision; - - struct static_route *r1, *r2; - int count = 0; - r1 = (void *) r->if_name; /* really */ - for (r2 = r1->mp_next; r2; r2 = r2->mp_next) - count += r2->installed; - - if (count) - { - /* Set of nexthops changed - force reinstall */ - r1->installed = 0; - static_install(p, r1, NULL); - } - else - static_remove(p, r1); +static void +static_bfd_notify(struct bfd_request *req) +{ + struct static_route *r = req->data; + struct proto *p = r->neigh->proto; - break; - } - } + // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX); + + static_update_rte(p, r); } static void @@ -414,7 +486,7 @@ static_same_dest(struct static_route *x, struct static_route *y) for (x = x->mp_next, y = y->mp_next; x && y; x = x->mp_next, y = y->mp_next) - if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if)) + if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd)) return 0; return !x && !y; @@ -499,6 +571,9 @@ static_reconfigure(struct proto *p, struct proto_config *new) WALK_LIST(r, n->other_routes) static_add(p, n, r); + WALK_LIST(r, o->other_routes) + static_rte_cleanup(p, r); + return 1; } @@ -584,13 +659,14 @@ static_show_rt(struct static_route *r) case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break; default: bsprintf(via, "???"); } - cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)"); + cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via, + r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)"); struct static_route *r2; if (r->dest == RTD_MULTIPATH) for (r2 = r->mp_next; r2; r2 = r2->mp_next) - cli_msg(-1009, "\tvia %I%J weight %d%s", r2->via, r2->via_if, r2->masklen + 1, /* really */ - r2->installed ? "" : " (dormant)"); + cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */ + r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)"); } void diff --git a/proto/static/static.h b/proto/static/static.h index f197d352..6b047234 100644 --- a/proto/static/static.h +++ b/proto/static/static.h @@ -9,6 +9,9 @@ #ifndef _BIRD_STATIC_H_ #define _BIRD_STATIC_H_ +#include "nest/route.h" +#include "nest/bfd.h" + struct static_config { struct proto_config c; list iface_routes; /* Routes to search on interface events */ @@ -33,6 +36,8 @@ struct static_route { struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */ struct f_inst *cmds; /* List of commands for setting attributes */ int installed; /* Installed in rt table, -1 for reinstall */ + int use_bfd; /* Configured to use BFD */ + struct bfd_request *bfd_req; /* BFD request, if BFD is used */ }; /* Dummy nodes (parts of multipath route) abuses masklen field for weight From 641172c6e5e4e291029084074f94f448d6bc69dd Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 28 Jul 2015 12:35:12 +0200 Subject: [PATCH 024/165] Netlink: Fixes uninitialized variable Thanks to Pavel Tvrdik for the bugfix --- sysdep/linux/netlink.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 9f206e1c..9c9449e2 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -100,11 +100,12 @@ nl_request_dump(int af, int cmd) struct { struct nlmsghdr nh; struct rtgenmsg g; - } req; - req.nh.nlmsg_type = cmd; - req.nh.nlmsg_len = sizeof(req); - req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.g.rtgen_family = af; + } req = { + .nh.nlmsg_type = cmd, + .nh.nlmsg_len = sizeof(req), + .nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .g.rtgen_family = af + }; nl_send(&nl_scan, &req.nh); } From dbf4c0cb258bd92efb54f95194f664f95ba98fd9 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 28 Jul 2015 12:56:51 +0200 Subject: [PATCH 025/165] Minor update to test commits --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index daeb18bd..5d4bd9b2 100644 --- a/README +++ b/README @@ -64,6 +64,7 @@ What do we support: o Static routes o Inter-table protocol o IPv6 router advertisements + o Bidirectional Forwarding Detection (BFD) o Command-line interface (using the `birdc' client; to get some help, just press `?') o Soft reconfiguration -- no online commands for changing the From c7b99a932cab1873042e356143ab71755920157a Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 28 Jul 2015 15:08:21 +0200 Subject: [PATCH 026/165] Nest: Fixes one of previous commit --- nest/config.Y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nest/config.Y b/nest/config.Y index 7ad6c712..799a09f9 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -108,7 +108,7 @@ idval: $$ = SYM_VAL($1).i; #ifndef IPV6 else if ($1->class == (SYM_CONSTANT | T_IP)) - $$ = SYM_VAL($1).px.ip; + $$ = ipa_to_u32(SYM_VAL($1).px.ip); #endif else cf_error("Number of IPv4 address constant expected"); From b5e76398de1d4468b4061d9ef57dd3154b2f745e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 19 Aug 2015 11:16:23 +0200 Subject: [PATCH 027/165] OSPF: Fixes some issues with link detection Thanks to Bernardo Figueiredo and Israel G. Lugo for the bugreport. --- proto/ospf/iface.c | 7 +++++-- proto/ospf/packet.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 9b0f7797..77ce839a 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -493,8 +493,11 @@ ospf_iface_add(struct object_lock *lock) ifa->flood_queue = mb_allocz(ifa->pool, ifa->flood_queue_size * sizeof(void *)); } - /* Do iface UP, unless there is no link and we use link detection */ - ospf_iface_sm(ifa, (ifa->check_link && !(ifa->iface->flags & IF_LINK_UP)) ? ISM_LOOP : ISM_UP); + /* Do iface UP, unless there is no link (then wait in LOOP state) */ + if (!ifa->check_link || (ifa->iface->flags & IF_LINK_UP)) + ospf_iface_sm(ifa, ISM_UP); + else + ospf_iface_chstate(ifa, OSPF_IS_LOOP); } static inline void diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index fb63e61c..6b8fd7b5 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -231,6 +231,10 @@ ospf_rx_hook(sock *sk, int len) const char *err_dsc = NULL; uint err_val = 0; + /* Should not happen */ + if (ifa->state <= OSPF_IS_LOOP) + return 1; + int src_local, dst_local, dst_mcast; src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen); dst_local = ipa_equal(sk->laddr, ifa->addr->ip); From 8465dccb06afffed171dc1e224e4eb5f67cc3326 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 5 Oct 2015 12:14:50 +0200 Subject: [PATCH 028/165] Major RIP redesign The new RIP implementation fixes plenty of old bugs and also adds support for many new features: ECMP support, link state support, BFD support, configurable split horizon and more. Most options are now per-interface. --- client/commands.c | 2 +- doc/bird.sgml | 244 ++++-- doc/reply_codes | 2 + lib/ip.c | 4 +- lib/ip.h | 7 + nest/password.c | 15 + nest/password.h | 3 +- nest/route.h | 12 +- nest/rt-fib.c | 19 + nest/rt-table.c | 6 - proto/ospf/packet.c | 2 +- proto/rip/Doc | 2 +- proto/rip/Makefile | 2 +- proto/rip/auth.c | 168 ---- proto/rip/config.Y | 207 +++-- proto/rip/packets.c | 772 ++++++++++++++++++ proto/rip/rip.c | 1853 ++++++++++++++++++++++++------------------- proto/rip/rip.h | 330 ++++---- 18 files changed, 2364 insertions(+), 1286 deletions(-) delete mode 100644 proto/rip/auth.c create mode 100644 proto/rip/packets.c diff --git a/client/commands.c b/client/commands.c index 50fcba40..226ae048 100644 --- a/client/commands.c +++ b/client/commands.c @@ -67,7 +67,7 @@ cmd_build_tree(void) new->plastson = &new->son; new->len = c-d; memcpy(new->token, d, c-d); - new->prio = (new->len == 3 && !memcmp(new->token, "roa", 3)) ? 0 : 1; /* Hack */ + new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */ } old = new; while (isspace(*c)) diff --git a/doc/bird.sgml b/doc/bird.sgml index df83aacd..86df0456 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2476,7 +2476,7 @@ protocol ospf <name> { This option specifies whether OSPF is allowed to generate ECMP (equal-cost multipath) routes. Such routes are used when there are several directions to the destination, each with the same (computed) - cost. This option also allows to specify a limit on maximal number of + cost. This option also allows to specify a limit on maximum number of nexthops in one route. By default, ECMP is disabled. If enabled, default value of the limit is 16. @@ -3244,16 +3244,14 @@ one). After some time, the distance reaches infinity (that's 15 in RIP) and all routers know that network is unreachable. RIP tries to minimize situations where counting to infinity is necessary, because it is slow. Due to infinity being 16, you can't use RIP on networks where maximal distance is higher than 15 -hosts. You can read more about RIP at -. Both IPv4 -(RFC 1723 ) and IPv6 -(RFC 2080 ) versions -of RIP are supported by BIRD, historical RIPv1 -(RFC 1058 ) is not -currently supported. RIPv4 MD5 authentication -(RFC 2082 ) is -supported. +hosts. + +

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

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 @@ -3261,39 +3259,156 @@ pretty much obsolete. It is still usable on very small networks. Configuration -

In addition to options common for all to other protocols, RIP supports the -following ones: +

RIP configuration consists mainly of common protocol options and interface +definitions, most RIP options are interface specific. + + +protocol rip [<name>] { + infinity <number>; + ecmp <switch> [limit <number>]; + interface <interface pattern> { + metric <number>; + mode multicast|broadcast; + passive <switch>; + address <ip>; + port <number>; + version 1|2; + split horizon <switch>; + poison reverse <switch>; + check zero <switch>; + update time <number>; + timeout time <number>; + garbage time <number>; + ecmp weight <number>; + ttl security <switch>; | tx only; + tx class|dscp <number>; + tx priority <number>; + rx buffer <number>; + tx length <number>; + check link <switch>; + authentication none|plaintext|cryptographic; + password "<text>"; + password "<text>" { + id <num>; + generate from "<date>"; + generate to "<date>"; + accept from "<date>"; + accept to "<date>"; + }; + }; +} + - authentication none|plaintext|md5 - Selects authentication method to be used. password - section. Default: none. + infinity number + Selects the distance of infinity. Bigger values will make + protocol convergence even slower. The default value is 16. - honor always|neighbor|never - Specifies when should requests for dumping routing table be honored. - (Always, when sent from a host on a directly connected network or - never.) Routing table updates are honored only from neighbors, that is - not configurable. Default: never. + ecmp switch [limit number] + This option specifies whether RIP is allowed to generate ECMP + (equal-cost multipath) routes. Such routes are used when there are + several directions to the destination, each with the same (computed) + cost. This option also allows to specify a limit on maximum number of + nexthops in one route. By default, ECMP is disabled. If enabled, + default value of the limit is 16. + + interface + Interface definitions specify a set of interfaces on which the + protocol is activated and contain interface specific options. + See common options for + detailed description. -

There are some options that can be specified per-interface: +

Interface specific options: metric - This option specifies the metric of the interface. Valid + This option specifies the metric of the interface. When a route is + received from the interface, its metric is increased by this value + before further processing. Valid values are 1-255, but values higher + than infinity has no further meaning. Default: 1. + + mode multicast|broadcast + This option selects the mode for RIP to use on the interface. The + default is multicast mode for RIPv2 and broadcast mode for RIPv1. + RIPng always uses the multicast mode. + + passive + Passive interfaces receive routing updates but do not transmit any + messages. Default: no. + + address + This option specifies a destination address used for multicast or + broadcast messages, the default is the official RIP (224.0.0.9) or RIPng + (ff02::9) multicast address, or an appropriate broadcast address in the + broadcast mode. + + port + This option selects an UDP port to operate on, the default is the + official RIP (520) or RIPng (521) port. + + version 1|2 + This option selects the version of RIP used on the interface. For RIPv1, + automatic subnet aggregation is not implemented, only classful network + routes and host routes are propagated. Note that BIRD allows RIPv1 to be + configured with features that are defined for RIPv2 only, like + authentication or using multicast sockets. The default is RIPv2 for IPv4 + RIP, the option is not supported for RIPng, as no further versions are + defined. + + split horizon + Split horizon is a scheme for preventing routing loops. When split + horizon is active, routes are not regularly propagated back to the + interface from which they were received. They are either not propagated + back at all (plain split horizon) or propagated back with an infinity + metric (split horizon with poisoned reverse). Therefore, other routers + on the interface will not consider the router as a part of an + independent path to the destination of the route. Default: yes. + + poison reverse + When split horizon is active, this option specifies whether the poisoned + reverse variant (propagating routes back with an infinity metric) is + used. The poisoned reverse has some advantages in faster convergence, + but uses more network traffic. Default: yes. + + check zero + Received RIPv1 packets with non-zero values in reserved fields should + be discarded. This option specifies whether the check is performed or + such packets are just processed as usual. Default: yes. + + update time + Specifies the number of seconds between periodic updates. A lower number + will mean faster convergence but bigger network load. Default: 30. + + timeout time + Specifies the time interval (in seconds) between the last received route + announcement and the route expiration. After that, the network is + considered unreachable, but still is propagated with infinity distance. + Default: 180. + + garbage time + Specifies the time interval (in seconds) between the route expiration + and the removal of the unreachable network entry. The garbage interval, + when a route with infinity metric is propagated, is used for both + internal (after expiration) and external (after withdrawal) routes. + Default: 120. + + ecmp weight + When ECMP (multipath) routes are allowed, this value specifies a + relative weight used for nexthops going through the iface. Valid + values are 1-256. Default value is 1. + + authentication none|plaintext|cryptographic + Selects authentication method to be used. password + section. Default: none. - mode multicast|broadcast|quiet|nolisten|version1 - This option selects the mode for RIP to use on the interface. If nothing - is specified, RIP runs in multicast mode. password " + Specifies a password used for authentication. See common option for detailed description. ttl security [ TTL security is a feature that protects routing protocols from remote @@ -3309,43 +3424,31 @@ following ones: compatibility with neighbors regardless of whether they use ttl security. - Note that for RIPng, TTL security is a standard behavior (required by - RFC 2080), but BIRD uses tx class|dscp|priority + tx class|dscp|priority These options specify the ToS/DiffServ/Traffic class/Priority of the outgoing RIP packets. See common option for detailed description. - -

The following options generally override behavior specified in RFC. If you -use any of these options, BIRD will no longer be RFC-compliant, which means it -will not be able to talk to anything other than equally configured BIRD. I have -warned you. + rx buffer + This option specifies the size of buffers used for packet processing. + The buffer size should be bigger than maximal size of received packets. + The default value is 532 for IPv4 RIP and interface MTU value for RIPng. - - port number - Selects IP port to operate on, default 520. (This is useful when testing - BIRD, if you set this to an address >1024, you will not need to run - bird with UID==0). + tx length + This option specifies the maximum length of generated RIP packets. To + avoid IP fragmentation, it should not exceed the interface MTU value. + The default value is 532 for IPv4 RIP and interface MTU value for RIPng. - infinity number - Selects the value of infinity, default is 16. Bigger values will make - protocol convergence even slower. - - period number - Specifies the number of seconds between periodic updates. Default is 30 - seconds. A lower number will mean faster convergence but bigger network - load. Do not use values lower than 12. - - timeout time number - Specifies how old route has to be to be considered unreachable. - Default is 4*garbage time number - Specifies how old route has to be to be discarded. Default is - 10*check link + If set, the hardware link state (as reported by OS) is taken into + 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. Attributes @@ -3356,27 +3459,26 @@ warned you. int RIP metric of the route (ranging from 0 to int RIP route tag: a 16-bit number which can be used to carry additional information with the route (for example, an originating AS number in - case of external routes). When importing a non-RIP route, the tag - defaults to 0. + case of external routes). When a non-RIP route is exported to RIP, the + default tag is 0. Example

-protocol rip MyRIP_test { +protocol rip { debug all; port 1520; period 12; garbage time 60; interface "eth0" { metric 3; mode multicast; }; interface "eth*" { metric 2; mode broadcast; }; - honor neighbor; authentication none; import filter { print "importing"; accept; }; export filter { print "exporting"; accept; }; diff --git a/doc/reply_codes b/doc/reply_codes index cd5f2620..79a7eb92 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -55,6 +55,8 @@ Reply codes of BIRD command-line interface 1018 Show memory 1019 Show ROA list 1020 Show BFD sessions +1021 Show RIP interface +1022 Show RIP neighbors 8000 Reply too long 8001 Route not found diff --git a/lib/ip.c b/lib/ip.c index e1bfba49..3dc8919a 100644 --- a/lib/ip.c +++ b/lib/ip.c @@ -364,7 +364,9 @@ ip4_class_mask(ip4_addr ad) { u32 m, a = _I(ad); - if (a < 0x80000000) + if (a == 0x00000000) + m = 0x00000000; + else if (a < 0x80000000) m = 0xff000000; else if (a < 0xc0000000) m = 0xffff0000; diff --git a/lib/ip.h b/lib/ip.h index 90bb7f8a..9ac5798a 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -15,8 +15,11 @@ #include "lib/unaligned.h" +#define IP4_ALL_NODES ipa_build4(224, 0, 0, 1) +#define IP4_ALL_ROUTERS ipa_build4(224, 0, 0, 2) #define IP4_OSPF_ALL_ROUTERS ipa_build4(224, 0, 0, 5) #define IP4_OSPF_DES_ROUTERS ipa_build4(224, 0, 0, 6) +#define IP4_RIP_ROUTERS ipa_build4(224, 0, 0, 9) #define IP6_ALL_NODES ipa_build6(0xFF020000, 0, 0, 1) #define IP6_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 2) @@ -32,6 +35,10 @@ #define IP_PREC_INTERNET_CONTROL 0xc0 +#define IP4_HEADER_LENGTH 20 +#define IP6_HEADER_LENGTH 40 +#define UDP_HEADER_LENGTH 8 + #ifdef IPV6 #define MAX_PREFIX_LENGTH 128 diff --git a/nest/password.c b/nest/password.c index 21e42e0e..91aaa418 100644 --- a/nest/password.c +++ b/nest/password.c @@ -51,3 +51,18 @@ password_find_by_id(list *l, int id) return NULL; } +struct password_item * +password_find_by_value(list *l, char *pass, uint size) +{ + struct password_item *pi; + + if (!l) + return NULL; + + WALK_LIST(pi, *l) + if (password_verify(pi, pass, size) && (pi->accfrom <= now_real) && (now_real < pi->accto)) + return pi; + + return NULL; +} + diff --git a/nest/password.h b/nest/password.h index cd120d70..1d9de53c 100644 --- a/nest/password.h +++ b/nest/password.h @@ -11,8 +11,6 @@ #define PASSWORD_H #include "lib/timer.h" -#define MD5_AUTH_SIZE 16 - struct password_item { node n; char *password; @@ -24,6 +22,7 @@ extern struct password_item *last_password_item; struct password_item *password_find(list *l, int first_fit); struct password_item *password_find_by_id(list *l, int id); +struct password_item *password_find_by_value(list *l, char *pass, uint size); static inline int password_verify(struct password_item *p1, char *p2, uint size) { diff --git a/nest/route.h b/nest/route.h index 6067526d..285fb372 100644 --- a/nest/route.h +++ b/nest/route.h @@ -75,6 +75,8 @@ void fib_check(struct fib *); /* Consistency check for debugging */ void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't call */ struct fib_node *fit_get(struct fib *, struct fib_iterator *); void fit_put(struct fib_iterator *, struct fib_node *); +void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos); + #define FIB_WALK(fib, z) do { \ struct fib_node *z, **ff = (fib)->hash_table; \ @@ -103,6 +105,11 @@ void fit_put(struct fib_iterator *, struct fib_node *); #define FIB_ITERATE_PUT(it, z) fit_put(it, z) +#define FIB_ITERATE_PUT_NEXT(it, fib, z) fit_put_next(fib, it, z, hpos) + +#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it) + + /* * Master Routing Tables. Generally speaking, each of them contains a FIB * with each entry pointing to a list of route entries representing routes @@ -196,10 +203,9 @@ typedef struct rte { union { /* Protocol-dependent data (metrics etc.) */ #ifdef CONFIG_RIP struct { - node garbage; /* List for garbage collection */ - byte metric; /* RIP metric */ + struct iface *from; /* Incoming iface */ + u8 metric; /* RIP metric */ u16 tag; /* External route tag */ - struct rip_entry *entry; } rip; #endif #ifdef CONFIG_OSPF diff --git a/nest/rt-fib.c b/nest/rt-fib.c index aa5e2357..a73de1fd 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -430,6 +430,25 @@ fit_put(struct fib_iterator *i, struct fib_node *n) i->prev = (struct fib_iterator *) n; } +void +fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos) +{ + if (n = n->next) + goto found; + + while (++hpos < f->hash_size) + if (n = f->hash_table[hpos]) + goto found; + + /* We are at the end */ + i->prev = i->next = NULL; + i->node = NULL; + return; + +found: + fit_put(i, n); +} + #ifdef DEBUGGING /** diff --git a/nest/rt-table.c b/nest/rt-table.c index 9e2c4e0d..b39f2a69 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -888,12 +888,6 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr } rte_free_quick(new); -#ifdef CONFIG_RIP - /* lastmod is used internally by RIP as the last time - when the route was received. */ - if (src->proto->proto == &proto_rip) - old->lastmod = now; -#endif return; } *k = old->next; diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index fb63e61c..65842037 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -223,7 +223,7 @@ ospf_rx_hook(sock *sk, int len) return 1; DBG("OSPF: RX hook called (iface %s, src %I, dst %I)\n", - sk->ifname, sk->faddr, sk->laddr); + sk->iface->name, sk->faddr, sk->laddr); /* Initially, the packet is associated with the 'master' iface */ struct ospf_iface *ifa = sk->data; diff --git a/proto/rip/Doc b/proto/rip/Doc index 2c7f4c8f..561b2153 100644 --- a/proto/rip/Doc +++ b/proto/rip/Doc @@ -1,2 +1,2 @@ S rip.c -S auth.c +S packets.c diff --git a/proto/rip/Makefile b/proto/rip/Makefile index d03e3a90..d2d3c987 100644 --- a/proto/rip/Makefile +++ b/proto/rip/Makefile @@ -1,4 +1,4 @@ -source=rip.c auth.c +source=rip.c packets.c root-rel=../../ dir-name=proto/rip diff --git a/proto/rip/auth.c b/proto/rip/auth.c deleted file mode 100644 index 5634547a..00000000 --- a/proto/rip/auth.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Rest in pieces - RIP protocol - * - * Copyright (c) 1999 Pavel Machek - * Copyright (c) 2004 Ondrej Filip - * - * Bug fixes by Eric Leblond , April 2003 - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#undef LOCAL_DEBUG - -#include "nest/bird.h" -#include "nest/iface.h" -#include "nest/protocol.h" -#include "nest/route.h" -#include "lib/socket.h" -#include "lib/resource.h" -#include "lib/lists.h" -#include "lib/timer.h" -#include "lib/md5.h" -#include "lib/string.h" - -#include "rip.h" - -#define P ((struct rip_proto *) p) -#define P_CF ((struct rip_proto_config *)p->cf) - -#define PACKETLEN(num) (num * sizeof(struct rip_block) + sizeof(struct rip_packet_heading)) - -/* - * rip_incoming_authentication - check authentication of incomming packet and return 1 if there's problem. - */ -int -rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num, ip_addr whotoldme ) -{ - DBG( "Incoming authentication: " ); - switch (ntohs(block->authtype)) { /* Authentication type */ - case AT_PLAINTEXT: - { - struct password_item *passwd = password_find(P_CF->passwords, 1); - DBG( "Plaintext passwd" ); - if (!passwd) { - log( L_AUTH "No passwords set and password authentication came" ); - return 1; - } - if (strncmp( (char *) (&block->packetlen), passwd->password, 16)) { - log( L_AUTH "Passwd authentication failed!" ); - DBG( "Expected %s, got %.16s\n", passwd->password, &block->packetlen ); - return 1; - } - } - break; - case AT_MD5: - DBG( "md5 password" ); - { - struct password_item *pass = NULL, *ptmp; - struct rip_md5_tail *tail; - struct MD5Context ctxt; - char md5sum_packet[16]; - char md5sum_computed[16]; - struct neighbor *neigh = neigh_find(p, &whotoldme, 0); - list *l = P_CF->passwords; - - if (ntohs(block->packetlen) != PACKETLEN(num) - sizeof(struct rip_md5_tail) ) { - log( L_ERR "Packet length in MD5 does not match computed value" ); - return 1; - } - - tail = (struct rip_md5_tail *) ((char *) packet + (ntohs(block->packetlen) )); - if ((tail->mustbeFFFF != 0xffff) || (tail->mustbe0001 != 0x0100)) { - log( L_ERR "MD5 tail signature is not there" ); - return 1; - } - - WALK_LIST(ptmp, *l) - { - if (block->keyid != ptmp->id) continue; - if ((ptmp->genfrom > now_real) || (ptmp->gento < now_real)) continue; - pass = ptmp; - break; - } - - if(!pass) return 1; - - if (!neigh) { - log( L_AUTH "Non-neighbour MD5 checksummed packet?" ); - } else { - if (neigh->aux > block->seq) { - log( L_AUTH "MD5 protected packet with lower numbers" ); - return 1; - } - neigh->aux = block->seq; - } - - memcpy(md5sum_packet, tail->md5, 16); - strncpy(tail->md5, pass->password, 16); - - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) packet, ntohs(block->packetlen) + sizeof(struct rip_block_auth) ); - MD5Final(md5sum_computed, &ctxt); - if (memcmp(md5sum_packet, md5sum_computed, 16)) - return 1; - } - } - - return 0; -} - -/* - * rip_outgoing_authentication - append authentication information to the packet. - * %num: number of rip_blocks already in packets. This function returns size of packet to send. - */ -int -rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num ) -{ - struct password_item *passwd = password_find(P_CF->passwords, 1); - - if (!P_CF->authtype) - return PACKETLEN(num); - - DBG( "Outgoing authentication: " ); - - if (!passwd) { - log( L_ERR "No suitable password found for authentication" ); - return PACKETLEN(num); - } - - block->authtype = htons(P_CF->authtype); - block->mustbeFFFF = 0xffff; - switch (P_CF->authtype) { - case AT_PLAINTEXT: - strncpy( (char *) (&block->packetlen), passwd->password, 16); - return PACKETLEN(num); - case AT_MD5: - { - struct rip_md5_tail *tail; - struct MD5Context ctxt; - static u32 sequence = 0; - - if (num > PACKET_MD5_MAX) - bug( "We can not add MD5 authentication to this long packet" ); - - /* need to preset the sequence number to a sane value */ - if (!sequence) - sequence = (u32) time(NULL); - - block->keyid = passwd->id; - block->authlen = sizeof(struct rip_block_auth); - block->seq = sequence++; - block->zero0 = 0; - block->zero1 = 0; - block->packetlen = htons(PACKETLEN(num)); - tail = (struct rip_md5_tail *) ((char *) packet + PACKETLEN(num) ); - tail->mustbeFFFF = 0xffff; - tail->mustbe0001 = 0x0100; - - strncpy(tail->md5, passwd->password, 16); - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) packet, PACKETLEN(num) + sizeof(struct rip_md5_tail)); - MD5Final(tail->md5, &ctxt); - return PACKETLEN(num) + block->authlen; - } - default: - bug( "Unknown authtype in outgoing authentication?" ); - } -} diff --git a/proto/rip/config.Y b/proto/rip/config.Y index b2b99095..29ea7eb1 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -1,17 +1,14 @@ /* * BIRD -- RIP Configuration * + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. + * * Can be freely distributed and used under the terms of the GNU GPL. */ -/* -To add: - -version1 switch - -*/ - - CF_HDR #include "proto/rip/rip.h" @@ -19,76 +16,141 @@ CF_HDR CF_DEFINES -#define RIP_CFG ((struct rip_proto_config *) this_proto) -#define RIP_IPATT ((struct rip_patt *) this_ipatt) +#define RIP_CFG ((struct rip_config *) this_proto) +#define RIP_IFACE ((struct rip_iface_config *) this_ipatt) + +static inline int rip_cfg_is_v2(void) { return RIP_CFG->rip2; } +static inline int rip_cfg_is_ng(void) { return ! RIP_CFG->rip2; } + +static inline void +rip_check_auth(void) +{ + if (rip_cfg_is_ng()) + cf_error("Authentication not supported in RIPng"); +} -#ifdef IPV6 -#define RIP_DEFAULT_TTL_SECURITY 2 -#else -#define RIP_DEFAULT_TTL_SECURITY 0 -#endif CF_DECLS -CF_KEYWORDS(RIP, INFINITY, METRIC, PORT, PERIOD, GARBAGE, TIMEOUT, - MODE, BROADCAST, MULTICAST, QUIET, NOLISTEN, VERSION1, - AUTHENTICATION, NONE, PLAINTEXT, MD5, TTL, SECURITY, - HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, ONLY, - RIP_METRIC, RIP_TAG) +CF_KEYWORDS(RIP, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT, + GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE, + VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD, + AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY, + RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG) -%type rip_mode rip_auth +%type rip_auth CF_GRAMMAR -CF_ADDTO(proto, rip_cfg '}' { RIP_CFG->passwords = get_passwords(); } ) +CF_ADDTO(proto, rip_proto) -rip_cfg_start: proto_start RIP { - this_proto = proto_config_new(&proto_rip, $1); - rip_init_config(RIP_CFG); - } - ; +rip_proto_start: proto_start RIP +{ + this_proto = proto_config_new(&proto_rip, $1); + init_list(&RIP_CFG->patt_list); -rip_cfg: - rip_cfg_start proto_name '{' - | rip_cfg proto_item ';' - | rip_cfg INFINITY expr ';' { RIP_CFG->infinity = $3; } - | rip_cfg PORT expr ';' { RIP_CFG->port = $3; } - | rip_cfg PERIOD expr ';' { RIP_CFG->period = $3; } - | rip_cfg GARBAGE TIME expr ';' { RIP_CFG->garbage_time = $4; } - | rip_cfg TIMEOUT TIME expr ';' { RIP_CFG->timeout_time = $4; } - | rip_cfg AUTHENTICATION rip_auth ';' {RIP_CFG->authtype = $3; } - | rip_cfg password_list ';' - | rip_cfg HONOR ALWAYS ';' { RIP_CFG->honor = HO_ALWAYS; } - | rip_cfg HONOR NEIGHBOR ';' { RIP_CFG->honor = HO_NEIGHBOR; } - | rip_cfg HONOR NEVER ';' { RIP_CFG->honor = HO_NEVER; } - | rip_cfg INTERFACE rip_iface ';' - ; + RIP_CFG->rip2 = RIP_IS_V2; + RIP_CFG->infinity = RIP_DEFAULT_INFINITY; -rip_auth: - PLAINTEXT { $$=AT_PLAINTEXT; } - | MD5 { $$=AT_MD5; } - | NONE { $$=AT_NONE; } - ; + RIP_CFG->min_timeout_time = 60; + RIP_CFG->max_garbage_time = 60; +}; +rip_proto_item: + proto_item + | ECMP bool { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; } + | ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); } + | INFINITY expr { RIP_CFG->infinity = $2; } + | INTERFACE rip_iface + ; -rip_mode: - BROADCAST { $$=IM_BROADCAST; } - | MULTICAST { $$=0; } - | QUIET { $$=IM_QUIET; } - | NOLISTEN { $$=IM_NOLISTEN; } - | VERSION1 { $$=IM_VERSION1 | IM_BROADCAST; } +rip_proto_opts: + /* empty */ + | rip_proto_opts rip_proto_item ';' ; +rip_proto: + rip_proto_start proto_name '{' rip_proto_opts '}'; + + +rip_iface_start: +{ + this_ipatt = cfg_allocz(sizeof(struct rip_iface_config)); + add_tail(&RIP_CFG->patt_list, NODE this_ipatt); + init_list(&this_ipatt->ipn_list); + reset_passwords(); + + RIP_IFACE->metric = 1; + RIP_IFACE->port = rip_cfg_is_v2() ? RIP_PORT : RIP_NG_PORT; + RIP_IFACE->version = rip_cfg_is_v2() ? RIP_V2 : RIP_V1; + RIP_IFACE->split_horizon = 1; + RIP_IFACE->poison_reverse = 1; + RIP_IFACE->check_zero = 1; + RIP_IFACE->ttl_security = rip_cfg_is_v2() ? 0 : 1; + RIP_IFACE->rx_buffer = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0; + RIP_IFACE->tx_length = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0; + RIP_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL; + RIP_IFACE->tx_priority = sk_priority_control; + RIP_IFACE->update_time = RIP_DEFAULT_UPDATE_TIME; + RIP_IFACE->timeout_time = RIP_DEFAULT_TIMEOUT_TIME; + RIP_IFACE->garbage_time = RIP_DEFAULT_GARBAGE_TIME; +}; + +rip_iface_finish: +{ + 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; + + 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); +}; + rip_iface_item: - | METRIC expr { RIP_IPATT->metric = $2; } - | MODE rip_mode { RIP_IPATT->mode |= $2; } - | TX tos { RIP_IPATT->tx_tos = $2; } - | TX PRIORITY expr { RIP_IPATT->tx_priority = $3; } - | TTL SECURITY bool { RIP_IPATT->ttl_security = $3; } - | TTL SECURITY TX ONLY { RIP_IPATT->ttl_security = 2; } + METRIC expr { RIP_IFACE->metric = $2; if (($2<1) || ($2>255)) cf_error("Metric must be in range 1-255"); } + | MODE MULTICAST { RIP_IFACE->mode = RIP_IM_MULTICAST; } + | MODE BROADCAST { RIP_IFACE->mode = RIP_IM_BROADCAST; if (rip_cfg_is_ng()) cf_error("Broadcast not supported in RIPng"); } + | PASSIVE bool { RIP_IFACE->passive = $2; } + | ADDRESS ipa { RIP_IFACE->address = $2; } + | PORT expr { RIP_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); } + | VERSION expr { RIP_IFACE->version = $2; + if (rip_cfg_is_ng()) cf_error("Version not supported in RIPng"); + if (($2 != RIP_V1) && ($2 != RIP_V2)) cf_error("Unsupported version"); + } + | VERSION ONLY bool { RIP_IFACE->version_only = $3; } + | SPLIT HORIZON bool { RIP_IFACE->split_horizon = $3; } + | POISON REVERSE bool { RIP_IFACE->poison_reverse = $3; } + | CHECK ZERO bool { RIP_IFACE->check_zero = $3; } + | UPDATE TIME expr { RIP_IFACE->update_time = $3; if ($3<=0) cf_error("Update time must be positive"); } + | TIMEOUT TIME expr { RIP_IFACE->timeout_time = $3; if ($3<=0) cf_error("Timeout time must be positive"); } + | GARBAGE TIME expr { RIP_IFACE->garbage_time = $3; if ($3<=0) cf_error("Garbage time must be positive"); } + | ECMP WEIGHT expr { RIP_IFACE->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); } + | RX BUFFER expr { RIP_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); } + | TX LENGTH expr { RIP_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); } + | TX tos { RIP_IFACE->tx_tos = $2; } + | TX PRIORITY expr { RIP_IFACE->tx_priority = $3; } + | TTL SECURITY bool { RIP_IFACE->ttl_security = $3; } + | TTL SECURITY TX ONLY { RIP_IFACE->ttl_security = 2; } + | CHECK LINK bool { RIP_IFACE->check_link = $3; } + | BFD bool { RIP_IFACE->bfd = $2; cf_check_bfd($2); } + | AUTHENTICATION rip_auth { RIP_IFACE->auth_type = $2; if ($2) rip_check_auth(); } + | password_list { rip_check_auth(); } +; + +rip_auth: + NONE { $$ = RIP_AUTH_NONE; } + | PLAINTEXT { $$ = RIP_AUTH_PLAIN; } + | CRYPTOGRAPHIC { $$ = RIP_AUTH_CRYPTO; } + | MD5 { $$ = RIP_AUTH_CRYPTO; } ; -rip_iface_opts: +rip_iface_opts: /* empty */ | rip_iface_opts rip_iface_item ';' ; @@ -98,25 +160,22 @@ rip_iface_opt_list: | '{' rip_iface_opts '}' ; -rip_iface_init: - /* EMPTY */ { - this_ipatt = cfg_allocz(sizeof(struct rip_patt)); - add_tail(&RIP_CFG->iface_list, NODE this_ipatt); - init_list(&this_ipatt->ipn_list); - RIP_IPATT->metric = 1; - RIP_IPATT->tx_tos = IP_PREC_INTERNET_CONTROL; - RIP_IPATT->tx_priority = sk_priority_control; - RIP_IPATT->ttl_security = RIP_DEFAULT_TTL_SECURITY; - } - ; +rip_iface: + rip_iface_start iface_patt_list_nopx rip_iface_opt_list rip_iface_finish; -rip_iface: /* TODO: switch to iface_patt_list_nopx */ - rip_iface_init iface_patt_list rip_iface_opt_list - ; CF_ADDTO(dynamic_attr, RIP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_METRIC); }) CF_ADDTO(dynamic_attr, RIP_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_TAG); }) +CF_CLI_HELP(SHOW RIP, ..., [[Show information about RIP protocol]]); + +CF_CLI(SHOW RIP INTERFACES, optsym opttext, [] [\"\"], [[Show information about RIP interfaces]]) +{ rip_show_interfaces(proto_get_named($4, &proto_rip), $5); }; + +CF_CLI(SHOW RIP NEIGHBORS, optsym opttext, [] [\"\"], [[Show information about RIP neighbors]]) +{ rip_show_neighbors(proto_get_named($4, &proto_rip), $5); }; + + CF_CODE CF_END diff --git a/proto/rip/packets.c b/proto/rip/packets.c new file mode 100644 index 00000000..be20734f --- /dev/null +++ b/proto/rip/packets.c @@ -0,0 +1,772 @@ +/* + * BIRD -- Routing Information Protocol (RIP) + * + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "rip.h" +#include "lib/md5.h" + + +#define RIP_CMD_REQUEST 1 /* want info */ +#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 + + +/* RIP packet header */ +struct rip_packet +{ + u8 command; + u8 version; + u16 unused; +}; + +/* RTE block for RIPv2 */ +struct rip_block_v2 +{ + u16 family; + u16 tag; + ip4_addr network; + ip4_addr netmask; + ip4_addr next_hop; + u32 metric; +}; + +/* RTE block for RIPng */ +struct rip_block_ng +{ + ip6_addr prefix; + u16 tag; + u8 pxlen; + u8 metric; +}; + +/* Authentication block for RIPv2 */ +struct rip_block_auth +{ + u16 must_be_ffff; + u16 auth_type; + char password[0]; + u16 packet_len; + u8 key_id; + u8 auth_len; + u32 seq_num; + u32 unused1; + u32 unused2; +}; + +/* Authentication tail, RFC 4822 */ +struct rip_auth_tail +{ + u16 must_be_ffff; + u16 must_be_0001; + byte auth_data[]; +}; + +/* Internal representation of RTE block data */ +struct rip_block +{ + ip_addr prefix; + int pxlen; + u32 metric; + u16 tag; + u16 no_af; + ip_addr next_hop; +}; + + +#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) +#define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0) +#define SKIP(DSC) do { err_dsc = DSC; goto skip; } while(0) + +#define LOG_PKT(msg, args...) \ + log_rl(&p->log_pkt_tbf, L_REMOTE "%s: " msg, p->p.name, args) + +#define LOG_PKT_AUTH(msg, args...) \ + log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args) + +#define LOG_RTE(msg, args...) \ + log_rl(&p->log_rte_tbf, L_REMOTE "%s: " msg, p->p.name, args) + + +static inline void * rip_tx_buffer(struct rip_iface *ifa) +{ return ifa->sk->tbuf; } + +static inline uint rip_pkt_hdrlen(struct rip_iface *ifa) +{ return sizeof(struct rip_packet) + (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); } + +static inline void +rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte) +{ + if (rip_is_v2(p)) + { + struct rip_block_v2 *block = (void *) pos; + block->family = rte->no_af ? 0 : htons(RIP_AF_IPV4); + block->tag = htons(rte->tag); + block->network = ip4_hton(ipa_to_ip4(rte->prefix)); + block->netmask = ip4_hton(ip4_mkmask(rte->pxlen)); + block->next_hop = ip4_hton(ipa_to_ip4(rte->next_hop)); + block->metric = htonl(rte->metric); + } + else /* RIPng */ + { + struct rip_block_ng *block = (void *) pos; + block->prefix = ip6_hton(ipa_to_ip6(rte->prefix)); + block->tag = htons(rte->tag); + block->pxlen = rte->pxlen; + block->metric = rte->metric; + } +} + +static inline void +rip_put_next_hop(struct rip_proto *p, byte *pos, struct rip_block *rte) +{ + struct rip_block_ng *block = (void *) pos; + block->prefix = ip6_hton(ipa_to_ip6(rte->next_hop)); + block->tag = 0; + block->pxlen = 0; + block->metric = 0xff; +} + +static inline int +rip_get_block(struct rip_proto *p, byte *pos, struct rip_block *rte) +{ + if (rip_is_v2(p)) + { + struct rip_block_v2 *block = (void *) pos; + + /* Skip blocks with strange AF, including authentication blocks */ + if (block->family != (rte->no_af ? 0 : htons(RIP_AF_IPV4))) + return 0; + + rte->prefix = ipa_from_ip4(ip4_ntoh(block->network)); + rte->pxlen = ip4_masklen(ip4_ntoh(block->netmask)); + rte->metric = ntohl(block->metric); + rte->tag = ntohs(block->tag); + rte->next_hop = ipa_from_ip4(ip4_ntoh(block->next_hop)); + + return 1; + } + else /* RIPng */ + { + struct rip_block_ng *block = (void *) pos; + + /* Handle and skip next hop blocks */ + if (block->metric == 0xff) + { + rte->next_hop = ipa_from_ip6(ip6_ntoh(block->prefix)); + if (!ipa_is_link_local(rte->next_hop)) rte->next_hop = IPA_NONE; + return 0; + } + + rte->prefix = ipa_from_ip6(ip6_ntoh(block->prefix)); + rte->pxlen = block->pxlen; + rte->metric = block->metric; + rte->tag = ntohs(block->tag); + /* rte->next_hop is deliberately kept unmodified */; + + return 1; + } +} + +static inline void +rip_update_csn(struct rip_proto *p UNUSED, struct rip_iface *ifa) +{ + /* + * We update crypto sequence numbers at the beginning of update session to + * avoid issues with packet reordering, so packets inside one update session + * have the same CSN. We are using real time, but enforcing monotonicity. + */ + if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) + ifa->csn = (ifa->csn < (u32) now_real) ? (u32) now_real : ifa->csn + 1; +} + +static void +rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen) +{ + struct rip_block_auth *auth = (void *) (pkt + 1); + struct password_item *pass = password_find(ifa->cf->passwords, 0); + + if (!pass) + { + /* FIXME: This should not happen */ + log(L_ERR "%s: No suitable password found for authentication", p->p.name); + memset(auth, 0, sizeof(struct rip_block_auth)); + return; + } + + switch (ifa->cf->auth_type) + { + case RIP_AUTH_PLAIN: + auth->must_be_ffff = htons(0xffff); + auth->auth_type = htons(RIP_AUTH_PLAIN); + strncpy(auth->password, pass->password, RIP_PASSWD_LENGTH); + return; + + case RIP_AUTH_CRYPTO: + auth->must_be_ffff = htons(0xffff); + 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->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0; + auth->unused1 = 0; + auth->unused2 = 0; + ifa->csn_ready = 1; + + /* + * Note that RFC 4822 is unclear whether auth_len should cover whole + * authentication trailer or just auth_data length. + * + * Crypto sequence numbers are increased by sender in rip_update_csn(). + * First CSN should be zero, this is handled by csn_ready. + */ + + 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; + + struct MD5Context ctxt; + MD5Init(&ctxt); + MD5Update(&ctxt, (byte *) pkt, *plen); + MD5Final(tail->auth_data, &ctxt); + return; + + default: + bug("Unknown authentication type"); + } +} + +static int +rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, struct rip_neighbor *n) +{ + struct rip_block_auth *auth = (void *) (pkt + 1); + struct password_item *pass = NULL; + const char *err_dsc = NULL; + uint err_val = 0; + uint auth_type = 0; + + /* Check for authentication entry */ + if ((*plen >= (sizeof(struct rip_packet) + sizeof(struct rip_block_auth))) && + (auth->must_be_ffff == htons(0xffff))) + auth_type = ntohs(auth->auth_type); + + if (auth_type != ifa->cf->auth_type) + DROP("authentication method mismatch", auth_type); + + switch (auth_type) + { + case RIP_AUTH_NONE: + return 1; + + case RIP_AUTH_PLAIN: + pass = password_find_by_value(ifa->cf->passwords, auth->password, RIP_PASSWD_LENGTH); + if (!pass) + DROP1("wrong password"); + + return 1; + + case RIP_AUTH_CRYPTO: + pass = password_find_by_id(ifa->cf->passwords, auth->key_id); + if (!pass) + 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; + + if (data_len + auth_len != *plen) + DROP("packet length mismatch", data_len); + + if ((auth->auth_len != RIP_MD5_LENGTH) && (auth->auth_len != auth_len)) + DROP("authentication data length mismatch", 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))) + DROP1("authentication trailer is missing"); + + /* Accept higher sequence number, or zero if connectivity is lost */ + /* FIXME: sequence number must be password/SA specific */ + u32 rcv_csn = ntohl(auth->seq_num); + if ((rcv_csn < n->csn) && (rcv_csn || n->uc)) + { + /* We want to report both new and old CSN */ + LOG_PKT_AUTH("Authentication failed for %I on %s - " + "lower sequence number (rcv %u, old %u)", + n->nbr->addr, ifa->iface->name, rcv_csn, n->csn); + return 0; + } + + char received[RIP_MD5_LENGTH]; + char computed[RIP_MD5_LENGTH]; + + memcpy(received, tail->auth_data, RIP_MD5_LENGTH); + strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); + + struct MD5Context ctxt; + MD5Init(&ctxt); + MD5Update(&ctxt, (byte *) pkt, *plen); + MD5Final(computed, &ctxt); + + if (memcmp(received, computed, RIP_MD5_LENGTH)) + DROP("wrong MD5 digest", pass->id); + + *plen = data_len; + n->csn = rcv_csn; + + return 1; + } + +drop: + LOG_PKT_AUTH("Authentication failed for %I on %s - %s (%u)", + n->nbr->addr, ifa->iface->name, err_dsc, err_val); + + return 0; +} + +static inline int +rip_send_to(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, ip_addr dst) +{ + if (ifa->cf->auth_type) + rip_fill_authentication(p, ifa, pkt, &plen); + + return sk_send_to(ifa->sk, plen, dst, 0); +} + + +void +rip_send_request(struct rip_proto *p, struct rip_iface *ifa) +{ + byte *pos = rip_tx_buffer(ifa); + + struct rip_packet *pkt = (void *) pos; + pkt->command = RIP_CMD_REQUEST; + pkt->version = ifa->cf->version; + pkt->unused = 0; + pos += rip_pkt_hdrlen(ifa); + + struct rip_block b = { .no_af = 1, .metric = p->infinity }; + rip_put_block(p, pos, &b); + pos += RIP_BLOCK_LENGTH; + + rip_update_csn(p, ifa); + + TRACE(D_PACKETS, "Sending request via %s", ifa->iface->name); + rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->addr); +} + +static void +rip_receive_request(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) +{ + TRACE(D_PACKETS, "Request received from %I on %s", from->nbr->addr, ifa->iface->name); + + byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); + + /* We expect one regular block */ + if (plen != (rip_pkt_hdrlen(ifa) + RIP_BLOCK_LENGTH)) + return; + + struct rip_block b = { .no_af = 1 }; + + if (!rip_get_block(p, pos, &b)) + return; + + /* Special case - zero prefix, infinity metric */ + if (ipa_nonzero(b.prefix) || b.pxlen || (b.metric != p->infinity)) + return; + + /* We do nothing if TX is already active */ + if (ifa->tx_active) + { + TRACE(D_EVENTS, "Skipping request from %I on %s, TX is busy", from->nbr->addr, ifa->iface->name); + return; + } + + if (!ifa->cf->passive) + rip_send_table(p, ifa, from->nbr->addr, 0); +} + + +static int +rip_send_response(struct rip_proto *p, struct rip_iface *ifa) +{ + if (! ifa->tx_active) + return 0; + + byte *pos = rip_tx_buffer(ifa); + byte *max = rip_tx_buffer(ifa) + ifa->tx_plen - + (rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH); + ip_addr last_next_hop = IPA_NONE; + int send = 0; + + struct rip_packet *pkt = (void *) pos; + pkt->command = RIP_CMD_RESPONSE; + pkt->version = ifa->cf->version; + pkt->unused = 0; + pos += rip_pkt_hdrlen(ifa); + + FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, z) + { + struct rip_entry *en = (struct rip_entry *) z; + + /* Dummy entries */ + if (!en->valid) + goto next_entry; + + /* Stale entries that should be removed */ + if ((en->valid == RIP_ENTRY_STALE) && + ((en->changed + ifa->cf->garbage_time) <= now)) + goto next_entry; + + /* Triggered updates */ + if (en->changed < ifa->tx_changed) + goto next_entry; + + /* Not enough space for current entry */ + if (pos > max) + { + FIB_ITERATE_PUT(&ifa->tx_fit, z); + goto break_loop; + } + + struct rip_block rte = { + .prefix = en->n.prefix, + .pxlen = en->n.pxlen, + .metric = en->metric, + .tag = en->tag + }; + + if (en->iface == ifa->iface) + rte.next_hop = en->next_hop; + + if (rip_is_v2(p) && (ifa->cf->version == RIP_V1)) + { + /* Skipping subnets (i.e. not hosts, classful networks or default route) */ + if (ip4_masklen(ip4_class_mask(ipa_to_ip4(en->n.prefix))) != en->n.pxlen) + goto next_entry; + + rte.tag = 0; + rte.pxlen = 0; + rte.next_hop = IPA_NONE; + } + + /* Split horizon */ + if (en->from == ifa->iface && ifa->cf->split_horizon) + { + if (ifa->cf->poison_reverse) + { + rte.metric = p->infinity; + rte.next_hop = IPA_NONE; + } + else + goto next_entry; + } + + // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); + + /* RIPng next hop entry */ + if (rip_is_ng(p) && !ipa_equal(rte.next_hop, last_next_hop)) + { + last_next_hop = rte.next_hop; + rip_put_next_hop(p, pos, &rte); + pos += RIP_BLOCK_LENGTH; + } + + rip_put_block(p, pos, &rte); + pos += RIP_BLOCK_LENGTH; + send = 1; + + next_entry: ; + } + FIB_ITERATE_END(z); + ifa->tx_active = 0; + + /* Do not send empty packet */ + if (!send) + return 0; + +break_loop: + TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name); + return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr); +} + +/** + * rip_send_table - RIP interface timer hook + * @p: RIP instance + * @ifa: RIP interface + * @addr: destination IP address + * @changed: time limit for triggered updates + * + * The function activates an update session and starts sending routing update + * packets (using rip_send_response()). The session may be finished during the + * call or may continue in rip_tx_hook() until all appropriate routes are + * transmitted. Note that there may be at most one active update session per + * interface, the function will terminate the old active session before + * activating the new one. + */ +void +rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed) +{ + DBG("RIP: Opening TX session to %I on %s\n", dst, ifa->iface->name); + + rip_reset_tx_session(p, ifa); + + ifa->tx_active = 1; + ifa->tx_addr = addr; + ifa->tx_changed = changed; + FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable); + + rip_update_csn(p, ifa); + + while (rip_send_response(p, ifa) > 0) + ; +} + +static void +rip_tx_hook(sock *sk) +{ + struct rip_iface *ifa = sk->data; + struct rip_proto *p = ifa->rip; + + DBG("RIP: TX hook called (iface %s, src %I, dst %I)\n", + sk->iface->name, sk->saddr, sk->daddr); + + while (rip_send_response(p, ifa) > 0) + ; +} + +static void +rip_err_hook(sock *sk, int err) +{ + struct rip_iface *ifa = sk->data; + struct rip_proto *p = ifa->rip; + + log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->iface->name, err); + + rip_reset_tx_session(p, ifa); +} + +static void +rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) +{ + struct rip_block rte = {}; + const char *err_dsc = NULL; + + TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name); + + byte *pos = (byte *) pkt + sizeof(struct rip_packet); + byte *end = (byte *) pkt + plen; + + for (; pos < end; pos += RIP_BLOCK_LENGTH) + { + /* Find next regular RTE */ + if (!rip_get_block(p, pos, &rte)) + continue; + + int c = ipa_classify_net(rte.prefix); + if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) + SKIP("invalid prefix"); + + if (rip_is_v2(p) && (pkt->version == RIP_V1)) + { + if (ifa->cf->check_zero && (rte.tag || rte.pxlen || ipa_nonzero(rte.next_hop))) + SKIP("RIPv1 reserved field is nonzero"); + + rte.tag = 0; + rte.pxlen = ip4_masklen(ip4_class_mask(ipa_to_ip4(rte.prefix))); + rte.next_hop = IPA_NONE; + } + + if ((rte.pxlen < 0) || (rte.pxlen > MAX_PREFIX_LENGTH)) + SKIP("invalid prefix length"); + + if (rte.metric > p->infinity) + SKIP("invalid metric"); + + if (ipa_nonzero(rte.next_hop)) + { + neighbor *nbr = neigh_find2(&p->p, &rte.next_hop, ifa->iface, 0); + if (!nbr || (nbr->scope <= 0)) + rte.next_hop = IPA_NONE; + } + + // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); + + rte.metric += ifa->cf->metric; + + if (rte.metric < p->infinity) + { + struct rip_rte new = { + .from = from, + .next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr, + .metric = rte.metric, + .tag = rte.tag, + .expires = now + ifa->cf->timeout_time + }; + + rip_update_rte(p, &rte.prefix, rte.pxlen, &new); + } + else + rip_withdraw_rte(p, &rte.prefix, rte.pxlen, from); + + continue; + + skip: + LOG_RTE("Ignoring route %I/%d received from %I - %s", + rte.prefix, rte.pxlen, from->nbr->addr, err_dsc); + } +} + +static int +rip_rx_hook(sock *sk, int len) +{ + struct rip_iface *ifa = sk->data; + struct rip_proto *p = ifa->rip; + const char *err_dsc = NULL; + uint err_val = 0; + + if (sk->lifindex != sk->iface->index) + return 1; + + DBG("RIP: RX hook called (iface %s, src %I, dst %I)\n", + sk->iface->name, sk->faddr, sk->laddr); + + /* Silently ignore my own packets */ + /* FIXME: Better local address check */ + if (ipa_equal(ifa->iface->addr->ip, sk->faddr)) + return 1; + + if (rip_is_ng(p) && !ipa_is_link_local(sk->faddr)) + DROP1("wrong src address"); + + struct rip_neighbor *n = rip_get_neighbor(p, &sk->faddr, ifa); + + if (!n) + DROP1("not from neighbor"); + + if ((ifa->cf->ttl_security == 1) && (sk->rcv_ttl < 255)) + DROP("wrong TTL", sk->rcv_ttl); + + if (sk->fport != sk->dport) + DROP("wrong src port", sk->fport); + + if (len < sizeof(struct rip_packet)) + DROP("too short", len); + + if (sk->flags & SKF_TRUNCATED) + DROP("truncated", len); + + struct rip_packet *pkt = (struct rip_packet *) sk->rbuf; + uint plen = len; + + if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version))) + DROP("wrong version", pkt->version); + + /* rip_check_authentication() has its own error logging */ + if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &plen, n)) + return 1; + + if ((plen - sizeof(struct rip_packet)) % RIP_BLOCK_LENGTH) + DROP("invalid length", plen); + + n->last_seen = now; + rip_update_bfd(p, n); + + switch (pkt->command) + { + case RIP_CMD_REQUEST: + rip_receive_request(p, ifa, pkt, plen, n); + break; + + case RIP_CMD_RESPONSE: + rip_receive_response(p, ifa, pkt, plen, n); + break; + + default: + DROP("unknown command", pkt->command); + } + return 1; + +drop: + LOG_PKT("Bad packet from %I via %s - %s (%u)", + sk->faddr, sk->iface->name, err_dsc, err_val); + + return 1; +} + +int +rip_open_socket(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + + sock *sk = sk_new(p->p.pool); + sk->type = SK_UDP; + sk->sport = ifa->cf->port; + sk->dport = ifa->cf->port; + sk->iface = ifa->iface; + + /* + * For RIPv2, we explicitly choose a primary address, mainly to ensure that + * RIP and BFD uses the same one. For RIPng, we left it to kernel, which + * should choose some link-local address based on the same scope rule. + */ + if (rip_is_v2(p)) + sk->saddr = ifa->iface->addr->ip; + + sk->rx_hook = rip_rx_hook; + sk->tx_hook = rip_tx_hook; + sk->err_hook = rip_err_hook; + sk->data = ifa; + + sk->tos = ifa->cf->tx_tos; + sk->priority = ifa->cf->tx_priority; + sk->ttl = ifa->cf->ttl_security ? 255 : 1; + sk->flags = SKF_LADDR_RX | ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0); + + /* sk->rbsize and sk->tbsize are handled in rip_iface_update_buffers() */ + + if (sk_open(sk) < 0) + goto err; + + if (ifa->cf->mode == RIP_IM_MULTICAST) + { + if (sk_setup_multicast(sk) < 0) + goto err; + + if (sk_join_group(sk, ifa->addr) < 0) + goto err; + } + else /* Broadcast */ + { + if (sk_setup_broadcast(sk) < 0) + goto err; + + if (ipa_zero(ifa->addr)) + { + sk->err = "Missing broadcast address"; + goto err; + } + } + + ifa->sk = sk; + return 1; + +err: + sk_log_error(sk, p->p.name); + rfree(sk); + return 0; +} diff --git a/proto/rip/rip.c b/proto/rip/rip.c index b77cf409..c85fd69b 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -1,1047 +1,1275 @@ /* - * Rest in pieces - RIP protocol + * BIRD -- Routing Information Protocol (RIP) * - * Copyright (c) 1998, 1999 Pavel Machek - * 2004 Ondrej Filip + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. - * - FIXME: IPv6 support: packet size - FIXME: (nonurgent) IPv6 support: receive "route using" blocks - FIXME: (nonurgent) IPv6 support: generate "nexthop" blocks - next hops are only advisory, and they are pretty ugly in IPv6. - I suggest just forgetting about them. - - FIXME: (nonurgent): fold rip_connection into rip_interface? - - FIXME: propagation of metric=infinity into main routing table may or may not be good idea. */ /** - * DOC: Routing Information Protocol + * DOC: Routing Information Protocol (RIP) + * + * The RIP protocol is implemented in two files: |rip.c| containing the protocol + * logic, route management and the protocol glue with BIRD core, and |packets.c| + * handling RIP packet processing, RX, TX and protocol sockets. + * + * Each instance of RIP is described by a structure &rip_proto, which contains + * an internal RIP routing table, a list of protocol interfaces and the main + * timer responsible for RIP routing table cleanup. + * + * RIP internal routing table contains incoming and outgoing routes. For each + * network (represented by structure &rip_entry) there is one outgoing route + * stored directly in &rip_entry and an one-way linked list of incoming routes + * (structures &rip_rte). The list contains incoming routes from different RIP + * neighbors, but only routes with the lowest metric are stored (i.e., all + * stored incoming routes have the same metric). + * + * Note that RIP itself does not select outgoing route, that is done by the core + * routing table. When a new incoming route is received, it is propagated to the + * RIP table by rip_update_rte() and possibly stored in the list of incoming + * routes. Then the change may be propagated to the core by rip_announce_rte(). + * The core selects the best route and propagate it to RIP by rip_rt_notify(), + * which updates outgoing route part of &rip_entry and possibly triggers route + * propagation by rip_trigger_update(). * - * RIP is a pretty simple protocol, so about a half of its code is interface - * with the core. + * RIP interfaces are represented by structures &rip_iface. A RIP interface + * contains a per-interface socket, a list of associated neighbors, interface + * configuration, and state information related to scheduled interface events + * and running update sessions. RIP interfaces are added and removed based on + * core interface notifications. * - * We maintain our own linked list of &rip_entry structures -- it serves - * as our small routing table. RIP never adds to this linked list upon - * packet reception; instead, it lets the core know about data from the packet - * and waits for the core to call rip_rt_notify(). + * There are two RIP interface events - regular updates and triggered updates. + * Both are managed from the RIP interface timer (rip_iface_timer()). Regular + * updates are called at fixed interval and propagate the whole routing table, + * while triggered updates are scheduled by rip_trigger_update() due to some + * routing table change and propagate only the routes modified since the time + * they were scheduled. There are also unicast-destined requested updates, but + * these are sent directly as a reaction to received RIP request message. The + * update session is started by rip_send_table(). There may be at most one + * active update session per interface, as the associated state (including the + * fib iterator) is stored directly in &rip_iface structure. * - * Within rip_tx(), the list is - * walked and a packet is generated using rip_tx_prepare(). This gets - * tricky because we may need to send more than one packet to one - * destination. Struct &rip_connection is used to hold context information such as how - * many of &rip_entry's we have already sent and it's also used to protect - * against two concurrent sends to one destination. Each &rip_interface has - * at most one &rip_connection. + * RIP neighbors are represented by structures &rip_neighbor. Compared to + * neighbor handling in other routing protocols, RIP does not have explicit + * neighbor discovery and adjacency maintenance, which makes the &rip_neighbor + * related code a bit peculiar. RIP neighbors are interlinked with core neighbor + * structures (&neighbor) and use core neighbor notifications to ensure that RIP + * neighbors are timely removed. RIP neighbors are added based on received route + * notifications and removed based on core neighbor and RIP interface events. * - * We are not going to honor requests for sending part of - * routing table. That would need to turn split horizon off etc. + * RIP neighbors are linked by RIP routes and use counter to track the number of + * associated routes, but when these RIP routes timeout, associated RIP neighbor + * is still alive (with zero counter). When RIP neighbor is removed but still + * has some associated routes, it is not freed, just changed to detached state + * (core neighbors and RIP ifaces are unlinked), then during the main timer + * cleanup phase the associated routes are removed and the &rip_neighbor + * structure is finally freed. * - * About triggered updates, RFC says: when a triggered update was sent, - * don't send a new one for something between 1 and 5 seconds (and send one - * after that). We do something else: each 5 seconds, - * we look for any changed routes and broadcast them. + * Supported standards: + * - RFC 1058 - RIPv1 + * - RFC 2453 - RIPv2 + * - RFC 2080 - RIPng + * - RFC 4822 - RIP cryptographic authentication */ -#undef LOCAL_DEBUG -#define LOCAL_DEBUG 1 - -#include "nest/bird.h" -#include "nest/iface.h" -#include "nest/protocol.h" -#include "nest/route.h" -#include "lib/socket.h" -#include "lib/resource.h" -#include "lib/lists.h" -#include "lib/timer.h" -#include "lib/string.h" - +#include #include "rip.h" -#define P ((struct rip_proto *) p) -#define P_CF ((struct rip_proto_config *)p->cf) -#undef TRACE -#define TRACE(level, msg, args...) do { if (p->debug & level) { log(L_TRACE "%s: " msg, p->name , ## args); } } while(0) +static inline void rip_lock_neighbor(struct rip_neighbor *n); +static inline void rip_unlock_neighbor(struct rip_neighbor *n); +static inline int rip_iface_link_up(struct rip_iface *ifa); +static inline void rip_kick_timer(struct rip_proto *p); +static inline void rip_iface_kick_timer(struct rip_iface *ifa); +static void rip_iface_timer(timer *timer); +static void rip_trigger_update(struct rip_proto *p); -static struct rip_interface *new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt); /* - * Output processing - * - * This part is responsible for getting packets out to the network. + * RIP routes */ static void -rip_tx_err( sock *s, int err ) +rip_init_entry(struct fib_node *fn) { - struct rip_connection *c = ((struct rip_interface *)(s->data))->busy; - struct proto *p = c->proto; - log( L_ERR "%s: Unexpected error at rip transmit: %M", p->name, err ); + // struct rip_entry *en = (void) *fn; + + const uint offset = OFFSETOF(struct rip_entry, routes); + memset((byte *)fn + offset, 0, sizeof(struct rip_entry) - offset); } -/* - * rip_tx_prepare: - * @e: rip entry that needs to be translated to form suitable for network - * @b: block to be filled - * - * Fill one rip block with info that needs to go to the network. Handle - * nexthop and split horizont correctly. (Next hop is ignored for IPv6, - * that could be fixed but it is not real problem). - */ -static int -rip_tx_prepare(struct proto *p, struct rip_block *b, struct rip_entry *e, struct rip_interface *rif, int pos ) +static struct rip_rte * +rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src) { - int metric; - DBG( "." ); - b->tag = htons( e->tag ); - b->network = e->n.prefix; - metric = e->metric; - if (neigh_connected_to(p, &e->whotoldme, rif->iface)) { - DBG( "(split horizon)" ); - metric = P_CF->infinity; - } -#ifndef IPV6 - b->family = htons( 2 ); /* AF_INET */ - b->netmask = ipa_mkmask( e->n.pxlen ); - ipa_hton( b->netmask ); + struct rip_rte *rt = sl_alloc(p->rte_slab); - if (neigh_connected_to(p, &e->nexthop, rif->iface)) - b->nexthop = e->nexthop; - else - b->nexthop = IPA_NONE; - ipa_hton( b->nexthop ); - b->metric = htonl( metric ); -#else - b->pxlen = e->n.pxlen; - b->metric = metric; /* it is u8 */ -#endif + memcpy(rt, src, sizeof(struct rip_rte)); + rt->next = *rp; + *rp = rt; - ipa_hton( b->network ); + rip_lock_neighbor(rt->from); - return pos+1; + return rt; } -/* - * rip_tx - send one rip packet to the network +static inline void +rip_remove_rte(struct rip_proto *p, struct rip_rte **rp) +{ + struct rip_rte *rt = *rp; + + rip_unlock_neighbor(rt->from); + + *rp = rt->next; + sl_free(p->rte_slab, rt); +} + +static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b) +{ return a->metric == b->metric && a->tag == b->tag && ipa_equal(a->next_hop, b->next_hop); } + +static inline int rip_valid_rte(struct rip_rte *rt) +{ return rt->from->ifa != NULL; } + +/** + * rip_announce_rte - announce route from RIP routing table to the core + * @p: RIP instance + * @en: related network + * + * The function takes a list of incoming routes from @en, prepare appropriate + * &rte for the core and propagate it by rte_update(). */ static void -rip_tx( sock *s ) +rip_announce_rte(struct rip_proto *p, struct rip_entry *en) { - struct rip_interface *rif = s->data; - struct rip_connection *c = rif->busy; - struct proto *p = c->proto; - struct rip_packet *packet = (void *) s->tbuf; - int i, packetlen; - int maxi, nullupdate = 1; - - DBG( "Sending to %I\n", s->daddr ); - do { - - if (c->done) - goto done; - - DBG( "Preparing packet to send: " ); - - packet->heading.command = RIPCMD_RESPONSE; -#ifndef IPV6 - packet->heading.version = RIP_V2; -#else - packet->heading.version = RIP_NG; -#endif - packet->heading.unused = 0; - - i = !!P_CF->authtype; -#ifndef IPV6 - maxi = ((P_CF->authtype == AT_MD5) ? PACKET_MD5_MAX : PACKET_MAX); -#else - maxi = 5; /* We need to have at least reserve of one at end of packet */ -#endif - - FIB_ITERATE_START(&P->rtable, &c->iter, z) { - struct rip_entry *e = (struct rip_entry *) z; - - if (!rif->triggered || (!(e->updated < now-2))) { /* FIXME: Should be probably 1 or some different algorithm */ - nullupdate = 0; - i = rip_tx_prepare( p, packet->block + i, e, rif, i ); - if (i >= maxi) { - FIB_ITERATE_PUT(&c->iter, z); - goto break_loop; - } - } - } FIB_ITERATE_END(z); - c->done = 1; + struct rip_rte *rt = en->routes; + + /* Find first valid rte */ + while (rt && !rip_valid_rte(rt)) + rt = rt->next; + + if (rt) + { + /* Update */ + net *n = net_get(p->p.table, en->n.prefix, en->n.pxlen); - break_loop: + rta a0 = { + .src = p->p.main_source, + .source = RTS_RIP, + .scope = SCOPE_UNIVERSE, + .cast = RTC_UNICAST + }; - packetlen = rip_outgoing_authentication(p, (void *) &packet->block[0], packet, i); + u8 rt_metric = rt->metric; + u16 rt_tag = rt->tag; + struct rip_rte *rt2 = rt->next; + + /* Find second valid rte */ + while (rt2 && !rip_valid_rte(rt2)) + rt2 = rt2->next; + + if (p->ecmp && rt2) + { + /* ECMP route */ + struct mpnh *nhs = NULL; + struct mpnh **nhp = &nhs; + int num = 0; - DBG( ", sending %d blocks, ", i ); - if (nullupdate) { - DBG( "not sending NULL update\n" ); - c->done = 1; - goto done; + for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next) + { + if (!rip_valid_rte(rt)) + continue; + + struct mpnh *nh = alloca(sizeof(struct mpnh)); + nh->gw = rt->next_hop; + nh->iface = rt->from->nbr->iface; + nh->weight = rt->from->ifa->cf->ecmp_weight; + nh->next = NULL; + *nhp = nh; + nhp = &(nh->next); + num++; + + if (rt->tag != rt_tag) + rt_tag = 0; + } + + a0.dest = RTD_MULTIPATH; + a0.nexthops = nhs; } - if (ipa_nonzero(c->daddr)) - i = sk_send_to( s, packetlen, c->daddr, c->dport ); else - i = sk_send( s, packetlen ); - - DBG( "it wants more\n" ); - - } while (i>0); - - if (i<0) rip_tx_err( s, i ); - DBG( "blocked\n" ); - return; - -done: - DBG( "Looks like I'm" ); - c->rif->busy = NULL; - rem_node(NODE c); - mb_free(c); - DBG( " done\n" ); - return; -} + { + /* Unipath route */ + a0.dest = RTD_ROUTER; + a0.gw = rt->next_hop; + a0.iface = rt->from->nbr->iface; + a0.from = rt->from->nbr->addr; + } -/* - * rip_sendto - send whole routing table to selected destination - * @rif: interface to use. Notice that we lock interface so that at - * most one send to one interface is done. - */ -static void -rip_sendto( struct proto *p, ip_addr daddr, int dport, struct rip_interface *rif ) -{ - struct iface *iface = rif->iface; - struct rip_connection *c; - static int num = 0; + rta *a = rta_lookup(&a0); + rte *e = rte_get_temp(a); - if (rif->busy) { - log (L_WARN "%s: Interface %s is much too slow, dropping request", p->name, iface->name); - return; + e->u.rip.from = a0.iface; + e->u.rip.metric = rt_metric; + e->u.rip.tag = rt_tag; + + e->net = n; + e->pflags = 0; + + rte_update(&p->p, n, e); } - c = mb_alloc( p->pool, sizeof( struct rip_connection )); - rif->busy = c; - - c->addr = daddr; - c->proto = p; - c->num = num++; - c->rif = rif; - - c->dport = dport; - c->daddr = daddr; - if (c->rif->sock->data != rif) - bug("not enough send magic"); - - c->done = 0; - FIB_ITERATE_INIT( &c->iter, &P->rtable ); - add_head( &P->connections, NODE c ); - if (ipa_nonzero(daddr)) - TRACE(D_PACKETS, "Sending my routing table to %I:%d on %s", daddr, dport, rif->iface->name ); else - TRACE(D_PACKETS, "Broadcasting routing table to %s", rif->iface->name ); - - rip_tx(c->rif->sock); + { + /* Withdraw */ + net *n = net_find(p->p.table, en->n.prefix, en->n.pxlen); + rte_update(&p->p, n, NULL); + } } -static struct rip_interface* -find_interface(struct proto *p, struct iface *what) +/** + * rip_update_rte - enter a route update to RIP routing table + * @p: RIP instance + * @prefix: network prefix + * @pxlen: network prefix length + * @new: a &rip_rte representing the new route + * + * The function is called by the RIP packet processing code whenever it receives + * a reachable route. The appropriate routing table entry is found and the list + * of incoming routes is updated. Eventually, the change is also propagated to + * the core by rip_announce_rte(). Note that for unreachable routes, + * rip_withdraw_rte() should be called instead of rip_update_rte(). + */ +void +rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new) { - struct rip_interface *i; + struct rip_entry *en = fib_get(&p->rtable, prefix, pxlen); + struct rip_rte *rt, **rp; + int changed = 0; + + /* If the new route is better, remove all current routes */ + if (en->routes && new->metric < en->routes->metric) + while (en->routes) + rip_remove_rte(p, &en->routes); + + /* Find the old route (also set rp for later) */ + for (rp = &en->routes; rt = *rp; rp = &rt->next) + if (rt->from == new->from) + { + if (rip_same_rte(rt, new)) + { + rt->expires = new->expires; + return; + } - WALK_LIST (i, P->interfaces) - if (i->iface == what) - return i; - return NULL; + /* Remove the old route */ + rip_remove_rte(p, rp); + changed = 1; + break; + } + + /* If the new route is optimal, add it to the list */ + if (!en->routes || new->metric == en->routes->metric) + { + rt = rip_add_rte(p, rp, new); + changed = 1; + } + + /* Announce change if on relevant position (the first or any for ECMP) */ + if (changed && (rp == &en->routes || p->ecmp)) + rip_announce_rte(p, en); } -/* - * Input processing +/** + * rip_withdraw_rte - enter a route withdraw to RIP routing table + * @p: RIP instance + * @prefix: network prefix + * @pxlen: network prefix length + * @from: a &rip_neighbor propagating the withdraw * - * This part is responsible for any updates that come from network + * The function is called by the RIP packet processing code whenever it receives + * an unreachable route. The incoming route for given network from nbr @from is + * removed. Eventually, the change is also propagated by rip_announce_rte(). */ +void +rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from) +{ + struct rip_entry *en = fib_find(&p->rtable, prefix, pxlen); + struct rip_rte *rt, **rp; -static int rip_rte_better(struct rte *new, struct rte *old); + if (!en) + return; -static void -rip_rte_update_if_better(rtable *tab, net *net, struct proto *p, rte *new) -{ - rte *old; + /* Find the old route */ + for (rp = &en->routes; rt = *rp; rp = &rt->next) + if (rt->from == from) + break; - old = rte_find(net, p->main_source); - if (!old || rip_rte_better(new, old) || - (ipa_equal(old->attrs->from, new->attrs->from) && - (old->u.rip.metric != new->u.rip.metric)) ) - rte_update(p, net, new); - else - rte_free(new); + if (!rt) + return; + + /* Remove the old route */ + rip_remove_rte(p, rp); + + /* Announce change if on relevant position */ + if (rp == &en->routes || p->ecmp) + rip_announce_rte(p, en); } /* - * advertise_entry - let main routing table know about our new entry - * @b: entry in network format - * - * This basically translates @b to format used by bird core and feeds - * bird core with this route. + * rip_rt_notify - core tells us about new route, so store + * it into our data structures. */ static void -advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme, struct iface *iface ) +rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, struct rte *new, + struct rte *old UNUSED, struct ea_list *attrs) { - rta *a, A; - rte *r; - net *n; - neighbor *neighbor; - struct rip_interface *rif; - int pxlen; - - bzero(&A, sizeof(A)); - A.src= p->main_source; - A.source = RTS_RIP; - A.scope = SCOPE_UNIVERSE; - A.cast = RTC_UNICAST; - A.dest = RTD_ROUTER; - A.flags = 0; -#ifndef IPV6 - A.gw = ipa_nonzero(b->nexthop) ? b->nexthop : whotoldme; - pxlen = ipa_masklen(b->netmask); -#else - /* FIXME: next hop is in other packet for v6 */ - A.gw = whotoldme; - pxlen = b->pxlen; -#endif - A.from = whotoldme; - - /* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */ - - neighbor = neigh_find2( p, &A.gw, iface, 0 ); - if (!neighbor) { - log( L_REMOTE "%s: %I asked me to route %I/%d using not-neighbor %I.", p->name, A.from, b->network, pxlen, A.gw ); - return; - } - if (neighbor->scope == SCOPE_HOST) { - DBG("Self-destined route, ignoring.\n"); - return; + struct rip_proto *p = (struct rip_proto *) P; + struct rip_entry *en; + int old_metric; + + if (new) + { + /* Update */ + u32 rt_metric = ea_get_int(attrs, EA_RIP_METRIC, 1); + u32 rt_tag = ea_get_int(attrs, EA_RIP_TAG, 0); + + if (rt_metric > p->infinity) + { + log(L_WARN "%s: Invalid rip_metric value %u for route %I/%d", + p->p.name, rt_metric, net->n.prefix, net->n.pxlen); + rt_metric = p->infinity; + } + + if (rt_tag > 0xffff) + { + log(L_WARN "%s: Invalid rip_tag value %u for route %I/%d", + p->p.name, rt_tag, net->n.prefix, net->n.pxlen); + rt_metric = p->infinity; + rt_tag = 0; + } + + /* + * Note that we accept exported routes with infinity metric (this could + * happen if rip_metric is modified in filters). Such entry has infinity + * metric but is RIP_ENTRY_VALID and therefore is not subject to garbage + * collection. + */ + + en = fib_get(&p->rtable, &net->n.prefix, net->n.pxlen); + + old_metric = en->valid ? en->metric : -1; + + en->valid = RIP_ENTRY_VALID; + en->metric = rt_metric; + en->tag = rt_tag; + en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL; + en->iface = new->attrs->iface; + en->next_hop = new->attrs->gw; } + else + { + /* Withdraw */ + en = fib_find(&p->rtable, &net->n.prefix, net->n.pxlen); - A.iface = neighbor->iface; - if (!(rif = neighbor->data)) { - rif = neighbor->data = find_interface(p, A.iface); + if (!en || en->valid != RIP_ENTRY_VALID) + return; + + old_metric = en->metric; + + en->valid = RIP_ENTRY_STALE; + en->metric = p->infinity; + en->tag = 0; + en->from = NULL; + en->iface = NULL; + en->next_hop = IPA_NONE; } - if (!rif) - bug("Route packet using unknown interface? No."); - - /* set to: interface of nexthop */ - a = rta_lookup(&A); - if (pxlen==-1) { - log( L_REMOTE "%s: %I gave me invalid pxlen/netmask for %I.", p->name, A.from, b->network ); - return; + + /* Activate triggered updates */ + if (en->metric != old_metric) + { + en->changed = now; + rip_trigger_update(p); } - n = net_get( p->table, b->network, pxlen ); - r = rte_get_temp(a); -#ifndef IPV6 - r->u.rip.metric = ntohl(b->metric) + rif->metric; -#else - r->u.rip.metric = b->metric + rif->metric; -#endif - - r->u.rip.entry = NULL; - if (r->u.rip.metric > P_CF->infinity) r->u.rip.metric = P_CF->infinity; - r->u.rip.tag = ntohl(b->tag); - r->net = n; - r->pflags = 0; /* Here go my flags */ - rip_rte_update_if_better( p->table, n, p, r ); - DBG( "done\n" ); } + /* - * process_block - do some basic check and pass block to advertise_entry + * RIP neighbors */ + +struct rip_neighbor * +rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa) +{ + neighbor *nbr = neigh_find2(&p->p, a, ifa->iface, 0); + + if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa)) + return NULL; + + if (nbr->data) + return nbr->data; + + TRACE(D_EVENTS, "New neighbor %I on %s", *a, ifa->iface->name); + + struct rip_neighbor *n = mb_allocz(p->p.pool, sizeof(struct rip_neighbor)); + n->ifa = ifa; + n->nbr = nbr; + nbr->data = n; + n->csn = nbr->aux; + + add_tail(&ifa->neigh_list, NODE n); + + return n; +} + static void -process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme, struct iface *iface ) +rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n) { - int metric, pxlen; - -#ifndef IPV6 - metric = ntohl( block->metric ); - pxlen = ipa_masklen(block->netmask); -#else - metric = block->metric; - pxlen = block->pxlen; -#endif - ip_addr network = block->network; - - CHK_MAGIC; - - TRACE(D_ROUTES, "block: %I tells me: %I/%d available, metric %d... ", - whotoldme, network, pxlen, metric ); - - if ((!metric) || (metric > P_CF->infinity)) { -#ifdef IPV6 /* Someone is sending us nexthop and we are ignoring it */ - if (metric == 0xff) - { DBG( "IPv6 nexthop ignored" ); return; } -#endif - log( L_WARN "%s: Got metric %d from %I", p->name, metric, whotoldme ); - return; - } + neighbor *nbr = n->nbr; + + TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->iface->name); - advertise_entry( p, block, whotoldme, iface ); + rem_node(NODE n); + n->ifa = NULL; + n->nbr = NULL; + nbr->data = NULL; + nbr->aux = n->csn; + + rfree(n->bfd_req); + n->bfd_req = NULL; + n->last_seen = 0; + + if (!n->uc) + mb_free(n); + + /* Related routes are removed in rip_timer() */ + rip_kick_timer(p); } -#define BAD( x ) { log( L_REMOTE "%s: " x, p->name ); return 1; } +static inline void +rip_lock_neighbor(struct rip_neighbor *n) +{ + n->uc++; +} -/* - * rip_process_packet - this is main routine for incoming packets. - */ -static int -rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr whotoldme, int port, struct iface *iface ) +static inline void +rip_unlock_neighbor(struct rip_neighbor *n) { - int i; - int authenticated = 0; - neighbor *neighbor; + n->uc--; + + if (!n->nbr && !n->uc) + mb_free(n); +} + +static void +rip_neigh_notify(struct neighbor *nbr) +{ + struct rip_proto *p = (struct rip_proto *) nbr->proto; + struct rip_neighbor *n = nbr->data; + + if (!n) + return; + + /* + * We assume that rip_neigh_notify() is called before rip_if_notify() for + * IF_CHANGE_DOWN and therefore n->ifa is still valid. We have no such + * ordering assumption for IF_CHANGE_LINK, so we test link state of the + * underlying iface instead of just rip_iface state. + */ + if ((nbr->scope <= 0) || !rip_iface_link_up(n->ifa)) + rip_remove_neighbor(p, n); +} + +static void +rip_bfd_notify(struct bfd_request *req) +{ + struct rip_neighbor *n = req->data; + struct rip_proto *p = n->ifa->rip; - switch( packet->heading.version ) { - case RIP_V1: DBG( "Rip1: " ); break; - case RIP_V2: DBG( "Rip2: " ); break; - default: BAD( "Unknown version" ); + if (req->down) + { + TRACE(D_EVENTS, "BFD session down for nbr %I on %s", + n->nbr->addr, n->ifa->iface->name); + rip_remove_neighbor(p, n); } +} + +void +rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n) +{ + int use_bfd = n->ifa->cf->bfd && n->last_seen; - switch( packet->heading.command ) { - case RIPCMD_REQUEST: DBG( "Asked to send my routing table\n" ); - if (P_CF->honor == HO_NEVER) - BAD( "They asked me to send routing table, but I was told not to do it" ); - - if ((P_CF->honor == HO_NEIGHBOR) && (!neigh_find2( p, &whotoldme, iface, 0 ))) - BAD( "They asked me to send routing table, but he is not my neighbor" ); - rip_sendto( p, whotoldme, port, HEAD(P->interfaces) ); /* no broadcast */ - break; - case RIPCMD_RESPONSE: DBG( "*** Rtable from %I\n", whotoldme ); - if (port != P_CF->port) { - log( L_REMOTE "%s: %I send me routing info from port %d", p->name, whotoldme, port ); - return 1; - } - - if (!(neighbor = neigh_find2( p, &whotoldme, iface, 0 )) || neighbor->scope == SCOPE_HOST) { - log( L_REMOTE "%s: %I send me routing info but he is not my neighbor", p->name, whotoldme ); - return 0; - } - - for (i=0; iblock[i]; -#ifndef IPV6 - /* Authentication is not defined for v6 */ - if (block->family == 0xffff) { - if (i) - continue; /* md5 tail has this family */ - if (rip_incoming_authentication(p, (void *) block, packet, num, whotoldme)) - BAD( "Authentication failed" ); - authenticated = 1; - continue; - } -#endif - if ((!authenticated) && (P_CF->authtype != AT_NONE)) - BAD( "Packet is not authenticated and it should be" ); - ipa_ntoh( block->network ); -#ifndef IPV6 - ipa_ntoh( block->netmask ); - ipa_ntoh( block->nexthop ); - if (packet->heading.version == RIP_V1) /* FIXME (nonurgent): switch to disable this? */ - block->netmask = ip4_class_mask(ipa_to_ip4(block->network)); -#endif - process_block( p, block, whotoldme, iface ); - } - break; - case RIPCMD_TRACEON: - case RIPCMD_TRACEOFF: BAD( "I was asked for traceon/traceoff" ); - case 5: BAD( "Some Sun extension around here" ); - default: BAD( "Unknown command" ); + if (use_bfd && !n->bfd_req) + { + /* + * For RIPv2, use the same address as rip_open_socket(). For RIPng, neighbor + * should contain an address from the same prefix, thus also link-local. It + * may cause problems if two link-local addresses are assigned to one iface. + */ + ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip; + n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr, + n->nbr->iface, rip_bfd_notify, n); } - return 0; + if (!use_bfd && n->bfd_req) + { + rfree(n->bfd_req); + n->bfd_req = NULL; + } } + /* - * rip_rx - Receive hook: do basic checks and pass packet to rip_process_packet + * RIP interfaces */ -static int -rip_rx(sock *s, int size) + +static void +rip_iface_start(struct rip_iface *ifa) { - struct rip_interface *i = s->data; - struct proto *p = i->proto; - struct iface *iface = NULL; - int num; + struct rip_proto *p = ifa->rip; - /* In non-listening mode, just ignore packet */ - if (i->mode & IM_NOLISTEN) - return 1; + TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name); -#ifdef IPV6 - if (! i->iface || s->lifindex != i->iface->index) - return 1; + ifa->next_regular = now + (random() % ifa->cf->update_time) + 1; + ifa->next_triggered = now; /* Available immediately */ + ifa->want_triggered = 1; /* All routes in triggered update */ + tm_start(ifa->timer, 1); /* Or 100 ms */ + ifa->up = 1; - iface = i->iface; -#endif + if (!ifa->cf->passive) + rip_send_request(ifa->rip, ifa); +} - if (i->check_ttl && (s->rcv_ttl < 255)) - { - log( L_REMOTE "%s: Discarding packet with TTL %d (< 255) from %I on %s", - p->name, s->rcv_ttl, s->faddr, i->iface->name); - return 1; - } +static void +rip_iface_stop(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + struct rip_neighbor *n; + TRACE(D_EVENTS, "Stopping interface %s", ifa->iface->name); - CHK_MAGIC; - DBG( "RIP: message came: %d bytes from %I via %s\n", size, s->faddr, i->iface ? i->iface->name : "(dummy)" ); - size -= sizeof( struct rip_packet_heading ); - if (size < 0) BAD( "Too small packet" ); - if (size % sizeof( struct rip_block )) BAD( "Odd sized packet" ); - num = size / sizeof( struct rip_block ); - if (num>PACKET_MAX) BAD( "Too many blocks" ); + rip_reset_tx_session(p, ifa); - if (ipa_equal(i->iface->addr->ip, s->faddr)) { - DBG("My own packet\n"); - return 1; - } + WALK_LIST_FIRST(n, ifa->neigh_list) + rip_remove_neighbor(p, n); - rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport, iface ); - return 1; + tm_stop(ifa->timer); + ifa->up = 0; } -/* - * Interface to BIRD core - */ +static inline int +rip_iface_link_up(struct rip_iface *ifa) +{ + return !ifa->cf->check_link || (ifa->iface->flags & IF_LINK_UP); +} static void -rip_dump_entry( struct rip_entry *e ) +rip_iface_update_state(struct rip_iface *ifa) { - debug( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ", - e->whotoldme, e->updated-now, e->changed-now, e->n.prefix, e->n.pxlen, e->nexthop, e->metric ); - debug( "\n" ); -} + int up = ifa->sk && rip_iface_link_up(ifa); -/** - * rip_timer - * @t: timer - * - * Broadcast routing tables periodically (using rip_tx) and kill - * routes that are too old. RIP keeps a list of its own entries present - * in the core table by a linked list (functions rip_rte_insert() and - * rip_rte_delete() are responsible for that), it walks this list in the timer - * and in case an entry is too old, it is discarded. - */ + if (up == ifa->up) + return; + + if (up) + rip_iface_start(ifa); + else + rip_iface_stop(ifa); +} static void -rip_timer(timer *t) +rip_iface_update_buffers(struct rip_iface *ifa) { - struct proto *p = t->data; - struct fib_node *e, *et; + if (!ifa->sk) + return; - CHK_MAGIC; - DBG( "RIP: tick tock\n" ); - - WALK_LIST_DELSAFE( e, et, P->garbage ) { - rte *rte; - rte = SKIP_BACK( struct rte, u.rip.garbage, e ); + uint rbsize = ifa->cf->rx_buffer ?: ifa->iface->mtu; + uint tbsize = ifa->cf->tx_length ?: ifa->iface->mtu; + rbsize = MAX(rbsize, tbsize); - CHK_MAGIC; + sk_set_rbsize(ifa->sk, rbsize); + sk_set_tbsize(ifa->sk, tbsize); - DBG( "Garbage: (%p)", rte ); rte_dump( rte ); + uint headers = (rip_is_v2(ifa->rip) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH) + UDP_HEADER_LENGTH; + ifa->tx_plen = tbsize - headers; - if (now - rte->lastmod > P_CF->timeout_time) { - TRACE(D_EVENTS, "entry is too old: %I", rte->net->n.prefix ); - if (rte->u.rip.entry) { - rte->u.rip.entry->metric = P_CF->infinity; - rte->u.rip.metric = P_CF->infinity; - } - } + if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) + ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH; +} - if (now - rte->lastmod > P_CF->garbage_time) { - TRACE(D_EVENTS, "entry is much too old: %I", rte->net->n.prefix ); - rte_discard(p->table, rte); - } - } +static inline void +rip_iface_update_bfd(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + struct rip_neighbor *n; + + WALK_LIST(n, ifa->neigh_list) + rip_update_bfd(p, n); +} + + +static void +rip_iface_locked(struct object_lock *lock) +{ + struct rip_iface *ifa = lock->data; + struct rip_proto *p = ifa->rip; - DBG( "RIP: Broadcasting routing tables\n" ); + if (!rip_open_socket(ifa)) { - struct rip_interface *rif; + log(L_ERR "%s: Cannot open socket for %s", p->p.name, ifa->iface->name); + return; + } - if ( P_CF->period > 2 ) { /* Bring some randomness into sending times */ - if (! (P->tx_count % P_CF->period)) P->rnd_count = random_u32() % 2; - } else P->rnd_count = P->tx_count % P_CF->period; + rip_iface_update_buffers(ifa); + rip_iface_update_state(ifa); +} - WALK_LIST( rif, P->interfaces ) { - struct iface *iface = rif->iface; - if (!iface) continue; - if (rif->mode & IM_QUIET) continue; - if (!(iface->flags & IF_UP)) continue; - rif->triggered = P->rnd_count; +static struct rip_iface * +rip_find_iface(struct rip_proto *p, struct iface *what) +{ + struct rip_iface *ifa; - rip_sendto( p, IPA_NONE, 0, rif ); - } - P->tx_count++; - P->rnd_count--; - } + WALK_LIST(ifa, p->iface_list) + if (ifa->iface == what) + return ifa; - DBG( "RIP: tick tock done\n" ); + return NULL; } -/* - * rip_start - initialize instance of rip - */ -static int -rip_start(struct proto *p) +static void +rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config *ic) { - struct rip_interface *rif; - DBG( "RIP: starting instance...\n" ); - - ASSERT(sizeof(struct rip_packet_heading) == 4); - ASSERT(sizeof(struct rip_block) == 20); - ASSERT(sizeof(struct rip_block_auth) == 20); - -#ifdef LOCAL_DEBUG - P->magic = RIP_MAGIC; -#endif - fib_init( &P->rtable, p->pool, sizeof( struct rip_entry ), 0, NULL ); - init_list( &P->connections ); - init_list( &P->garbage ); - init_list( &P->interfaces ); - P->timer = tm_new( p->pool ); - P->timer->data = p; - P->timer->recurrent = 1; - P->timer->hook = rip_timer; - tm_start( P->timer, 2 ); - rif = new_iface(p, NULL, 0, NULL); /* Initialize dummy interface */ - add_head( &P->interfaces, NODE rif ); - CHK_MAGIC; - - DBG( "RIP: ...done\n"); - return PS_UP; + struct rip_iface *ifa; + + TRACE(D_EVENTS, "Adding interface %s", iface->name); + + ifa = mb_allocz(p->p.pool, sizeof(struct rip_iface)); + ifa->rip = p; + ifa->iface = iface; + ifa->cf = ic; + + if (ipa_nonzero(ic->address)) + ifa->addr = ic->address; + else if (ic->mode == RIP_IM_MULTICAST) + ifa->addr = rip_is_v2(p) ? IP4_RIP_ROUTERS : IP6_RIP_ROUTERS; + else /* Broadcast */ + ifa->addr = iface->addr->brd; + + init_list(&ifa->neigh_list); + + add_tail(&p->iface_list, NODE ifa); + + ifa->timer = tm_new_set(p->p.pool, rip_iface_timer, ifa, 0, 0); + + struct object_lock *lock = olock_new(p->p.pool); + lock->type = OBJLOCK_UDP; + lock->port = ic->port; + lock->iface = iface; + lock->data = ifa; + lock->hook = rip_iface_locked; + ifa->lock = lock; + + olock_acquire(lock); } static void -rip_dump(struct proto *p) +rip_remove_iface(struct rip_proto *p, struct rip_iface *ifa) { - int i; - node *w; - struct rip_interface *rif; + rip_iface_stop(ifa); - CHK_MAGIC; - WALK_LIST( w, P->connections ) { - struct rip_connection *n = (void *) w; - debug( "RIP: connection #%d: %I\n", n->num, n->addr ); - } - i = 0; - FIB_WALK( &P->rtable, e ) { - debug( "RIP: entry #%d: ", i++ ); - rip_dump_entry( (struct rip_entry *)e ); - } FIB_WALK_END; - i = 0; - WALK_LIST( rif, P->interfaces ) { - debug( "RIP: interface #%d: %s, %I, busy = %x\n", i++, rif->iface?rif->iface->name:"(dummy)", rif->sock->daddr, rif->busy ); - } + TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name); + + rem_node(NODE ifa); + + rfree(ifa->sk); + rfree(ifa->lock); + rfree(ifa->timer); + + mb_free(ifa); +} + +static int +rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_iface_config *new) +{ + struct rip_iface_config *old = ifa->cf; + + /* Change of these options would require to reset the iface socket */ + if ((new->mode != old->mode) || + (new->port != old->port) || + (new->tx_tos != old->tx_tos) || + (new->tx_priority != old->tx_priority) || + (new->ttl_security != old->ttl_security)) + return 0; + + TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name); + + ifa->cf = new; + + 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); + + if (new->bfd != old->bfd) + rip_iface_update_bfd(ifa); + + if (ifa->up) + rip_iface_kick_timer(ifa); + + return 1; } static void -rip_get_route_info(rte *rte, byte *buf, ea_list *attrs) +rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf) { - eattr *metric = ea_find(attrs, EA_RIP_METRIC); - eattr *tag = ea_find(attrs, EA_RIP_TAG); + struct iface *iface; + + WALK_LIST(iface, iface_list) + { + if (! (iface->flags & IF_UP)) + continue; + + struct rip_iface *ifa = rip_find_iface(p, iface); + struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); - buf += bsprintf(buf, " (%d/%d)", rte->pref, metric ? metric->u.data : 0); - if (tag && tag->u.data) - bsprintf(buf, " t%04x", tag->u.data); + if (ifa && ic) + { + if (rip_reconfigure_iface(p, ifa, ic)) + continue; + + /* Hard restart */ + log(L_INFO "%s: Restarting interface %s", p->p.name, ifa->iface->name); + rip_remove_iface(p, ifa); + rip_add_iface(p, iface, ic); + } + + if (ifa && !ic) + rip_remove_iface(p, ifa); + + if (!ifa && ic) + rip_add_iface(p, iface, ic); + } } static void -kill_iface(struct rip_interface *i) +rip_if_notify(struct proto *P, unsigned flags, struct iface *iface) { - DBG( "RIP: Interface %s disappeared\n", i->iface->name); - rfree(i->sock); - mb_free(i); + struct rip_proto *p = (void *) P; + struct rip_config *cf = (void *) P->cf; + + if (iface->flags & IF_IGNORE) + return; + + if (flags & IF_CHANGE_UP) + { + struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); + + if (ic) + rip_add_iface(p, iface, ic); + + return; + } + + struct rip_iface *ifa = rip_find_iface(p, iface); + + if (!ifa) + return; + + if (flags & IF_CHANGE_DOWN) + { + rip_remove_iface(p, ifa); + return; + } + + if (flags & IF_CHANGE_MTU) + rip_iface_update_buffers(ifa); + + if (flags & IF_CHANGE_LINK) + rip_iface_update_state(ifa); } + +/* + * RIP timer events + */ + /** - * new_iface - * @p: myself - * @new: interface to be created or %NULL if we are creating a magic - * socket. The magic socket is used for listening and also for - * sending requested responses. - * @flags: interface flags - * @patt: pattern this interface matched, used for access to config options + * rip_timer - RIP main timer hook + * @t: timer * - * Create an interface structure and start listening on the interface. + * The RIP main timer is responsible for routing table maintenance. Invalid or + * expired routes (&rip_rte) are removed and garbage collection of stale routing + * table entries (&rip_entry) is done. Changes are propagated to core tables, + * route reload is also done here. Note that garbage collection uses a maximal + * GC time, while interfaces maintain an illusion of per-interface GC times in + * rip_send_response(). + * + * Keeping incoming routes and the selected outgoing route are two independent + * functions, therefore after garbage collection some entries now considered + * invalid (RIP_ENTRY_DUMMY) still may have non-empty list of incoming routes, + * while some valid entries (representing an outgoing route) may have that list + * empty. + * + * The main timer is not scheduled periodically but it uses the time of the + * current next event and the minimal interval of any possible event to compute + * the time of the next run. */ -static struct rip_interface * -new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt ) +static void +rip_timer(timer *t) { - struct rip_interface *rif; - struct rip_patt *PATT = (struct rip_patt *) patt; - - rif = mb_allocz(p->pool, sizeof( struct rip_interface )); - rif->iface = new; - rif->proto = p; - rif->busy = NULL; - if (PATT) { - rif->mode = PATT->mode; - rif->metric = PATT->metric; - rif->multicast = (!(PATT->mode & IM_BROADCAST)) && (flags & IF_MULTICAST); - rif->check_ttl = (PATT->ttl_security == 1); - } - /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */ - - if (rif->multicast) - DBG( "Doing multicasts!\n" ); - - rif->sock = sk_new( p->pool ); - rif->sock->type = SK_UDP; - rif->sock->sport = P_CF->port; - rif->sock->rx_hook = rip_rx; - rif->sock->data = rif; - rif->sock->rbsize = 10240; - rif->sock->iface = new; /* Automagically works for dummy interface */ - rif->sock->tbuf = mb_alloc( p->pool, sizeof( struct rip_packet )); - rif->sock->tx_hook = rip_tx; - rif->sock->err_hook = rip_tx_err; - rif->sock->daddr = IPA_NONE; - rif->sock->dport = P_CF->port; - if (new) + struct rip_proto *p = t->data; + struct rip_config *cf = (void *) (p->p.cf); + struct rip_iface *ifa; + struct rip_neighbor *n, *nn; + struct fib_iterator fit; + bird_clock_t next = now + MIN(cf->min_timeout_time, cf->max_garbage_time); + bird_clock_t expires = 0; + + TRACE(D_EVENTS, "Main timer fired"); + + FIB_ITERATE_INIT(&fit, &p->rtable); + + loop: + FIB_ITERATE_START(&p->rtable, &fit, node) + { + struct rip_entry *en = (struct rip_entry *) node; + struct rip_rte *rt, **rp; + int changed = 0; + + /* Checking received routes for timeout and for dead neighbors */ + for (rp = &en->routes; rt = *rp; /* rp = &rt->next */) { - rif->sock->tos = PATT->tx_tos; - rif->sock->priority = PATT->tx_priority; - rif->sock->ttl = PATT->ttl_security ? 255 : 1; - rif->sock->flags = SKF_LADDR_RX | (rif->check_ttl ? SKF_TTL_RX : 0); - } + if (!rip_valid_rte(rt) || (rt->expires <= now)) + { + rip_remove_rte(p, rp); + changed = 1; + continue; + } - if (new) { - if (new->addr->flags & IA_PEER) - log( L_WARN "%s: rip is not defined over unnumbered links", p->name ); - if (rif->multicast) { -#ifndef IPV6 - rif->sock->daddr = ipa_from_u32(0xe0000009); -#else - rif->sock->daddr = IP6_RIP_ROUTERS; -#endif - } else { - rif->sock->daddr = new->addr->brd; + next = MIN(next, rt->expires); + rp = &rt->next; } - } - if (!ipa_nonzero(rif->sock->daddr)) { - if (rif->iface) - log( L_WARN "%s: interface %s is too strange for me", p->name, rif->iface->name ); - } else { + /* Propagating eventual change */ + if (changed || p->rt_reload) + { + /* + * We have to restart the iteration because there may be a cascade of + * synchronous events rip_announce_rte() -> nest table change -> + * rip_rt_notify() -> p->rtable change, invalidating hidden variables. + */ + + FIB_ITERATE_PUT_NEXT(&fit, &p->rtable, node); + rip_announce_rte(p, en); + goto loop; + } - if (sk_open(rif->sock) < 0) - goto err; + /* Checking stale entries for garbage collection timeout */ + if (en->valid == RIP_ENTRY_STALE) + { + expires = en->changed + cf->max_garbage_time; - if (rif->multicast) + if (expires <= now) { - if (sk_setup_multicast(rif->sock) < 0) - goto err; - if (sk_join_group(rif->sock, rif->sock->daddr) < 0) - goto err; + // TRACE(D_EVENTS, "entry is too old: %I/%d", en->n.prefix, en->n.pxlen); + en->valid = 0; } - else + else + next = MIN(next, expires); + } + + /* Remove empty nodes */ + if (!en->valid && !en->routes) + { + FIB_ITERATE_PUT(&fit, node); + fib_delete(&p->rtable, node); + goto loop; + } + } + FIB_ITERATE_END(node); + + p->rt_reload = 0; + + /* Handling neighbor expiration */ + WALK_LIST(ifa, p->iface_list) + WALK_LIST_DELSAFE(n, nn, ifa->neigh_list) + if (n->last_seen) { - if (sk_setup_broadcast(rif->sock) < 0) - goto err; + expires = n->last_seen + n->ifa->cf->timeout_time; + + if (expires <= now) + rip_remove_neighbor(p, n); + else + next = MIN(next, expires); } - } - TRACE(D_EVENTS, "Listening on %s, port %d, mode %s (%I)", rif->iface ? rif->iface->name : "(dummy)", P_CF->port, rif->multicast ? "multicast" : "broadcast", rif->sock->daddr ); - - return rif; + tm_start(p->timer, MAX(next - now, 1)); +} + +static inline void +rip_kick_timer(struct rip_proto *p) +{ + if (p->timer->expires > (now + 1)) + tm_start(p->timer, 1); /* Or 100 ms */ +} - err: - sk_log_error(rif->sock, p->name); - log(L_ERR "%s: Cannot open socket for %s", p->name, rif->iface ? rif->iface->name : "(dummy)" ); - if (rif->iface) { - rfree(rif->sock); - mb_free(rif); - return NULL; +/** + * rip_iface_timer - RIP interface timer hook + * @t: timer + * + * RIP interface timers are responsible for scheduling both regular and + * triggered updates. Fixed, delay-independent period is used for regular + * updates, while minimal separating interval is enforced for triggered updates. + * The function also ensures that a new update is not started when the old one + * is still running. + */ +static void +rip_iface_timer(timer *t) +{ + struct rip_iface *ifa = t->data; + struct rip_proto *p = ifa->rip; + bird_clock_t period = ifa->cf->update_time; + + if (ifa->cf->passive) + return; + + TRACE(D_EVENTS, "Interface timer fired for %s", ifa->iface->name); + + if (ifa->tx_active) + { + if (now < (ifa->next_regular + period)) + { tm_start(ifa->timer, 1); return; } + + /* We are too late, reset is done by rip_send_table() */ + log(L_WARN "%s: Too slow update on %s, resetting", p->p.name, ifa->iface->name); } - /* On dummy, we just return non-working socket, so that user gets error every time anyone requests table */ - return rif; + + if (now >= ifa->next_regular) + { + /* Send regular update, set timer for next period (or following one if necessay) */ + TRACE(D_EVENTS, "Sending regular updates for %s", ifa->iface->name); + rip_send_table(p, ifa, ifa->addr, 0); + ifa->next_regular += period * (1 + ((now - ifa->next_regular) / period)); + ifa->want_triggered = 0; + p->triggered = 0; + } + else if (ifa->want_triggered && (now >= ifa->next_triggered)) + { + /* Send triggered update, enforce interval between triggered updates */ + TRACE(D_EVENTS, "Sending triggered updates for %s", ifa->iface->name); + rip_send_table(p, ifa, ifa->addr, ifa->want_triggered); + ifa->next_triggered = now + MIN(5, period / 2 + 1); + ifa->want_triggered = 0; + p->triggered = 0; + } + + tm_start(ifa->timer, ifa->want_triggered ? 1 : (ifa->next_regular - now)); } -static void -rip_real_if_add(struct object_lock *lock) +static inline void +rip_iface_kick_timer(struct rip_iface *ifa) { - struct iface *iface = lock->iface; - struct proto *p = lock->data; - struct rip_interface *rif; - struct iface_patt *k = iface_patt_find(&P_CF->iface_list, iface, iface->addr); - - if (!k) - bug("This can not happen! It existed few seconds ago!" ); - DBG("adding interface %s\n", iface->name ); - rif = new_iface(p, iface, iface->flags, k); - if (rif) { - add_head( &P->interfaces, NODE rif ); - DBG("Adding object lock of %p for %p\n", lock, rif); - rif->lock = lock; - } else { rfree(lock); } + if (ifa->timer->expires > (now + 1)) + tm_start(ifa->timer, 1); /* Or 100 ms */ } static void -rip_if_notify(struct proto *p, unsigned c, struct iface *iface) +rip_trigger_update(struct rip_proto *p) { - DBG( "RIP: if notify\n" ); - if (iface->flags & IF_IGNORE) + if (p->triggered) return; - if (c & IF_CHANGE_DOWN) { - struct rip_interface *i; - i = find_interface(p, iface); - if (i) { - rem_node(NODE i); - rfree(i->lock); - kill_iface(i); - } - } - if (c & IF_CHANGE_UP) { - struct iface_patt *k = iface_patt_find(&P_CF->iface_list, iface, iface->addr); - struct object_lock *lock; - struct rip_patt *PATT = (struct rip_patt *) k; - - if (!k) return; /* We are not interested in this interface */ - - lock = olock_new( p->pool ); - if (!(PATT->mode & IM_BROADCAST) && (iface->flags & IF_MULTICAST)) -#ifndef IPV6 - lock->addr = ipa_from_u32(0xe0000009); -#else - lock->addr = IP6_RIP_ROUTERS; -#endif - else - lock->addr = iface->addr->brd; - lock->port = P_CF->port; - lock->iface = iface; - lock->hook = rip_real_if_add; - lock->data = p; - lock->type = OBJLOCK_UDP; - olock_acquire(lock); + + struct rip_iface *ifa; + WALK_LIST(ifa, p->iface_list) + { + /* Interface not active */ + if (! ifa->up) + continue; + + /* Already scheduled */ + if (ifa->want_triggered) + continue; + + TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name); + ifa->want_triggered = now; + rip_iface_kick_timer(ifa); } + + p->triggered = 1; } + +/* + * RIP protocol glue + */ + static struct ea_list * -rip_gen_attrs(struct linpool *pool, int metric, u16 tag) +rip_prepare_attrs(struct linpool *pool, ea_list *next, u8 metric, u16 tag) { - struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2*sizeof(eattr)); + struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr)); - l->next = NULL; + l->next = next; l->flags = EALF_SORTED; l->count = 2; - l->attrs[0].id = EA_RIP_TAG; + + l->attrs[0].id = EA_RIP_METRIC; l->attrs[0].flags = 0; l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP; - l->attrs[0].u.data = tag; - l->attrs[1].id = EA_RIP_METRIC; + l->attrs[0].u.data = metric; + + l->attrs[1].id = EA_RIP_TAG; l->attrs[1].flags = 0; l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP; - l->attrs[1].u.data = metric; + l->attrs[1].u.data = tag; + return l; } static int -rip_import_control(struct proto *p, struct rte **rt, struct ea_list **attrs, struct linpool *pool) +rip_import_control(struct proto *P, struct rte **rt, struct ea_list **attrs, struct linpool *pool) { - if ((*rt)->attrs->src->proto == p) /* My own must not be touched */ - return 1; + /* Prepare attributes with initial values */ + if ((*rt)->attrs->source != RTS_RIP) + *attrs = rip_prepare_attrs(pool, *attrs, 1, 0); - if ((*rt)->attrs->source != RTS_RIP) { - struct ea_list *new = rip_gen_attrs(pool, 1, 0); - new->next = *attrs; - *attrs = new; - } return 0; } +static int +rip_reload_routes(struct proto *P) +{ + struct rip_proto *p = (struct rip_proto *) P; + + if (p->rt_reload) + return 1; + + TRACE(D_EVENTS, "Scheduling route reload"); + p->rt_reload = 1; + rip_kick_timer(p); + + return 1; +} + static struct ea_list * rip_make_tmp_attrs(struct rte *rt, struct linpool *pool) { - return rip_gen_attrs(pool, rt->u.rip.metric, rt->u.rip.tag); + return rip_prepare_attrs(pool, NULL, rt->u.rip.metric, rt->u.rip.tag); } -static void +static void rip_store_tmp_attrs(struct rte *rt, struct ea_list *attrs) { - rt->u.rip.tag = ea_get_int(attrs, EA_RIP_TAG, 0); rt->u.rip.metric = ea_get_int(attrs, EA_RIP_METRIC, 1); + rt->u.rip.tag = ea_get_int(attrs, EA_RIP_TAG, 0); } -/* - * rip_rt_notify - core tells us about new route (possibly our - * own), so store it into our data structures. - */ -static void -rip_rt_notify(struct proto *p, struct rtable *table UNUSED, struct network *net, - struct rte *new, struct rte *old UNUSED, struct ea_list *attrs) +static int +rip_rte_better(struct rte *new, struct rte *old) { - CHK_MAGIC; - struct rip_entry *e; - - e = fib_find( &P->rtable, &net->n.prefix, net->n.pxlen ); - if (e) - fib_delete( &P->rtable, e ); - - if (new) { - e = fib_get( &P->rtable, &net->n.prefix, net->n.pxlen ); - - e->nexthop = new->attrs->gw; - e->metric = 0; - e->whotoldme = IPA_NONE; - new->u.rip.entry = e; - - e->tag = ea_get_int(attrs, EA_RIP_TAG, 0); - e->metric = ea_get_int(attrs, EA_RIP_METRIC, 1); - if (e->metric > P_CF->infinity) - e->metric = P_CF->infinity; - - if (new->attrs->src->proto == p) - e->whotoldme = new->attrs->from; - - if (!e->metric) /* That's okay: this way user can set his own value for external - routes in rip. */ - e->metric = 5; - e->updated = e->changed = now; - e->flags = 0; - } + return new->u.rip.metric < old->u.rip.metric; } static int rip_rte_same(struct rte *new, struct rte *old) { - /* new->attrs == old->attrs always */ - return new->u.rip.metric == old->u.rip.metric; + return ((new->u.rip.metric == old->u.rip.metric) && + (new->u.rip.tag == old->u.rip.tag) && + (new->u.rip.from == old->u.rip.from)); } +static struct proto * +rip_init(struct proto_config *cfg) +{ + struct proto *P = proto_new(cfg, sizeof(struct rip_proto)); + + P->accept_ra_types = RA_OPTIMAL; + P->if_notify = rip_if_notify; + P->rt_notify = rip_rt_notify; + P->neigh_notify = rip_neigh_notify; + P->import_control = rip_import_control; + P->reload_routes = rip_reload_routes; + P->make_tmp_attrs = rip_make_tmp_attrs; + P->store_tmp_attrs = rip_store_tmp_attrs; + P->rte_better = rip_rte_better; + P->rte_same = rip_rte_same; + + return P; +} + static int -rip_rte_better(struct rte *new, struct rte *old) +rip_start(struct proto *P) { - struct proto *p = new->attrs->src->proto; + struct rip_proto *p = (void *) P; + struct rip_config *cf = (void *) (P->cf); - if (ipa_equal(old->attrs->from, new->attrs->from)) - return 1; + init_list(&p->iface_list); + fib_init(&p->rtable, P->pool, sizeof(struct rip_entry), 0, rip_init_entry); + p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte)); + p->timer = tm_new_set(P->pool, rip_timer, p, 0, 0); - if (old->u.rip.metric < new->u.rip.metric) - return 0; + p->ecmp = cf->ecmp; + p->infinity = cf->infinity; + p->triggered = 0; - if (old->u.rip.metric > new->u.rip.metric) - return 1; + p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; + p->log_rte_tbf = (struct tbf){ .rate = 4, .burst = 20 }; - if (old->attrs->src->proto == new->attrs->src->proto) /* This does not make much sense for different protocols */ - if ((old->u.rip.metric == new->u.rip.metric) && - ((now - old->lastmod) > (P_CF->timeout_time / 2))) - return 1; + tm_start(p->timer, MIN(cf->min_timeout_time, cf->max_garbage_time)); - return 0; + return PS_UP; } -/* - * rip_rte_insert - we maintain linked list of "our" entries in main - * routing table, so that we can timeout them correctly. rip_timer() - * walks the list. - */ -static void -rip_rte_insert(net *net UNUSED, rte *rte) +static int +rip_reconfigure(struct proto *P, struct proto_config *c) { - struct proto *p = rte->attrs->src->proto; - CHK_MAGIC; - DBG( "rip_rte_insert: %p\n", rte ); - add_head( &P->garbage, &rte->u.rip.garbage ); -} + struct rip_proto *p = (void *) P; + struct rip_config *new = (void *) c; + // struct rip_config *old = (void *) (P->cf); -/* - * rip_rte_remove - link list maintenance - */ -static void -rip_rte_remove(net *net UNUSED, rte *rte) -{ -#ifdef LOCAL_DEBUG - struct proto *p = rte->attrs->src->proto; - CHK_MAGIC; - DBG( "rip_rte_remove: %p\n", rte ); -#endif - rem_node( &rte->u.rip.garbage ); -} + if (new->infinity != p->infinity) + return 0; -static struct proto * -rip_init(struct proto_config *cfg) -{ - struct proto *p = proto_new(cfg, sizeof(struct rip_proto)); - - p->accept_ra_types = RA_OPTIMAL; - p->if_notify = rip_if_notify; - p->rt_notify = rip_rt_notify; - p->import_control = rip_import_control; - p->make_tmp_attrs = rip_make_tmp_attrs; - p->store_tmp_attrs = rip_store_tmp_attrs; - p->rte_better = rip_rte_better; - p->rte_same = rip_rte_same; - p->rte_insert = rip_rte_insert; - p->rte_remove = rip_rte_remove; - - return p; + TRACE(D_EVENTS, "Reconfiguring"); + + p->p.cf = c; + p->ecmp = new->ecmp; + rip_reconfigure_ifaces(p, new); + + p->rt_reload = 1; + rip_kick_timer(p); + + return 1; } -void -rip_init_config(struct rip_proto_config *c) +static void +rip_get_route_info(rte *rte, byte *buf, ea_list *attrs) { - init_list(&c->iface_list); - c->infinity = 16; - c->port = RIP_PORT; - c->period = 30; - c->garbage_time = 120+180; - c->timeout_time = 120; - c->passwords = NULL; - c->authtype = AT_NONE; + buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric); + + if (rte->u.rip.tag) + bsprintf(buf, " [%04x]", rte->u.rip.tag); } static int rip_get_attr(eattr *a, byte *buf, int buflen UNUSED) { - switch (a->id) { - case EA_RIP_METRIC: bsprintf( buf, "metric: %d", a->u.data ); return GA_FULL; - case EA_RIP_TAG: bsprintf( buf, "tag: %d", a->u.data ); return GA_FULL; - default: return GA_UNKNOWN; + switch (a->id) + { + case EA_RIP_METRIC: + bsprintf(buf, "metric: %d", a->u.data); + return GA_FULL; + + case EA_RIP_TAG: + bsprintf(buf, "tag: %04x", a->u.data); + return GA_FULL; + + default: + return GA_UNKNOWN; } } -static int -rip_pat_compare(struct rip_patt *a, struct rip_patt *b) +void +rip_show_interfaces(struct proto *P, char *iff) { - return ((a->metric == b->metric) && - (a->mode == b->mode) && - (a->tx_tos == b->tx_tos) && - (a->tx_priority == b->tx_priority)); + struct rip_proto *p = (void *) P; + struct rip_iface *ifa = NULL; + struct rip_neighbor *n = NULL; + + if (p->p.proto_state != PS_UP) + { + cli_msg(-1021, "%s: is not up", p->p.name); + cli_msg(0, ""); + return; + } + + cli_msg(-1021, "%s:", p->p.name); + cli_msg(-1021, "%-10s %-6s %6s %6s %6s", + "Interface", "State", "Metric", "Nbrs", "Timer"); + + WALK_LIST(ifa, p->iface_list) + { + if (iff && !patmatch(iff, ifa->iface->name)) + continue; + + int nbrs = 0; + WALK_LIST(n, ifa->neigh_list) + if (n->last_seen) + nbrs++; + + int timer = MAX(ifa->next_regular - now, 0); + cli_msg(-1021, "%-10s %-6s %6u %6u %6u", + ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer); + } + + cli_msg(0, ""); } -static int -rip_reconfigure(struct proto *p, struct proto_config *c) +void +rip_show_neighbors(struct proto *P, char *iff) { - struct rip_proto_config *new = (struct rip_proto_config *) c; - int generic = sizeof(struct proto_config) + sizeof(list) /* + sizeof(struct password_item *) */; + struct rip_proto *p = (void *) P; + struct rip_iface *ifa = NULL; + struct rip_neighbor *n = NULL; - if (!iface_patts_equal(&P_CF->iface_list, &new->iface_list, (void *) rip_pat_compare)) - return 0; - return !memcmp(((byte *) P_CF) + generic, - ((byte *) new) + generic, - sizeof(struct rip_proto_config) - generic); + if (p->p.proto_state != PS_UP) + { + cli_msg(-1022, "%s: is not up", p->p.name); + cli_msg(0, ""); + return; + } + + cli_msg(-1022, "%s:", p->p.name); + cli_msg(-1022, "%-25s %-10s %6s %6s %6s", + "IP address", "Interface", "Metric", "Routes", "Seen"); + + WALK_LIST(ifa, p->iface_list) + { + if (iff && !patmatch(iff, ifa->iface->name)) + continue; + + WALK_LIST(n, ifa->neigh_list) + { + if (!n->last_seen) + continue; + + int timer = now - n->last_seen; + cli_msg(-1022, "%-25I %-10s %6u %6u %6u", + n->nbr->addr, ifa->iface->name, ifa->cf->metric, n->uc, timer); + } + } + + cli_msg(0, ""); } static void -rip_copy_config(struct proto_config *dest, struct proto_config *src) +rip_dump(struct proto *P) { - /* Shallow copy of everything */ - proto_copy_rest(dest, src, sizeof(struct rip_proto_config)); + struct rip_proto *p = (struct rip_proto *) P; + struct rip_iface *ifa; + int i; - /* We clean up iface_list, ifaces are non-sharable */ - init_list(&((struct rip_proto_config *) dest)->iface_list); + i = 0; + FIB_WALK(&p->rtable, e) + { + struct rip_entry *en = (struct rip_entry *) e; + debug("RIP: entry #%d: %I/%d via %I dev %s valid %d metric %d age %d s\n", + i++, en->n.prefix, en->n.pxlen, en->next_hop, en->iface->name, + en->valid, en->metric, now - en->changed); + } + FIB_WALK_END; - /* Copy of passwords is OK, it just will be replaced in dest when used */ + i = 0; + WALK_LIST(ifa, p->iface_list) + { + debug("RIP: interface #%d: %s, %I, up = %d, busy = %d\n", + i++, ifa->iface->name, ifa->sk ? ifa->sk->daddr : IPA_NONE, + ifa->up, ifa->tx_active); + } } @@ -1050,12 +1278,11 @@ struct protocol proto_rip = { .template = "rip%d", .attr_class = EAP_RIP, .preference = DEF_PREF_RIP, - .config_size = sizeof(struct rip_proto_config), + .config_size = sizeof(struct rip_config), .init = rip_init, .dump = rip_dump, .start = rip_start, .reconfigure = rip_reconfigure, - .copy_config = rip_copy_config, .get_route_info = rip_get_route_info, .get_attr = rip_get_attr }; diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 2a327260..f245e612 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -1,185 +1,227 @@ /* - * Structures for RIP protocol + * BIRD -- Routing Information Protocol (RIP) * - FIXME: in V6, they insert additional entry whenever next hop differs. Such entry is identified by 0xff in metric. + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. */ +#ifndef _BIRD_RIP_H_ +#define _BIRD_RIP_H_ + +#include "nest/bird.h" +#include "nest/cli.h" +#include "nest/iface.h" +#include "nest/protocol.h" #include "nest/route.h" #include "nest/password.h" #include "nest/locks.h" +#include "nest/bfd.h" +#include "lib/lists.h" +#include "lib/resource.h" +#include "lib/socket.h" +#include "lib/string.h" +#include "lib/timer.h" -#define EA_RIP_TAG EA_CODE(EAP_RIP, 0) -#define EA_RIP_METRIC EA_CODE(EAP_RIP, 1) -#define PACKET_MAX 25 -#define PACKET_MD5_MAX 18 /* FIXME */ +#ifdef IPV6 +#define RIP_IS_V2 0 +#else +#define RIP_IS_V2 1 +#endif +#define RIP_V1 1 +#define RIP_V2 2 -#define RIP_V1 1 -#define RIP_V2 2 -#define RIP_NG 1 /* A new version numbering */ +#define RIP_PORT 520 /* RIP for IPv4 */ +#define RIP_NG_PORT 521 /* RIPng */ -#ifndef IPV6 -#define RIP_PORT 520 /* RIP for IPv4 */ -#else -#define RIP_PORT 521 /* RIPng */ -#endif +#define RIP_MAX_PKT_LENGTH 532 /* 512 + IP4_HEADER_LENGTH */ +#define RIP_AUTH_TAIL_LENGTH 20 /* 4 + MD5 length */ -struct rip_connection { - node n; +#define RIP_DEFAULT_ECMP_LIMIT 16 +#define RIP_DEFAULT_INFINITY 16 +#define RIP_DEFAULT_UPDATE_TIME 30 +#define RIP_DEFAULT_TIMEOUT_TIME 180 +#define RIP_DEFAULT_GARBAGE_TIME 120 - int num; - struct proto *proto; - ip_addr addr; - sock *send; - struct rip_interface *rif; - struct fib_iterator iter; - ip_addr daddr; - int dport; - int done; -}; +struct rip_config +{ + struct proto_config c; + list patt_list; /* List of iface configs (struct rip_iface_config) */ -struct rip_packet_heading { /* 4 bytes */ - u8 command; -#define RIPCMD_REQUEST 1 /* want info */ -#define RIPCMD_RESPONSE 2 /* responding to request */ -#define RIPCMD_TRACEON 3 /* turn tracing on */ -#define RIPCMD_TRACEOFF 4 /* turn it off */ -#define RIPCMD_MAX 5 - u8 version; -#define RIP_V1 1 -#define RIP_V2 2 -#define RIP_NG 1 /* this is verion 1 of RIPng */ - u16 unused; + u8 rip2; /* RIPv2 (IPv4) or RIPng (IPv6) */ + u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */ + u8 infinity; /* Maximum metric value, representing infinity */ + + u32 min_timeout_time; /* Minimum of interface timeout_time */ + u32 max_garbage_time; /* Maximum of interface garbage_time */ }; -#ifndef IPV6 -struct rip_block { /* 20 bytes */ - u16 family; /* 0xffff on first message means this is authentication */ - u16 tag; - ip_addr network; - ip_addr netmask; - ip_addr nexthop; - u32 metric; +struct rip_iface_config +{ + struct iface_patt i; + ip_addr address; /* Configured dst address */ + u16 port; /* Src+dst port */ + u8 metric; /* Incoming metric */ + u8 mode; /* Interface mode (RIP_IM_*) */ + u8 passive; /* Passive iface - no packets are sent */ + u8 version; /* RIP version used for outgoing packets */ + u8 version_only; /* FIXXX */ + u8 split_horizon; /* Split horizon is used in route updates */ + u8 poison_reverse; /* Poisoned reverse is used in route updates */ + u8 check_zero; /* Validation of RIPv1 reserved fields */ + u8 ecmp_weight; /* Weight for ECMP routes*/ + u8 auth_type; /* Authentication type (RIP_AUTH_*) */ + u8 ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ + u8 check_link; /* Whether iface link change is used */ + u8 bfd; /* Use BFD on iface */ + u16 rx_buffer; /* RX buffer size, 0 for MTU */ + u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */ + int tx_tos; + int tx_priority; + u32 update_time; /* Periodic update interval */ + u32 timeout_time; /* Route expiration timeout */ + u32 garbage_time; /* Unreachable entry GC timeout */ + list *passwords; /* Passwords for authentication */ }; -#else -struct rip_block { /* IPv6 version!, 20 bytes, too */ - ip_addr network; - u16 tag; - u8 pxlen; - u8 metric; + +struct rip_proto +{ + struct proto p; + struct fib rtable; /* Internal routing table */ + list iface_list; /* List of interfaces (struct rip_iface) */ + slab *rte_slab; /* Slab for internal routes (struct rip_rte) */ + timer *timer; /* Main protocol timer */ + + u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */ + u8 infinity; /* Maximum metric value, representing infinity */ + u8 triggered; /* Logical AND of interface want_triggered values */ + u8 rt_reload; /* Route reload is scheduled */ + + struct tbf log_pkt_tbf; /* TBF for packet messages */ + struct tbf log_rte_tbf; /* TBF for RTE messages */ }; -#endif -struct rip_block_auth { /* 20 bytes */ - u16 mustbeFFFF; - u16 authtype; - u16 packetlen; - u8 keyid; - u8 authlen; - u32 seq; - u32 zero0; - u32 zero1; +struct rip_iface +{ + node n; + struct rip_proto *rip; + struct iface *iface; /* Underyling core interface */ + struct rip_iface_config *cf; /* Related config, must be updated in reconfigure */ + struct object_lock *lock; /* Interface lock */ + timer *timer; /* Interface timer */ + sock *sk; /* UDP socket */ + + u8 up; /* Interface is active */ + u8 csn_ready; /* Nonzero CSN can be used */ + u16 tx_plen; /* Max TX packet data length */ + u32 csn; /* Last used crypto sequence number */ + ip_addr addr; /* Destination multicast/broadcast address */ + list neigh_list; /* List of iface neighbors (struct rip_neighbor) */ + + /* Update scheduling */ + bird_clock_t next_regular; /* Next time when regular update should be called */ + bird_clock_t next_triggered; /* Next time when triggerd update may be called */ + bird_clock_t want_triggered; /* Nonzero if triggered update is scheduled */ + + /* Active update */ + int tx_active; /* Update session is active */ + ip_addr tx_addr; /* Update session destination address */ + bird_clock_t tx_changed; /* Minimal changed time for triggered update */ + struct fib_iterator tx_fit; /* FIB iterator in RIP routing table (p.rtable) */ }; -struct rip_md5_tail { /* 20 bytes */ - u16 mustbeFFFF; - u16 mustbe0001; - char md5[16]; +struct rip_neighbor +{ + node n; + struct rip_iface *ifa; /* Associated interface, may be NULL if stale */ + struct neighbor *nbr; /* Associaded core neighbor, may be NULL if stale */ + struct bfd_request *bfd_req; /* BFD request, if BFD is used */ + bird_clock_t last_seen; /* Time of last received and accepted message */ + u32 uc; /* Use count, number of routes linking the neighbor */ + u32 csn; /* Last received crypto sequence number */ }; -struct rip_entry { +struct rip_entry +{ struct fib_node n; + struct rip_rte *routes; /* List of incoming routes */ - ip_addr whotoldme; - ip_addr nexthop; - int metric; - u16 tag; + u8 valid; /* Entry validity state (RIP_ENTRY_*) */ + u8 metric; /* Outgoing route metric */ + u16 tag; /* Outgoing route tag */ + struct iface *from; /* Outgoing route from, NULL if from proto */ + struct iface *iface; /* Outgoing route iface (for next hop) */ + ip_addr next_hop; /* Outgoing route next hop */ - bird_clock_t updated, changed; - int flags; + bird_clock_t changed; /* Last time when the outgoing route metric changed */ }; -struct rip_packet { - struct rip_packet_heading heading; - struct rip_block block[PACKET_MAX]; -}; +struct rip_rte +{ + struct rip_rte *next; -struct rip_interface { - node n; - struct proto *proto; - struct iface *iface; - sock *sock; - struct rip_connection *busy; - int metric; /* You don't want to put struct rip_patt *patt here -- think about reconfigure */ - int mode; - int check_ttl; /* Check incoming packets for TTL 255 */ - int triggered; - struct object_lock *lock; - int multicast; + struct rip_neighbor *from; /* Advertising router */ + ip_addr next_hop; /* Route next hop (iface is from->nbr->iface) */ + u16 metric; /* Route metric (after increase) */ + u16 tag; /* Route tag */ + + bird_clock_t expires; /* Time of route expiration */ }; -struct rip_patt { - struct iface_patt i; - int metric; /* If you add entries here, don't forget to modify patt_compare! */ - int mode; -#define IM_BROADCAST 2 -#define IM_QUIET 4 -#define IM_NOLISTEN 8 -#define IM_VERSION1 16 - int tx_tos; - int tx_priority; - int ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ -}; +#define RIP_AUTH_NONE 0 +#define RIP_AUTH_PLAIN 2 +#define RIP_AUTH_CRYPTO 3 -struct rip_proto_config { - struct proto_config c; - list iface_list; /* Patterns configured -- keep it first; see rip_reconfigure why */ - list *passwords; /* Passwords, keep second */ - - int infinity; /* User configurable data; must be comparable with memcmp */ - int port; - int period; - int garbage_time; - int timeout_time; - - int authtype; -#define AT_NONE 0 -#define AT_PLAINTEXT 2 -#define AT_MD5 3 - int honor; -#define HO_NEVER 0 -#define HO_NEIGHBOR 1 -#define HO_ALWAYS 2 -}; +#define RIP_IM_MULTICAST 1 +#define RIP_IM_BROADCAST 2 -struct rip_proto { - struct proto inherited; - timer *timer; - list connections; - struct fib rtable; - list garbage; - list interfaces; /* Interfaces we really know about */ -#ifdef LOCAL_DEBUG - int magic; -#endif - int tx_count; /* Do one regular update once in a while */ - int rnd_count; /* Randomize sending time */ -}; +#define RIP_ENTRY_DUMMY 0 /* Only used to store list of incoming routes */ +#define RIP_ENTRY_VALID 1 /* Valid outgoing route */ +#define RIP_ENTRY_STALE 2 /* Stale outgoing route, waiting for GC */ -#ifdef LOCAL_DEBUG -#define RIP_MAGIC 81861253 -#define CHK_MAGIC do { if (P->magic != RIP_MAGIC) bug( "Not enough magic" ); } while (0) -#else -#define CHK_MAGIC do { } while (0) -#endif +#define EA_RIP_METRIC EA_CODE(EAP_RIP, 0) +#define EA_RIP_TAG EA_CODE(EAP_RIP, 1) +#define rip_is_v2(X) RIP_IS_V2 +#define rip_is_ng(X) (!RIP_IS_V2) -void rip_init_config(struct rip_proto_config *c); +/* +static inline int rip_is_v2(struct rip_proto *p) +{ return p->rip2; } + +static inline int rip_is_ng(struct rip_proto *p) +{ return ! p->rip2; } +*/ + +static inline void +rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa) +{ + if (ifa->tx_active) + { + FIB_ITERATE_UNLINK(&ifa->tx_fit, &p->rtable); + ifa->tx_active = 0; + } +} + +/* rip.c */ +void rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new); +void rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from); +struct rip_neighbor * rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa); +void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n); +void rip_show_interfaces(struct proto *P, char *iff); +void rip_show_neighbors(struct proto *P, char *iff); + +/* packets.c */ +void rip_send_request(struct rip_proto *p, struct rip_iface *ifa); +void rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed); +int rip_open_socket(struct rip_iface *ifa); -/* Authentication functions */ -int rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num, ip_addr whotoldme ); -int rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num ); +#endif From acb04cfdc550697a7171a86ca559fd8c52841acb Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sat, 17 Oct 2015 14:36:53 +0200 Subject: [PATCH 029/165] Minor changes --- nest/route.h | 34 ++++++++++++++++++++-------------- sysdep/linux/netlink.c | 23 ++++++++++++++++------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/nest/route.h b/nest/route.h index 6067526d..d0f8c688 100644 --- a/nest/route.h +++ b/nest/route.h @@ -174,7 +174,7 @@ struct hostentry { ip_addr addr; /* IP address of host, part of key */ ip_addr link; /* (link-local) IP address of host, used as gw if host is directly attached */ - struct rtable *tab; /* Dependent table, part of key*/ + struct rtable *tab; /* Dependent table, part of key */ struct hostentry *next; /* Next in hash chain */ unsigned hash_key; /* Hash key */ unsigned uc; /* Use count */ @@ -507,19 +507,25 @@ void rta_show(struct cli *, rta *, ea_list *); void rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_addr *ll); /* - * rta_set_recursive_next_hop() acquires hostentry from hostcache and - * fills rta->hostentry field. New hostentry has zero use - * count. Cached rta locks its hostentry (increases its use count), - * uncached rta does not lock it. Hostentry with zero use count is - * removed asynchronously during host cache update, therefore it is - * safe to hold such hostentry temorarily. Hostentry holds a lock for - * a 'source' rta, mainly to share multipath nexthops. There is no - * need to hold a lock for hostentry->dep table, because that table - * contains routes responsible for that hostentry, and therefore is - * non-empty if given hostentry has non-zero use count. The protocol - * responsible for routes with recursive next hops should also hold a - * lock for a table governing that routes (argument tab to - * rta_set_recursive_next_hop()). + * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills + * rta->hostentry field. New hostentry has zero use count. Cached rta locks its + * hostentry (increases its use count), uncached rta does not lock it. Hostentry + * with zero use count is removed asynchronously during host cache update, + * therefore it is safe to hold such hostentry temorarily. Hostentry holds a + * lock for a 'source' rta, mainly to share multipath nexthops. + * + * There is no need to hold a lock for hostentry->dep table, because that table + * contains routes responsible for that hostentry, and therefore is non-empty if + * given hostentry has non-zero use count. If the hostentry has zero use count, + * the entry is removed before dep is referenced. + * + * The protocol responsible for routes with recursive next hops should hold a + * lock for a 'source' table governing that routes (argument tab to + * rta_set_recursive_next_hop()), because its routes reference hostentries + * (through rta) related to the governing table. When all such routes are + * removed, rtas are immediately removed achieving zero uc. Then the 'source' + * table lock could be immediately released, although hostentries may still + * exist - they will be freed together with the 'source' table. */ static inline void rt_lock_hostentry(struct hostentry *he) { if (he) he->uc++; } diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 9c9449e2..674d338b 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -239,6 +239,16 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) return 1; } +static inline ip4_addr rta_get_u32(struct rtattr *a) +{ return *(u32 *) RTA_DATA(a); } + +static inline ip4_addr rta_get_ip4(struct rtattr *a) +{ return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); } + +static inline ip6_addr rta_get_ip6(struct rtattr *a) +{ return ip6_ntoh(*(ip6_addr *) RTA_DATA(a)); } + + struct rtattr * nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen) { @@ -420,7 +430,7 @@ nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) return -1; metrics[0] |= 1 << a->rta_type; - metrics[a->rta_type] = *(u32 *)RTA_DATA(a); + metrics[a->rta_type] = rta_get_u32(a); } if (len > 0) @@ -456,7 +466,7 @@ nl_parse_link(struct nlmsghdr *h, int scan) return; } name = RTA_DATA(a[IFLA_IFNAME]); - memcpy(&mtu, RTA_DATA(a[IFLA_MTU]), sizeof(u32)); + mtu = rta_get_u32(a[IFLA_MTU]); ifi = if_find_by_index(i->ifi_index); if (!new) @@ -831,7 +841,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) } if (a[RTA_OIF]) - memcpy(&oif, RTA_DATA(a[RTA_OIF]), sizeof(oif)); + oif = rta_get_u32(a[RTA_OIF]); p = nl_table_map[i->rtm_table]; /* Do we know this table? */ DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p ? p->p.name : "(none)"); @@ -965,11 +975,10 @@ nl_parse_route(struct nlmsghdr *h, int scan) e->u.krt.src = src; e->u.krt.proto = i->rtm_protocol; e->u.krt.type = i->rtm_type; + e->u.krt.metric = 0; if (a[RTA_PRIORITY]) - memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); - else - e->u.krt.metric = 0; + e->u.krt.metric = rta_get_u32(a[RTA_PRIORITY]); if (a[RTA_PREFSRC]) { @@ -1000,7 +1009,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) ea->attrs[0].id = EA_KRT_REALM; ea->attrs[0].flags = 0; ea->attrs[0].type = EAF_TYPE_INT; - memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4); + ea->attrs[0].u.data = rta_get_u32(a[RTA_FLOW]); } if (a[RTA_METRICS]) From 338f85ca7721fac16394ccabd561ddb5ccaacb36 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 3 Nov 2015 11:08:57 +0100 Subject: [PATCH 030/165] IO: Handle fd values too big for select() If the number of sockets is too much for select(), we should at least handle it with proper error messages and reject new sockets instead of breaking the event loop. Thanks to Alexander V. Chernikov for the patch. --- sysdep/unix/io.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 0724667d..726f1e49 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1309,6 +1309,16 @@ sk_passive_connected(sock *s, int type) return 0; } + if (fd >= FD_SETSIZE) + { + /* FIXME: Call err_hook instead ? */ + log(L_ERR "SOCK: Incoming connection from %I%J (port %d) %s", + t->daddr, ipa_is_link_local(t->daddr) ? t->iface : NULL, + t->dport, "rejected due to FD_SETSIZE limit"); + close(fd); + return 1; + } + sock *t = sk_new(s->pool); t->type = type; t->fd = fd; @@ -1404,6 +1414,9 @@ sk_open(sock *s) if (fd < 0) ERR("socket"); + if (fd >= FD_SETSIZE) + ERR2("FD_SETSIZE limit reached"); + s->af = af; s->fd = fd; From 3aed0a6ff7b2b811a535202fd787281d2ac33409 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 3 Nov 2015 11:27:27 +0100 Subject: [PATCH 031/165] IO: Fix the previous bugfix I should check it after making some trivial changes. The original patch from Alexander has it right. --- sysdep/unix/io.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 726f1e49..b636e799 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1309,16 +1309,6 @@ sk_passive_connected(sock *s, int type) return 0; } - if (fd >= FD_SETSIZE) - { - /* FIXME: Call err_hook instead ? */ - log(L_ERR "SOCK: Incoming connection from %I%J (port %d) %s", - t->daddr, ipa_is_link_local(t->daddr) ? t->iface : NULL, - t->dport, "rejected due to FD_SETSIZE limit"); - close(fd); - return 1; - } - sock *t = sk_new(s->pool); t->type = type; t->fd = fd; @@ -1338,6 +1328,18 @@ sk_passive_connected(sock *s, int type) log(L_WARN "SOCK: Cannot get remote IP address for TCP<"); } + if (fd >= FD_SETSIZE) + { + /* FIXME: Call err_hook instead ? */ + log(L_ERR "SOCK: Incoming connection from %I%J (port %d) %s", + t->daddr, ipa_is_link_local(t->daddr) ? t->iface : NULL, + t->dport, "rejected due to FD_SETSIZE limit"); + close(fd); + t->fd = -1; + rfree(t); + return 1; + } + if (sk_setup(t) < 0) { /* FIXME: Call err_hook instead ? */ From 9b9a7143c43d01f0459d40363d56e9c7690c596f Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 9 Nov 2015 00:42:02 +0100 Subject: [PATCH 032/165] Conf: Fixes bug in symbol lookup during reconfiguration Symbol lookup by cf_find_symbol() not only did the lookup but also added new void symbols allocated from cfg_mem linpool, which gets broken when lookups are done outside of config parsing, which may lead to crashes during reconfiguration. The patch separates lookup-only cf_find_symbol() and config-modifying cf_get_symbol(), while the later is called only during parsing. Also new_config and cfg_mem global variables are NULLed outside of parsing. --- conf/cf-lex.l | 64 ++++++++++++++++++++++++++++++--------------- conf/conf.c | 65 +++++++++++++++++++++++++++------------------- conf/conf.h | 4 ++- nest/proto.c | 2 +- nest/rt-roa.c | 2 +- nest/rt-table.c | 4 +-- sysdep/unix/main.c | 2 +- 7 files changed, 90 insertions(+), 53 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 61e04075..5a2a4d6b 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -70,7 +70,7 @@ struct sym_scope { static struct sym_scope *conf_this_scope; static int cf_hash(byte *c); -static struct symbol *cf_find_sym(byte *c, unsigned int h0); +static inline struct symbol * cf_get_sym(byte *c, uint h0); linpool *cfg_mem; @@ -194,7 +194,7 @@ else: { } k=k->next; } - cf_lval.s = cf_find_sym(yytext, h); + cf_lval.s = cf_get_sym(yytext, h); return SYM; } @@ -426,8 +426,9 @@ check_eof(void) } static struct symbol * -cf_new_sym(byte *c, unsigned int h) +cf_new_sym(byte *c, uint h0) { + uint h = h0 & (SYM_HASH_SIZE-1); struct symbol *s, **ht; int l; @@ -449,56 +450,77 @@ cf_new_sym(byte *c, unsigned int h) } static struct symbol * -cf_find_sym(byte *c, unsigned int h0) +cf_find_sym(struct config *cfg, byte *c, uint h0) { - unsigned int h = h0 & (SYM_HASH_SIZE-1); + uint h = h0 & (SYM_HASH_SIZE-1); struct symbol *s, **ht; - if (ht = new_config->sym_hash) + if (ht = cfg->sym_hash) { for(s = ht[h]; s; s=s->next) if (!strcmp(s->name, c) && s->scope->active) return s; } - if (new_config->sym_fallback) + if (ht = cfg->sym_fallback) { /* We know only top-level scope is active */ - for(s = new_config->sym_fallback[h]; s; s=s->next) + for(s = ht[h]; s; s=s->next) if (!strcmp(s->name, c) && s->scope->active) return s; } - return cf_new_sym(c, h); + + return NULL; +} + +static inline struct symbol * +cf_get_sym(byte *c, uint h0) +{ + return cf_find_sym(new_config, c, h0) ?: cf_new_sym(c, h0); } /** * cf_find_symbol - find a symbol by name + * @cfg: specificed config + * @c: symbol name + * + * This functions searches the symbol table in the config @cfg for a symbol of + * given name. First it examines the current scope, then the second recent one + * and so on until it either finds the symbol and returns a pointer to its + * &symbol structure or reaches the end of the scope chain and returns %NULL to + * signify no match. + */ +struct symbol * +cf_find_symbol(struct config *cfg, byte *c) +{ + return cf_find_sym(cfg, c, cf_hash(c)); +} + +/** + * cf_get_symbol - get a symbol by name * @c: symbol name * - * This functions searches the symbol table for a symbol of given - * name. First it examines the current scope, then the second recent - * one and so on until it either finds the symbol and returns a pointer - * to its &symbol structure or reaches the end of the scope chain - * and returns %NULL to signify no match. + * This functions searches the symbol table of the currently parsed config + * (@new_config) for a symbol of given name. It returns either the already + * existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no + * existing symbol is found. */ struct symbol * -cf_find_symbol(byte *c) +cf_get_symbol(byte *c) { - return cf_find_sym(c, cf_hash(c)); + return cf_get_sym(c, cf_hash(c)); } struct symbol * cf_default_name(char *template, int *counter) { - char buf[32]; + char buf[SYM_MAX_LEN]; struct symbol *s; char *perc = strchr(template, '%'); for(;;) { bsprintf(buf, template, ++(*counter)); - s = cf_find_sym(buf, cf_hash(buf)); - if (!s) - break; + s = cf_get_sym(buf, cf_hash(buf)); if (s->class == SYM_VOID) return s; if (!perc) @@ -529,7 +551,7 @@ cf_define_symbol(struct symbol *sym, int type, void *def) { if (sym->scope == conf_this_scope) cf_error("Symbol already defined"); - sym = cf_new_sym(sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1)); + sym = cf_new_sym(sym->name, cf_hash(sym->name)); } sym->class = type; sym->def = def; diff --git a/conf/conf.c b/conf/conf.c index a907402d..825a8e9f 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -20,19 +20,19 @@ * * There can exist up to four different configurations at one time: an active * one (pointed to by @config), configuration we are just switching from - * (@old_config), one queued for the next reconfiguration (@future_config; - * if there is one and the user wants to reconfigure once again, we just - * free the previous queued config and replace it with the new one) and - * finally a config being parsed (@new_config). The stored @old_config - * is also used for undo reconfiguration, which works in a similar way. - * Reconfiguration could also have timeout (using @config_timer) and undo - * is automatically called if the new configuration is not confirmed later. + * (@old_config), one queued for the next reconfiguration (@future_config; if + * there is one and the user wants to reconfigure once again, we just free the + * previous queued config and replace it with the new one) and finally a config + * being parsed (@new_config). The stored @old_config is also used for undo + * reconfiguration, which works in a similar way. Reconfiguration could also + * have timeout (using @config_timer) and undo is automatically called if the + * new configuration is not confirmed later. The new config (@new_config) and + * associated linear pool (@cfg_mem) is non-NULL only during parsing. * - * Loading of new configuration is very simple: just call config_alloc() - * to get a new &config structure, then use config_parse() to parse a - * configuration file and fill all fields of the structure - * and finally ask the config manager to switch to the new - * config by calling config_commit(). + * Loading of new configuration is very simple: just call config_alloc() to get + * a new &config structure, then use config_parse() to parse a configuration + * file and fill all fields of the structure and finally ask the config manager + * to switch to the new config by calling config_commit(). * * CLI commands are parsed in a very similar way -- there is also a stripped-down * &config structure associated with them and they are lex-ed and parsed by the @@ -91,10 +91,15 @@ config_alloc(byte *name) linpool *l = lp_new(p, 4080); struct config *c = lp_allocz(l, sizeof(struct config)); + /* Duplication of name string in local linear pool */ + uint nlen = strlen(name) + 1; + char *ndup = lp_allocu(l, nlen); + memcpy(ndup, name, nlen); + c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */ c->pool = p; - cfg_mem = c->mem = l; - c->file_name = cfg_strdup(name); + c->mem = l; + c->file_name = ndup; c->load_time = now; c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600}; c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0}; @@ -119,11 +124,13 @@ config_alloc(byte *name) int config_parse(struct config *c) { + int done = 0; DBG("Parsing configuration file `%s'\n", c->file_name); new_config = c; cfg_mem = c->mem; if (setjmp(conf_jmpbuf)) - return 0; + goto cleanup; + cf_lex_init(0, c); sysdep_preconfig(c); protos_preconfig(c); @@ -137,7 +144,12 @@ config_parse(struct config *c) if (!c->router_id) cf_error("Router ID must be configured manually on IPv6 routers"); #endif - return 1; + done = 1; + +cleanup: + new_config = NULL; + cfg_mem = NULL; + return done; } /** @@ -150,14 +162,22 @@ config_parse(struct config *c) int cli_parse(struct config *c) { - new_config = c; + int done = 0; c->sym_fallback = config->sym_hash; + new_config = c; cfg_mem = c->mem; if (setjmp(conf_jmpbuf)) - return 0; + goto cleanup; + cf_lex_init(1, c); cf_parse(); - return 1; + done = 1; + +cleanup: + c->sym_fallback = NULL; + new_config = NULL; + cfg_mem = NULL; + return done; } /** @@ -237,10 +257,6 @@ config_do_commit(struct config *c, int type) if (old_config && !config->shutdown) log(L_INFO "Reconfiguring"); - /* This should not be necessary, but it seems there are some - functions that access new_config instead of config */ - new_config = config; - if (old_config) old_config->obstacle_count++; @@ -254,9 +270,6 @@ config_do_commit(struct config *c, int type) DBG("protos_commit\n"); protos_commit(c, old_config, force_restart, type); - /* Just to be sure nobody uses that now */ - new_config = NULL; - int obs = 0; if (old_config) obs = --old_config->obstacle_count; diff --git a/conf/conf.h b/conf/conf.h index 515efbb3..89a2c5b7 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -147,7 +147,9 @@ int cf_lex(void); void cf_lex_init(int is_cli, struct config *c); void cf_lex_unwind(void); -struct symbol *cf_find_symbol(byte *c); +struct symbol *cf_find_symbol(struct config *cfg, byte *c); + +struct symbol *cf_get_symbol(byte *c); struct symbol *cf_default_name(char *template, int *counter); struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def); void cf_push_scope(struct symbol *); diff --git a/nest/proto.c b/nest/proto.c index 6531083c..d04da333 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -521,7 +521,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty WALK_LIST(oc, old->protos) { p = oc->proto; - sym = cf_find_symbol(oc->name); + sym = cf_find_symbol(new, oc->name); if (sym && sym->class == SYM_PROTO && !new->shutdown) { /* Found match, let's check if we can smoothly switch to new configuration */ diff --git a/nest/rt-roa.c b/nest/rt-roa.c index aa453f16..0fd89667 100644 --- a/nest/rt-roa.c +++ b/nest/rt-roa.c @@ -311,7 +311,7 @@ roa_commit(struct config *new, struct config *old) if (old) WALK_LIST(t, roa_table_list) { - struct symbol *sym = cf_find_symbol(t->name); + struct symbol *sym = cf_find_symbol(new, t->name); if (sym && sym->class == SYM_ROA) { /* Found old table in new config */ diff --git a/nest/rt-table.c b/nest/rt-table.c index 9e2c4e0d..2ddff12e 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1663,7 +1663,7 @@ rt_prune_loop(void) void rt_preconfig(struct config *c) { - struct symbol *s = cf_find_symbol("master"); + struct symbol *s = cf_get_symbol("master"); init_list(&c->tables); c->master_rtc = rt_new_table(s); @@ -1903,7 +1903,7 @@ rt_commit(struct config *new, struct config *old) rtable *ot = o->table; if (!ot->deleted) { - struct symbol *sym = cf_find_symbol(o->name); + struct symbol *sym = cf_find_symbol(new, o->name); if (sym && sym->class == SYM_TABLE && !new->shutdown) { DBG("\t%s: same\n", o->name); diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index e31471da..24d34f60 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -96,7 +96,7 @@ drop_gid(gid_t gid) static inline void add_num_const(char *name, int val) { - struct symbol *s = cf_find_symbol(name); + struct symbol *s = cf_get_symbol(name); s->class = SYM_CONSTANT | T_INT; s->def = cfg_allocz(sizeof(struct f_val)); SYM_TYPE(s) = T_INT; From 86b4e17001fe4cca6dde7ff523346121c0ae68fe Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 9 Nov 2015 01:01:12 +0100 Subject: [PATCH 033/165] Nest: Fixes bug in missing cleanup during table removal When a table is removed during reconfiguration, a reference was not cleared in the old configuration, which breaks undo. --- nest/rt-table.c | 1 + 1 file changed, 1 insertion(+) diff --git a/nest/rt-table.c b/nest/rt-table.c index 2ddff12e..10ce400a 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1868,6 +1868,7 @@ rt_unlock_table(rtable *r) { struct config *conf = r->deleted; DBG("Deleting routing table %s\n", r->name); + r->config->table = NULL; if (r->hostcache) rt_free_hostcache(r); rem_node(&r->n); From 9ddbfbddf87462bbf50437bdc1d44499a5c223e7 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 3 Nov 2015 14:42:41 +0100 Subject: [PATCH 034/165] Netlink: Allow more than 256 routing tables. Since 2.6.19, the netlink API defines RTA_TABLE routing attribute to allow 32-bit routing table IDs. Using this attribute to index routing tables at Linux, instead of 8-bit rtm_table field. --- sysdep/bsd/krt-sock.c | 7 +++- sysdep/bsd/krt-sys.h | 1 + sysdep/linux/krt-sys.h | 8 ++-- sysdep/linux/netlink.Y | 2 - sysdep/linux/netlink.c | 90 ++++++++++++++++++++++++++++-------------- sysdep/unix/krt.c | 11 +++++- sysdep/unix/krt.h | 3 +- 7 files changed, 82 insertions(+), 40 deletions(-) diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 064bae18..29203d1b 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -970,13 +970,15 @@ krt_sock_close_shared(void) } } -void +int krt_sys_start(struct krt_proto *p) { krt_table_map[KRT_CF->sys.table_id] = p; krt_sock_open_shared(); p->sys.sk = krt_sock; + + return 1; } void @@ -992,10 +994,11 @@ krt_sys_shutdown(struct krt_proto *p) #else -void +int krt_sys_start(struct krt_proto *p) { p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id); + return 1; } void diff --git a/sysdep/bsd/krt-sys.h b/sysdep/bsd/krt-sys.h index 2c6e35c5..a63f8caf 100644 --- a/sysdep/bsd/krt-sys.h +++ b/sysdep/bsd/krt-sys.h @@ -42,6 +42,7 @@ struct krt_state { }; +static inline void krt_sys_io_init(void) { } static inline void krt_sys_init(struct krt_proto *p UNUSED) { } static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { } diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index e32e4fe1..7fd5f139 100644 --- a/sysdep/linux/krt-sys.h +++ b/sysdep/linux/krt-sys.h @@ -84,18 +84,18 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; } #define EA_KRT_FEATURE_ALLFRAG EA_KRT_FEATURES | EA_BIT(0x3) - -#define NL_NUM_TABLES 256 - struct krt_params { - int table_id; /* Kernel table ID we sync with */ + u32 table_id; /* Kernel table ID we sync with */ }; struct krt_state { + struct krt_proto *hash_next; }; static inline void krt_sys_init(struct krt_proto *p UNUSED) { } +static inline void krt_sys_preconfig(struct config *c UNUSED) { } +static inline void krt_sys_postconfig(struct krt_config *x UNUSED) { } #endif diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index f8137e23..e9c225a2 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -23,8 +23,6 @@ CF_ADDTO(kern_proto, kern_proto kern_sys_item ';') kern_sys_item: KERNEL TABLE expr { - if ($3 <= 0 || $3 >= NL_NUM_TABLES) - cf_error("Kernel routing table number out of range"); THIS_KRT->sys.table_id = $3; } ; diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 674d338b..db998926 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -25,6 +25,7 @@ #include "lib/krt.h" #include "lib/socket.h" #include "lib/string.h" +#include "lib/hash.h" #include "conf/conf.h" #include @@ -32,6 +33,7 @@ #include #include + #ifndef MSG_TRUNC /* Hack: Several versions of glibc miss this one :( */ #define MSG_TRUNC 0x20 #endif @@ -40,6 +42,11 @@ #define IFF_LOWER_UP 0x10000 #endif +#ifndef RTA_TABLE +#define RTA_TABLE 15 +#endif + + /* * Synchronous Netlink interface */ @@ -650,7 +657,23 @@ kif_do_scan(struct kif_proto *p UNUSED) * Routes */ -static struct krt_proto *nl_table_map[NL_NUM_TABLES]; +static inline u32 +krt_table_id(struct krt_proto *p) +{ + return KRT_CF->sys.table_id; +} + +static HASH(struct krt_proto) nl_table_map; + +#define RTH_FN(k) u32_hash(k) +#define RTH_EQ(k1,k2) k1 == k2 +#define RTH_KEY(p) krt_table_id(p) +#define RTH_NEXT(p) p->sys.hash_next + +#define RTH_REHASH rth_rehash +#define RTH_PARAMS /8, *2, 2, 2, 6, 20 + +HASH_DEFINE_REHASH_FN(RTH, struct krt_proto) int krt_capable(rte *e) @@ -708,12 +731,15 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) r.r.rtm_family = BIRD_AF; r.r.rtm_dst_len = net->n.pxlen; - r.r.rtm_tos = 0; - r.r.rtm_table = KRT_CF->sys.table_id; r.r.rtm_protocol = RTPROT_BIRD; r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); + if (krt_table_id(p) < 256) + r.r.rtm_table = krt_table_id(p); + else + nl_add_attr_u32(&r.h, sizeof(r), RTA_TABLE, krt_table_id(p)); + /* For route delete, we do not specify route attributes */ if (!new) return nl_exchange(&r.h); @@ -809,11 +835,12 @@ nl_parse_route(struct nlmsghdr *h, int scan) { struct krt_proto *p; struct rtmsg *i; - struct rtattr *a[RTA_CACHEINFO+1]; + struct rtattr *a[RTA_TABLE+1]; int new = h->nlmsg_type == RTM_NEWROUTE; ip_addr dst = IPA_NONE; u32 oif = ~0; + u32 table; int src; if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a))) @@ -825,6 +852,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) (a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) || #endif (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) || + (a[RTA_TABLE] && RTA_PAYLOAD(a[RTA_TABLE]) != 4) || (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) || (a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) || (a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) || @@ -843,10 +871,15 @@ nl_parse_route(struct nlmsghdr *h, int scan) if (a[RTA_OIF]) oif = rta_get_u32(a[RTA_OIF]); - p = nl_table_map[i->rtm_table]; /* Do we know this table? */ - DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p ? p->p.name : "(none)"); + if (a[RTA_TABLE]) + table = rta_get_u32(a[RTA_TABLE]); + else + table = i->rtm_table; + + p = HASH_FIND(nl_table_map, RTH, table); /* Do we know this table? */ + DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, table, i->rtm_protocol, p ? p->p.name : "(none)"); if (!p) - SKIP("unknown table %d\n", i->rtm_table); + SKIP("unknown table %d\n", table); #ifdef IPV6 @@ -1186,25 +1219,41 @@ nl_open_async(void) bug("Netlink: sk_open failed"); } + /* * Interface to the UNIX krt module */ -static u8 nl_cf_table[(NL_NUM_TABLES+7) / 8]; - void +krt_sys_io_init(void) +{ + HASH_INIT(nl_table_map, krt_pool, 6); +} + +int krt_sys_start(struct krt_proto *p) { - nl_table_map[KRT_CF->sys.table_id] = p; + struct krt_proto *old = HASH_FIND(nl_table_map, RTH, krt_table_id(p)); + + if (old) + { + log(L_ERR "%s: Kernel table %u already registered by %s", + p->p.name, krt_table_id(p), old->p.name); + return 0; + } + + HASH_INSERT2(nl_table_map, RTH, krt_pool, p); nl_open(); nl_open_async(); + + return 1; } void -krt_sys_shutdown(struct krt_proto *p UNUSED) +krt_sys_shutdown(struct krt_proto *p) { - nl_table_map[KRT_CF->sys.table_id] = NULL; + HASH_REMOVE2(nl_table_map, RTH, krt_pool, p); } int @@ -1213,23 +1262,6 @@ krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt return n->sys.table_id == o->sys.table_id; } - -void -krt_sys_preconfig(struct config *c UNUSED) -{ - bzero(&nl_cf_table, sizeof(nl_cf_table)); -} - -void -krt_sys_postconfig(struct krt_config *x) -{ - int id = x->sys.table_id; - - if (nl_cf_table[id/8] & (1 << (id%8))) - cf_error("Multiple kernel syncers defined for table #%d", id); - nl_cf_table[id/8] |= (1 << (id%8)); -} - void krt_sys_init_config(struct krt_config *cf) { diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 2eab5cb2..49bf9519 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -77,6 +77,7 @@ krt_io_init(void) krt_pool = rp_new(&root_pool, "Kernel Syncer"); krt_filter_lp = lp_new(krt_pool, 4080); init_list(&krt_proto_list); + krt_sys_io_init(); } /* @@ -1126,7 +1127,11 @@ krt_start(struct proto *P) krt_learn_init(p); #endif - krt_sys_start(p); + if (!krt_sys_start(p)) + { + rem_node(&p->krt_node); + return PS_START; + } krt_scan_timer_start(p); @@ -1150,8 +1155,10 @@ krt_shutdown(struct proto *P) p->ready = 0; p->initialized = 0; - krt_sys_shutdown(p); + if (p->p.proto_state == PS_START) + return PS_DOWN; + krt_sys_shutdown(p); rem_node(&p->krt_node); return PS_DOWN; diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index 9d5d4e8c..aea20102 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -119,8 +119,9 @@ struct proto_config * krt_init_config(int class); /* krt sysdep */ +void krt_sys_io_init(void); void krt_sys_init(struct krt_proto *); -void krt_sys_start(struct krt_proto *); +int krt_sys_start(struct krt_proto *); void krt_sys_shutdown(struct krt_proto *); int krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o); From fce764f90e8331d1adb6a85ec00136dfeae1a398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Mon, 9 Nov 2015 09:14:26 +0100 Subject: [PATCH 035/165] Fix compiling with --enable-debug option --- sysdep/linux/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index db998926..f2f60100 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -246,7 +246,7 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) return 1; } -static inline ip4_addr rta_get_u32(struct rtattr *a) +static inline u32 rta_get_u32(struct rtattr *a) { return *(u32 *) RTA_DATA(a); } static inline ip4_addr rta_get_ip4(struct rtattr *a) From 75ff08022ea58fe3efa96639f080ce375e997675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:10:33 +0100 Subject: [PATCH 036/165] Add get_u64() and put_u64() into lib/unaligned.h --- lib/unaligned.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/unaligned.h b/lib/unaligned.h index af655204..a2dbae4f 100644 --- a/lib/unaligned.h +++ b/lib/unaligned.h @@ -35,6 +35,15 @@ get_u32(void *p) return ntohl(x); } +static inline u64 +get_u64(const void *p) +{ + u32 xh, xl; + memcpy(&xh, p, 4); + memcpy(&xl, p+4, 4); + return (((u64) ntohl(xh)) << 32) | ntohl(xl); +} + static inline void put_u16(void *p, u16 x) { @@ -49,4 +58,14 @@ put_u32(void *p, u32 x) memcpy(p, &x, 4); } +static inline void +put_u64(void *p, u64 x) +{ + u32 xh, xl; + xh = htonl(x >> 32); + xl = htonl((u32) x); + memcpy(p, &xh, 4); + memcpy(p+4, &xl, 4); +} + #endif From 5d0c36f1da83b2a2a07e043247410948d90c600e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:11:51 +0100 Subject: [PATCH 037/165] Add SHA1 and SHA1-HMAC crypto hash --- lib/Modules | 2 + lib/sha1.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sha1.h | 86 +++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 lib/sha1.c create mode 100644 lib/sha1.h diff --git a/lib/Modules b/lib/Modules index 7254df2d..a227f9a1 100644 --- a/lib/Modules +++ b/lib/Modules @@ -1,3 +1,5 @@ +sha1.c +sha1.h birdlib.h bitops.c bitops.h diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 00000000..dd29b5fd --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,342 @@ +/* + * BIRD Library -- SHA-1 Hash Function (FIPS 180-1, RFC 3174) and HMAC-SHA-1 + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libucw-6.4 + * (c) 2008--2009 Martin Mares + * + * Based on the code from libgcrypt-1.2.3, which is + * (c) 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "lib/sha1.h" +#include "lib/unaligned.h" + +void +sha1_init(struct sha1_context *hd) +{ + hd->h0 = 0x67452301; + hd->h1 = 0xefcdab89; + hd->h2 = 0x98badcfe; + hd->h3 = 0x10325476; + hd->h4 = 0xc3d2e1f0; + hd->nblocks = 0; + hd->count = 0; +} + +/* + * Transform the message X which consists of 16 32-bit-words + */ +static void +sha1_transform(struct sha1_context *hd, const byte *data) +{ + u32 a,b,c,d,e,tm; + u32 x[16]; + + /* Get values from the chaining vars. */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + +#ifdef CPU_BIG_ENDIAN + memcpy(x, data, 64); +#else + int i; + for (i = 0; i < 16; i++) + x[i] = get_u32(data+4*i); +#endif + +#define K1 0x5A827999L +#define K2 0x6ED9EBA1L +#define K3 0x8F1BBCDCL +#define K4 0xCA62C1D6L +#define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) +#define F2(x,y,z) ( x ^ y ^ z ) +#define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) +#define F4(x,y,z) ( x ^ y ^ z ) + +#define M(i) (tm = x[i&0x0f] ^ x[(i-14)&0x0f] ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f], (x[i&0x0f] = ROL(tm, 1))) + +/* Bitwise rotation of an unsigned int to the left **/ +#define ROL(x, bits) (((x) << (bits)) | ((uint)(x) >> (sizeof(uint)*8 - (bits)))) + + #define R(a, b, c, d, e, f, k, m) \ + do \ + { \ + e += ROL(a, 5) + f(b, c, d) + k + m; \ + b = ROL( b, 30 ); \ + } while(0) + + R( a, b, c, d, e, F1, K1, x[ 0] ); + R( e, a, b, c, d, F1, K1, x[ 1] ); + R( d, e, a, b, c, F1, K1, x[ 2] ); + R( c, d, e, a, b, F1, K1, x[ 3] ); + R( b, c, d, e, a, F1, K1, x[ 4] ); + R( a, b, c, d, e, F1, K1, x[ 5] ); + R( e, a, b, c, d, F1, K1, x[ 6] ); + R( d, e, a, b, c, F1, K1, x[ 7] ); + R( c, d, e, a, b, F1, K1, x[ 8] ); + R( b, c, d, e, a, F1, K1, x[ 9] ); + R( a, b, c, d, e, F1, K1, x[10] ); + R( e, a, b, c, d, F1, K1, x[11] ); + R( d, e, a, b, c, F1, K1, x[12] ); + R( c, d, e, a, b, F1, K1, x[13] ); + R( b, c, d, e, a, F1, K1, x[14] ); + R( a, b, c, d, e, F1, K1, x[15] ); + R( e, a, b, c, d, F1, K1, M(16) ); + R( d, e, a, b, c, F1, K1, M(17) ); + R( c, d, e, a, b, F1, K1, M(18) ); + R( b, c, d, e, a, F1, K1, M(19) ); + R( a, b, c, d, e, F2, K2, M(20) ); + R( e, a, b, c, d, F2, K2, M(21) ); + R( d, e, a, b, c, F2, K2, M(22) ); + R( c, d, e, a, b, F2, K2, M(23) ); + R( b, c, d, e, a, F2, K2, M(24) ); + R( a, b, c, d, e, F2, K2, M(25) ); + R( e, a, b, c, d, F2, K2, M(26) ); + R( d, e, a, b, c, F2, K2, M(27) ); + R( c, d, e, a, b, F2, K2, M(28) ); + R( b, c, d, e, a, F2, K2, M(29) ); + R( a, b, c, d, e, F2, K2, M(30) ); + R( e, a, b, c, d, F2, K2, M(31) ); + R( d, e, a, b, c, F2, K2, M(32) ); + R( c, d, e, a, b, F2, K2, M(33) ); + R( b, c, d, e, a, F2, K2, M(34) ); + R( a, b, c, d, e, F2, K2, M(35) ); + R( e, a, b, c, d, F2, K2, M(36) ); + R( d, e, a, b, c, F2, K2, M(37) ); + R( c, d, e, a, b, F2, K2, M(38) ); + R( b, c, d, e, a, F2, K2, M(39) ); + R( a, b, c, d, e, F3, K3, M(40) ); + R( e, a, b, c, d, F3, K3, M(41) ); + R( d, e, a, b, c, F3, K3, M(42) ); + R( c, d, e, a, b, F3, K3, M(43) ); + R( b, c, d, e, a, F3, K3, M(44) ); + R( a, b, c, d, e, F3, K3, M(45) ); + R( e, a, b, c, d, F3, K3, M(46) ); + R( d, e, a, b, c, F3, K3, M(47) ); + R( c, d, e, a, b, F3, K3, M(48) ); + R( b, c, d, e, a, F3, K3, M(49) ); + R( a, b, c, d, e, F3, K3, M(50) ); + R( e, a, b, c, d, F3, K3, M(51) ); + R( d, e, a, b, c, F3, K3, M(52) ); + R( c, d, e, a, b, F3, K3, M(53) ); + R( b, c, d, e, a, F3, K3, M(54) ); + R( a, b, c, d, e, F3, K3, M(55) ); + R( e, a, b, c, d, F3, K3, M(56) ); + R( d, e, a, b, c, F3, K3, M(57) ); + R( c, d, e, a, b, F3, K3, M(58) ); + R( b, c, d, e, a, F3, K3, M(59) ); + R( a, b, c, d, e, F4, K4, M(60) ); + R( e, a, b, c, d, F4, K4, M(61) ); + R( d, e, a, b, c, F4, K4, M(62) ); + R( c, d, e, a, b, F4, K4, M(63) ); + R( b, c, d, e, a, F4, K4, M(64) ); + R( a, b, c, d, e, F4, K4, M(65) ); + R( e, a, b, c, d, F4, K4, M(66) ); + R( d, e, a, b, c, F4, K4, M(67) ); + R( c, d, e, a, b, F4, K4, M(68) ); + R( b, c, d, e, a, F4, K4, M(69) ); + R( a, b, c, d, e, F4, K4, M(70) ); + R( e, a, b, c, d, F4, K4, M(71) ); + R( d, e, a, b, c, F4, K4, M(72) ); + R( c, d, e, a, b, F4, K4, M(73) ); + R( b, c, d, e, a, F4, K4, M(74) ); + R( a, b, c, d, e, F4, K4, M(75) ); + R( e, a, b, c, d, F4, K4, M(76) ); + R( d, e, a, b, c, F4, K4, M(77) ); + R( c, d, e, a, b, F4, K4, M(78) ); + R( b, c, d, e, a, F4, K4, M(79) ); + + /* Update chaining vars. */ + hd->h0 += a; + hd->h1 += b; + hd->h2 += c; + hd->h3 += d; + hd->h4 += e; +} + +/* + * Update the message digest with the contents + * of INBUF with length INLEN. + */ +void +sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen) +{ + if (hd->count == 64) /* flush the buffer */ + { + sha1_transform(hd, hd->buf); + hd->count = 0; + hd->nblocks++; + } + if (!inbuf) + return; + + if (hd->count) + { + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; + sha1_update( hd, NULL, 0 ); + if(!inlen) + return; + } + + while (inlen >= 64) + { + sha1_transform(hd, inbuf); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; +} + +/* + * The routine final terminates the computation and + * returns the digest. + * The handle is prepared for a new cycle, but adding bytes to the + * handle will the destroy the returned buffer. + * Returns: 20 bytes representing the digest. + */ +byte * +sha1_final(struct sha1_context *hd) +{ + u32 t, msb, lsb; + u32 *p; + + sha1_update(hd, NULL, 0); /* flush */; + + t = hd->nblocks; + /* multiply by 64 to make a byte count */ + lsb = t << 6; + msb = t >> 26; + /* add the count */ + t = lsb; + if ((lsb += hd->count) < t) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 29; + + if (hd->count < 56) /* enough room */ + { + hd->buf[hd->count++] = 0x80; /* pad */ + while (hd->count < 56) + hd->buf[hd->count++] = 0; /* pad */ + } + else /* need one extra block */ + { + hd->buf[hd->count++] = 0x80; /* pad character */ + while (hd->count < 64) + hd->buf[hd->count++] = 0; + sha1_update(hd, NULL, 0); /* flush */; + memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + hd->buf[56] = msb >> 24; + hd->buf[57] = msb >> 16; + hd->buf[58] = msb >> 8; + hd->buf[59] = msb ; + hd->buf[60] = lsb >> 24; + hd->buf[61] = lsb >> 16; + hd->buf[62] = lsb >> 8; + hd->buf[63] = lsb ; + sha1_transform(hd, hd->buf); + + p = (u32*) hd->buf; +#define X(a) do { put_u32(p, hd->h##a); p++; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); +#undef X + + return hd->buf; +} + + +/* + * SHA1-HMAC + */ + +/* + * Shortcut function which puts the hash value of the supplied buffer + * into outbuf which must have a size of 20 bytes. + */ +void +sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length) +{ + struct sha1_context ctx; + + sha1_init(&ctx); + sha1_update(&ctx, buffer, length); + memcpy(outbuf, sha1_final(&ctx), SHA1_SIZE); +} + +void +sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen) +{ + byte keybuf[SHA1_BLOCK_SIZE], buf[SHA1_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA1_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA1_BLOCK_SIZE - keylen); + } + else + { + sha1_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA1_SIZE, SHA1_BLOCK_SIZE - SHA1_SIZE); + } + + /* Initialize the inner digest */ + sha1_init(&ctx->ictx); + int i; + for (i = 0; i < SHA1_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha1_update(&ctx->ictx, buf, SHA1_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha1_init(&ctx->octx); + for (i = 0; i < SHA1_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha1_update(&ctx->octx, buf, SHA1_BLOCK_SIZE); +} + +void +sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen) +{ + /* Just update the inner digest */ + sha1_update(&ctx->ictx, data, datalen); +} + +byte *sha1_hmac_final(struct sha1_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha1_final(&ctx->ictx); + + /* Finish the outer digest */ + sha1_update(&ctx->octx, isha, SHA1_SIZE); + return sha1_final(&ctx->octx); +} + +void +sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen) +{ + struct sha1_hmac_context hd; + sha1_hmac_init(&hd, key, keylen); + sha1_hmac_update(&hd, data, datalen); + byte *osha = sha1_hmac_final(&hd); + memcpy(outbuf, osha, SHA1_SIZE); +} diff --git a/lib/sha1.h b/lib/sha1.h new file mode 100644 index 00000000..425160a0 --- /dev/null +++ b/lib/sha1.h @@ -0,0 +1,86 @@ +/* + * BIRD Library -- SHA-1 Hash Function (FIPS 180-1, RFC 3174) and HMAC-SHA-1 + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libucw-6.4 + * (c) 2008--2009 Martin Mares + * + * Based on the code from libgcrypt-1.2.3, which is + * (c) 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_SHA1_H_ +#define _BIRD_SHA1_H_ + +#include "nest/bird.h" + +/* + * Internal SHA1 state. + * You should use it just as an opaque handle only. + */ +struct sha1_context { + u32 h0,h1,h2,h3,h4; + u32 nblocks; + byte buf[64]; + int count; +} ; + +void sha1_init(struct sha1_context *hd); /* Initialize new algorithm run in the @hd context. **/ +/* + * Push another @inlen bytes of data pointed to by @inbuf onto the + * SHA1 hash currently in @hd. You can call this any times you want on + * the same hash (and you do not need to reinitialize it by + * @sha1_init()). It has the same effect as concatenating all the data + * together and passing them at once. + */ +void sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen); +/* + * No more @sha1_update() calls will be done. This terminates the hash + * and returns a pointer to it. + * + * Note that the pointer points into data in the @hd context. If it ceases + * to exist, the pointer becomes invalid. + * + * To convert the hash to its usual hexadecimal representation, see + * <>. + */ +byte *sha1_final(struct sha1_context *hd); + +/* + * A convenience one-shot function for SHA1 hash. + * It is equivalent to this snippet of code: + * + * sha1_context hd; + * sha1_init(&hd); + * sha1_update(&hd, buffer, length); + * memcpy(outbuf, sha1_final(&hd), SHA1_SIZE); + */ +void sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length); + +/* + * SHA1 HMAC message authentication. If you provide @key and @data, + * the result will be stored in @outbuf. + */ +void sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen); + +/* + * The HMAC also exists in a stream version in a way analogous to the + * plain SHA1. Pass this as a context. + */ +struct sha1_hmac_context { + struct sha1_context ictx; + struct sha1_context octx; +}; + +void sha1_hmac_init(struct sha1_hmac_context *hd, const byte *key, uint keylen); /* Initialize HMAC with context @hd and the given key. See sha1_init(). */ +void sha1_hmac_update(struct sha1_hmac_context *hd, const byte *data, uint datalen); /* Hash another @datalen bytes of data. See sha1_update(). */ +byte *sha1_hmac_final(struct sha1_hmac_context *hd); /* Terminate the HMAC and return a pointer to the allocated hash. See sha1_final(). */ + +#define SHA1_SIZE 20 /* Size of the SHA1 hash in its binary representation **/ +#define SHA1_HEX_SIZE 41 /* Buffer length for a string containing SHA1 in hexadecimal format. **/ +#define SHA1_BLOCK_SIZE 64 /* SHA1 splits input to blocks of this size. **/ + +#endif /* _BIRD_SHA1_H_ */ From 4035e0e79c609bde30a2f755eec864771ea08e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:13:15 +0100 Subject: [PATCH 038/165] Add SHA-224/256 and HMAC-SHA-224/256 crypto hash --- lib/Modules | 2 + lib/sha256.c | 467 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sha256.h | 71 ++++++++ 3 files changed, 540 insertions(+) create mode 100644 lib/sha256.c create mode 100644 lib/sha256.h diff --git a/lib/Modules b/lib/Modules index a227f9a1..b3daf25d 100644 --- a/lib/Modules +++ b/lib/Modules @@ -1,3 +1,5 @@ +sha256.c +sha256.h sha1.c sha1.h birdlib.h diff --git a/lib/sha256.c b/lib/sha256.c new file mode 100644 index 00000000..2d979f90 --- /dev/null +++ b/lib/sha256.c @@ -0,0 +1,467 @@ +/* + * BIRD Library -- SHA-256 and SHA-224 Hash Functions, + * HMAC-SHA-256 and HMAC-SHA-224 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "lib/sha256.h" +#include "lib/unaligned.h" + +static uint sha256_transform(void *ctx, const byte *data, size_t nblks); + +void +sha256_init(struct sha256_context *ctx) +{ + ctx->h0 = 0x6a09e667; + ctx->h1 = 0xbb67ae85; + ctx->h2 = 0x3c6ef372; + ctx->h3 = 0xa54ff53a; + ctx->h4 = 0x510e527f; + ctx->h5 = 0x9b05688c; + ctx->h6 = 0x1f83d9ab; + ctx->h7 = 0x5be0cd19; + + ctx->nblocks = 0; + ctx->nblocks_high = 0; + ctx->count = 0; + ctx->blocksize = 64; + ctx->transform = sha256_transform; +} + +void +sha224_init(struct sha224_context *ctx) +{ + ctx->h0 = 0xc1059ed8; + ctx->h1 = 0x367cd507; + ctx->h2 = 0x3070dd17; + ctx->h3 = 0xf70e5939; + ctx->h4 = 0xffc00b31; + ctx->h5 = 0x68581511; + ctx->h6 = 0x64f98fa7; + ctx->h7 = 0xbefa4fa4; + + ctx->nblocks = 0; + ctx->nblocks_high = 0; + ctx->count = 0; + ctx->blocksize = 64; + ctx->transform = sha256_transform; +} + +/* (4.2) same as SHA-1's F1. */ +static inline u32 +f1(u32 x, u32 y, u32 z) +{ + return (z ^ (x & (y ^ z))); +} + +/* (4.3) same as SHA-1's F3 */ +static inline u32 +f3(u32 x, u32 y, u32 z) +{ + return ((x & y) | (z & (x|y))); +} + +/* Bitwise rotation of an uint to the right */ +static inline u32 ror(u32 x, int n) +{ + return ( (x >> (n&(32-1))) | (x << ((32-n)&(32-1))) ); +} + +/* (4.4) */ +static inline u32 +sum0(u32 x) +{ + return (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22)); +} + +/* (4.5) */ +static inline u32 +sum1(u32 x) +{ + return (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25)); +} + +/* + Transform the message X which consists of 16 32-bit-words. See FIPS + 180-2 for details. */ +#define S0(x) (ror((x), 7) ^ ror((x), 18) ^ ((x) >> 3)) /* (4.6) */ +#define S1(x) (ror((x), 17) ^ ror((x), 19) ^ ((x) >> 10)) /* (4.7) */ +#define R(a,b,c,d,e,f,g,h,k,w) \ + do \ + { \ + t1 = (h) + sum1((e)) + f1((e),(f),(g)) + (k) + (w); \ + t2 = sum0((a)) + f3((a),(b),(c)); \ + h = g; \ + g = f; \ + f = e; \ + e = d + t1; \ + d = c; \ + c = b; \ + b = a; \ + a = t1 + t2; \ + } while (0) + +/* + The SHA-256 core: Transform the message X which consists of 16 + 32-bit-words. See FIPS 180-2 for details. + */ +static uint +sha256_transform_block(struct sha256_context *ctx, const byte *data) +{ + static const u32 K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + u32 a,b,c,d,e,f,g,h,t1,t2; + u32 w[64]; + int i; + + a = ctx->h0; + b = ctx->h1; + c = ctx->h2; + d = ctx->h3; + e = ctx->h4; + f = ctx->h5; + g = ctx->h6; + h = ctx->h7; + + for (i = 0; i < 16; i++) + w[i] = get_u32(data + i * 4); + for (; i < 64; i++) + w[i] = S1(w[i-2]) + w[i-7] + S0(w[i-15]) + w[i-16]; + + for (i = 0; i < 64;) + { + t1 = h + sum1(e) + f1(e, f, g) + K[i] + w[i]; + t2 = sum0 (a) + f3(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + sum1(d) + f1(d, e, f) + K[i+1] + w[i+1]; + t2 = sum0 (h) + f3(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + sum1(c) + f1(c, d, e) + K[i+2] + w[i+2]; + t2 = sum0 (g) + f3(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + sum1(b) + f1(b, c, d) + K[i+3] + w[i+3]; + t2 = sum0 (f) + f3(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + sum1(a) + f1(a, b, c) + K[i+4] + w[i+4]; + t2 = sum0 (e) + f3(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + sum1(h) + f1(h, a, b) + K[i+5] + w[i+5]; + t2 = sum0 (d) + f3(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + sum1(g) + f1(g, h, a) + K[i+6] + w[i+6]; + t2 = sum0 (c) + f3(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + sum1(f) + f1(f, g, h) + K[i+7] + w[i+7]; + t2 = sum0 (b) + f3(b, c, d); + e += t1; + a = t1 + t2; + + i += 8; + } + + ctx->h0 += a; + ctx->h1 += b; + ctx->h2 += c; + ctx->h3 += d; + ctx->h4 += e; + ctx->h5 += f; + ctx->h6 += g; + ctx->h7 += h; + + return /*burn_stack*/ 74*4+32; +} +#undef S0 +#undef S1 +#undef R + +static uint +sha256_transform(void *ctx, const byte *data, size_t nblks) +{ + struct sha256_context *hd = ctx; + uint burn; + + do + { + burn = sha256_transform_block(hd, data); + data += 64; + } + while (--nblks); + + return burn; +} + +/* Common function to write a chunk of data to the transform function + of a hash algorithm. Note that the use of the term "block" does + not imply a fixed size block. Note that we explicitly allow to use + this function after the context has been finalized; the result does + not have any meaning but writing after finalize is sometimes + helpful to mitigate timing attacks. */ +void +sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len) +{ + const uint blocksize = ctx->blocksize; + size_t inblocks; + + if (sizeof(ctx->buf) < blocksize) + debug("BUG: in file %s at line %d", __FILE__ , __LINE__); + + if (ctx->count == blocksize) /* Flush the buffer. */ + { + ctx->transform(ctx, ctx->buf, 1); + ctx->count = 0; + if (!++ctx->nblocks) + ctx->nblocks_high++; + } + if (!in_buf) + return; + + if (ctx->count) + { + for (; in_len && ctx->count < blocksize; in_len--) + ctx->buf[ctx->count++] = *in_buf++; + sha256_update(ctx, NULL, 0); + if (!in_len) + return; + } + + if (in_len >= blocksize) + { + inblocks = in_len / blocksize; + ctx->transform(ctx, in_buf, inblocks); + ctx->count = 0; + ctx->nblocks_high += (ctx->nblocks + inblocks < inblocks); + ctx->nblocks += inblocks; + in_len -= inblocks * blocksize; + in_buf += inblocks * blocksize; + } + for (; in_len && ctx->count < blocksize; in_len--) + ctx->buf[ctx->count++] = *in_buf++; +} + +/* + The routine finally terminates the computation and returns the + digest. The handle is prepared for a new cycle, but adding bytes + to the handle will the destroy the returned buffer. Returns: 32 + bytes with the message the digest. */ +byte* +sha256_final(struct sha256_context *ctx) +{ + u32 t, th, msb, lsb; + byte *p; + + sha256_update(ctx, NULL, 0); /* flush */; + + t = ctx->nblocks; + if (sizeof t == sizeof ctx->nblocks) + th = ctx->nblocks_high; + else + th = 0; + + /* multiply by 64 to make a byte count */ + lsb = t << 6; + msb = (th << 6) | (t >> 26); + /* add the count */ + t = lsb; + if ((lsb += ctx->count) < t) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 29; + + if (ctx->count < 56) + { /* enough room */ + ctx->buf[ctx->count++] = 0x80; /* pad */ + while (ctx->count < 56) + ctx->buf[ctx->count++] = 0; /* pad */ + } + else + { /* need one extra block */ + ctx->buf[ctx->count++] = 0x80; /* pad character */ + while (ctx->count < 64) + ctx->buf[ctx->count++] = 0; + sha256_update(ctx, NULL, 0); /* flush */; + memset (ctx->buf, 0, 56 ); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + put_u32(ctx->buf + 56, msb); + put_u32(ctx->buf + 60, lsb); + sha256_transform(ctx, ctx->buf, 1); + + p = ctx->buf; + +#define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); + X(5); + X(6); + X(7); +#undef X + + return ctx->buf; +} + + +/* + * SHA256-HMAC + */ + +static void +sha256_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha256_context hd_tmp; + + sha256_init(&hd_tmp); + sha256_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha256_final(&hd_tmp), SHA256_SIZE); +} + +void +sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA256_BLOCK_SIZE], buf[SHA256_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA256_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA256_BLOCK_SIZE - keylen); + } + else + { + sha256_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA256_SIZE, SHA256_BLOCK_SIZE - SHA256_SIZE); + } + + /* Initialize the inner digest */ + sha256_init(&ctx->ictx); + int i; + for (i = 0; i < SHA256_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha256_update(&ctx->ictx, buf, SHA256_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha256_init(&ctx->octx); + for (i = 0; i < SHA256_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha256_update(&ctx->octx, buf, SHA256_BLOCK_SIZE); +} + +void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha256_update(&ctx->ictx, buf, buflen); +} + +byte *sha256_hmac_final(struct sha256_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha256_final(&ctx->ictx); + + /* Finish the outer digest */ + sha256_update(&ctx->octx, isha, SHA256_SIZE); + return sha256_final(&ctx->octx); +} + + +/* + * SHA224-HMAC + */ + +static void +sha224_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha224_context hd_tmp; + + sha224_init(&hd_tmp); + sha224_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha224_final(&hd_tmp), SHA224_SIZE); +} + +void +sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA224_BLOCK_SIZE], buf[SHA224_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA224_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA224_BLOCK_SIZE - keylen); + } + else + { + sha224_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA224_SIZE, SHA224_BLOCK_SIZE - SHA224_SIZE); + } + + /* Initialize the inner digest */ + sha224_init(&ctx->ictx); + int i; + for (i = 0; i < SHA224_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha224_update(&ctx->ictx, buf, SHA224_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha224_init(&ctx->octx); + for (i = 0; i < SHA224_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha224_update(&ctx->octx, buf, SHA224_BLOCK_SIZE); +} + +void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha256_update(&ctx->ictx, buf, buflen); +} + +byte *sha224_hmac_final(struct sha224_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha224_final(&ctx->ictx); + + /* Finish the outer digest */ + sha224_update(&ctx->octx, isha, SHA224_SIZE); + return sha224_final(&ctx->octx); +} diff --git a/lib/sha256.h b/lib/sha256.h new file mode 100644 index 00000000..848d2176 --- /dev/null +++ b/lib/sha256.h @@ -0,0 +1,71 @@ +/* + * BIRD Library -- SHA-256 and SHA-224 Hash Functions, + * HMAC-SHA-256 and HMAC-SHA-224 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_SHA256_H_ +#define _BIRD_SHA256_H_ + +#include "nest/bird.h" + +#define SHA224_SIZE 28 +#define SHA224_HEX_SIZE 57 +#define SHA224_BLOCK_SIZE 64 + +#define SHA256_SIZE 32 +#define SHA256_HEX_SIZE 65 +#define SHA256_BLOCK_SIZE 64 + +struct sha256_context { + u32 h0,h1,h2,h3,h4,h5,h6,h7; + byte buf[128]; /* 128 is for SHA384 and SHA512 support, otherwise for SHA224 and SHA256 is 64 enough */ + u32 nblocks; + u32 nblocks_high; + int count; + u32 blocksize; + uint (*transform)(void *c, const byte *blks, size_t nblks); +}; +#define sha224_context sha256_context /* aliasing 'struct sha224_context' to 'struct sha256_context' */ + +void sha256_init(struct sha256_context *ctx); +void sha224_init(struct sha224_context *ctx); + +void sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len); +static inline void sha224_update(struct sha224_context *ctx, const byte *in_buf, size_t in_len) +{ + sha256_update(ctx, in_buf, in_len); +} + +byte* sha256_final(struct sha256_context *ctx); +static inline byte* sha224_final(struct sha224_context *ctx) +{ + return sha256_final(ctx); +} + +/* + * HMAC-SHA256, HMAC-SHA224 + */ +struct sha256_hmac_context +{ + struct sha256_context ictx; + struct sha256_context octx; +}; +#define sha224_hmac_context sha256_hmac_context /* aliasing 'struct sha224_hmac_context' to 'struct sha256_hmac_context' */ + +void sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen); +void sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen); + +void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen); +void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen); + +byte *sha256_hmac_final(struct sha256_hmac_context *ctx); +byte *sha224_hmac_final(struct sha224_hmac_context *ctx); + +#endif /* _BIRD_SHA256_H_ */ From f312a837e919c660884ceb9c50c106df1e4c0658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:14:36 +0100 Subject: [PATCH 039/165] Add SHA-384/512 and HMAC-SHA-384/512 crypto hash --- lib/Modules | 2 + lib/sha512.c | 614 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sha512.h | 73 ++++++ 3 files changed, 689 insertions(+) create mode 100644 lib/sha512.c create mode 100644 lib/sha512.h diff --git a/lib/Modules b/lib/Modules index b3daf25d..745306d9 100644 --- a/lib/Modules +++ b/lib/Modules @@ -1,5 +1,7 @@ sha256.c sha256.h +sha512.c +sha512.h sha1.c sha1.h birdlib.h diff --git a/lib/sha512.c b/lib/sha512.c new file mode 100644 index 00000000..e46e4c98 --- /dev/null +++ b/lib/sha512.c @@ -0,0 +1,614 @@ +/* + * BIRD Library -- SHA-512 and SHA-384 Hash Functions, + * HMAC-SHA-512 and HMAC-SHA-384 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "lib/sha256.h" +#include "lib/sha512.h" +#include "lib/unaligned.h" + +static uint sha512_transform(void *context, const byte *data, size_t nblks); + +void +sha512_init(struct sha512_context *ctx) +{ + struct sha512_state *hd = &ctx->state; + + hd->h0 = UINT64_C(0x6a09e667f3bcc908); + hd->h1 = UINT64_C(0xbb67ae8584caa73b); + hd->h2 = UINT64_C(0x3c6ef372fe94f82b); + hd->h3 = UINT64_C(0xa54ff53a5f1d36f1); + hd->h4 = UINT64_C(0x510e527fade682d1); + hd->h5 = UINT64_C(0x9b05688c2b3e6c1f); + hd->h6 = UINT64_C(0x1f83d9abfb41bd6b); + hd->h7 = UINT64_C(0x5be0cd19137e2179); + + ctx->bctx.nblocks = 0; + ctx->bctx.nblocks_high = 0; + ctx->bctx.count = 0; + ctx->bctx.blocksize = 128; + ctx->bctx.transform = sha512_transform; +} + +void +sha384_init(struct sha384_context *ctx) +{ + struct sha512_state *hd = &ctx->state; + + hd->h0 = UINT64_C(0xcbbb9d5dc1059ed8); + hd->h1 = UINT64_C(0x629a292a367cd507); + hd->h2 = UINT64_C(0x9159015a3070dd17); + hd->h3 = UINT64_C(0x152fecd8f70e5939); + hd->h4 = UINT64_C(0x67332667ffc00b31); + hd->h5 = UINT64_C(0x8eb44a8768581511); + hd->h6 = UINT64_C(0xdb0c2e0d64f98fa7); + hd->h7 = UINT64_C(0x47b5481dbefa4fa4); + + ctx->bctx.nblocks = 0; + ctx->bctx.nblocks_high = 0; + ctx->bctx.count = 0; + ctx->bctx.blocksize = 128; + ctx->bctx.transform = sha512_transform; +} + +void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len) +{ + sha256_update(&ctx->bctx, in_buf, in_len); +} + +static inline u64 +ROTR(u64 x, u64 n) +{ + return ((x >> n) | (x << (64 - n))); +} + +static inline u64 +Ch(u64 x, u64 y, u64 z) +{ + return ((x & y) ^ ( ~x & z)); +} + +static inline u64 +Maj(u64 x, u64 y, u64 z) +{ + return ((x & y) ^ (x & z) ^ (y & z)); +} + +static inline u64 +Sum0(u64 x) +{ + return (ROTR (x, 28) ^ ROTR (x, 34) ^ ROTR (x, 39)); +} + +static inline u64 +Sum1 (u64 x) +{ + return (ROTR (x, 14) ^ ROTR (x, 18) ^ ROTR (x, 41)); +} + +static const u64 k[] = +{ + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +/* + * Transform the message W which consists of 16 64-bit-words + */ +static uint +sha512_transform_block(struct sha512_state *hd, const byte *data) +{ + u64 a, b, c, d, e, f, g, h; + u64 w[16]; + int t; + + /* get values from the chaining vars */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + f = hd->h5; + g = hd->h6; + h = hd->h7; + + for ( t = 0; t < 16; t++ ) + w[t] = get_u64(data + t * 8); + +#define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7)) +#define S1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6)) + + for (t = 0; t < 80 - 16; ) + { + u64 t1, t2; + + /* Performance on a AMD Athlon(tm) Dual Core Processor 4050e + with gcc 4.3.3 using gcry_md_hash_buffer of each 10000 bytes + initialized to 0,1,2,3...255,0,... and 1000 iterations: + + Not unrolled with macros: 440ms + Unrolled with macros: 350ms + Unrolled with inline: 330ms + */ +#if 0 /* Not unrolled. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; + w[t%16] += S1 (w[(t - 2)%16]) + w[(t - 7)%16] + S0 (w[(t - 15)%16]); + t2 = Sum0 (a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; +#else /* Unrolled to interweave the chain variables. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; + w[0] += S1 (w[14]) + w[9] + S0 (w[1]); + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; + w[1] += S1 (w[15]) + w[10] + S0 (w[2]); + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; + w[2] += S1 (w[0]) + w[11] + S0 (w[3]); + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; + w[3] += S1 (w[1]) + w[12] + S0 (w[4]); + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; + w[4] += S1 (w[2]) + w[13] + S0 (w[5]); + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; + w[5] += S1 (w[3]) + w[14] + S0 (w[6]); + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; + w[6] += S1 (w[4]) + w[15] + S0 (w[7]); + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; + w[7] += S1 (w[5]) + w[0] + S0 (w[8]); + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; + w[8] += S1 (w[6]) + w[1] + S0 (w[9]); + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; + w[9] += S1 (w[7]) + w[2] + S0 (w[10]); + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; + w[10] += S1 (w[8]) + w[3] + S0 (w[11]); + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; + w[11] += S1 (w[9]) + w[4] + S0 (w[12]); + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; + w[12] += S1 (w[10]) + w[5] + S0 (w[13]); + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; + w[13] += S1 (w[11]) + w[6] + S0 (w[14]); + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; + w[14] += S1 (w[12]) + w[7] + S0 (w[15]); + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; + w[15] += S1 (w[13]) + w[8] + S0 (w[0]); + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t += 16; +#endif + } + + for (; t < 80; ) + { + u64 t1, t2; + +#if 0 /* Not unrolled. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; + t2 = Sum0 (a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; +#else /* Unrolled to interweave the chain variables. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t += 16; +#endif + } + + /* Update chaining vars. */ + hd->h0 += a; + hd->h1 += b; + hd->h2 += c; + hd->h3 += d; + hd->h4 += e; + hd->h5 += f; + hd->h6 += g; + hd->h7 += h; + + return /* burn_stack */ (8 + 16) * sizeof(u64) + sizeof(u32) + 3 * sizeof(void*); +} + +static uint +sha512_transform(void *context, const byte *data, size_t nblks) +{ + struct sha512_context *ctx = context; + uint burn; + + do + { + burn = sha512_transform_block(&ctx->state, data) + 3 * sizeof(void*); + data += 128; + } + while(--nblks); + + return burn; +} + +/* The routine final terminates the computation and + * returns the digest. + * The handle is prepared for a new cycle, but adding bytes to the + * handle will the destroy the returned buffer. + * Returns: 64 bytes representing the digest. When used for sha384, + * we take the leftmost 48 of those bytes. + */ +byte * +sha512_final(struct sha512_context *ctx) +{ + u64 t, th, msb, lsb; + byte *p; + + sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; + + t = ctx->bctx.nblocks; + /* if (sizeof t == sizeof ctx->bctx.nblocks) */ + th = ctx->bctx.nblocks_high; + /* else */ + /* th = ctx->bctx.nblocks >> 64; In case we ever use u128 */ + + /* multiply by 128 to make a byte count */ + lsb = t << 7; + msb = (th << 7) | (t >> 57); + /* add the count */ + t = lsb; + if ((lsb += ctx->bctx.count) < t) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 61; + + if (ctx->bctx.count < 112) + { /* enough room */ + ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad */ + while(ctx->bctx.count < 112) + ctx->bctx.buf[ctx->bctx.count++] = 0; /* pad */ + } + else + { /* need one extra block */ + ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad character */ + while(ctx->bctx.count < 128) + ctx->bctx.buf[ctx->bctx.count++] = 0; + sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; + memset(ctx->bctx.buf, 0, 112); /* fill next block with zeroes */ + } + /* append the 128 bit count */ + put_u64(ctx->bctx.buf + 112, msb); + put_u64(ctx->bctx.buf + 120, lsb); + sha512_transform(ctx, ctx->bctx.buf, 1); + + p = ctx->bctx.buf; +#define X(a) do { put_u64(p, ctx->state.h##a); p += 8; } while(0) + X (0); + X (1); + X (2); + X (3); + X (4); + X (5); + /* Note that these last two chunks are included even for SHA384. + We just ignore them. */ + X (6); + X (7); +#undef X + + return ctx->bctx.buf; +} + + +/* + * SHA512-HMAC + */ + +static void +sha512_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha512_context hd_tmp; + + sha512_init(&hd_tmp); + sha512_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha512_final(&hd_tmp), SHA512_SIZE); +} + +void +sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA512_BLOCK_SIZE], buf[SHA512_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA512_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA512_BLOCK_SIZE - keylen); + } + else + { + sha512_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA512_SIZE, SHA512_BLOCK_SIZE - SHA512_SIZE); + } + + /* Initialize the inner digest */ + sha512_init(&ctx->ictx); + int i; + for (i = 0; i < SHA512_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha512_update(&ctx->ictx, buf, SHA512_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha512_init(&ctx->octx); + for (i = 0; i < SHA512_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha512_update(&ctx->octx, buf, SHA512_BLOCK_SIZE); +} + +void sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha512_update(&ctx->ictx, buf, buflen); +} + +byte *sha512_hmac_final(struct sha512_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha512_final(&ctx->ictx); + + /* Finish the outer digest */ + sha512_update(&ctx->octx, isha, SHA512_SIZE); + return sha512_final(&ctx->octx); +} + + +/* + * SHA384-HMAC + */ + +static void +sha384_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha384_context hd_tmp; + + sha384_init(&hd_tmp); + sha384_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha384_final(&hd_tmp), SHA384_SIZE); +} + +void +sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA384_BLOCK_SIZE], buf[SHA384_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA384_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA384_BLOCK_SIZE - keylen); + } + else + { + sha384_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA384_SIZE, SHA384_BLOCK_SIZE - SHA384_SIZE); + } + + /* Initialize the inner digest */ + sha384_init(&ctx->ictx); + int i; + for (i = 0; i < SHA384_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha384_update(&ctx->ictx, buf, SHA384_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha384_init(&ctx->octx); + for (i = 0; i < SHA384_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha384_update(&ctx->octx, buf, SHA384_BLOCK_SIZE); +} + +void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha384_update(&ctx->ictx, buf, buflen); +} + +byte *sha384_hmac_final(struct sha384_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha384_final(&ctx->ictx); + + /* Finish the outer digest */ + sha384_update(&ctx->octx, isha, SHA384_SIZE); + return sha384_final(&ctx->octx); +} diff --git a/lib/sha512.h b/lib/sha512.h new file mode 100644 index 00000000..bd998152 --- /dev/null +++ b/lib/sha512.h @@ -0,0 +1,73 @@ +/* + * BIRD Library -- SHA-512 and SHA-384 Hash Functions, + * HMAC-SHA-512 and HMAC-SHA-384 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_SHA512_H_ +#define _BIRD_SHA512_H_ + +#include "lib/sha256.h" + +#define SHA384_SIZE 48 +#define SHA384_HEX_SIZE 97 +#define SHA384_BLOCK_SIZE 128 + +#define SHA512_SIZE 64 +#define SHA512_HEX_SIZE 129 +#define SHA512_BLOCK_SIZE 128 + +struct sha512_state +{ + u64 h0, h1, h2, h3, h4, h5, h6, h7; +}; + +struct sha512_context +{ + struct sha256_context bctx; + struct sha512_state state; +}; +#define sha384_context sha512_context /* aliasing 'struct sha384_context' to 'struct sha512_context' */ + + +void sha512_init(struct sha512_context *ctx); +void sha384_init(struct sha384_context *ctx); + +void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len); +static inline void sha384_update(struct sha384_context *ctx, const byte *in_buf, size_t in_len) +{ + sha512_update(ctx, in_buf, in_len); +} + +byte* sha512_final(struct sha512_context *ctx); +static inline byte* sha384_final(struct sha384_context *ctx) +{ + return sha512_final(ctx); +} + +/* + * HMAC-SHA512, HMAC-SHA384 + */ +struct sha512_hmac_context +{ + struct sha512_context ictx; + struct sha512_context octx; +} ; +#define sha384_hmac_context sha512_hmac_context /* aliasing 'struct sha384_hmac_context' to 'struct sha384_hmac_context' */ + +void sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen); +void sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen); + +void sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen); +void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen); + +byte *sha512_hmac_final(struct sha512_hmac_context *ctx); +byte *sha384_hmac_final(struct sha384_hmac_context *ctx); + +#endif /* _BIRD_SHA512_H_ */ From 1e4891e48e7b6f022564e7409d15c3fdb65ec2ad Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 23 Nov 2015 11:13:40 +0100 Subject: [PATCH 040/165] Nest: Fix bug in device proto If an interface address notification is received during device protocol shutdown/restart, BIRD crashed. Thanks to Wei Huang for the bugreport. --- sysdep/linux/netlink.c | 6 ++++-- sysdep/unix/krt.c | 2 +- sysdep/unix/krt.h | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index f2f60100..efbf41a6 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1116,12 +1116,14 @@ nl_async_msg(struct nlmsghdr *h) case RTM_NEWLINK: case RTM_DELLINK: DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type); - nl_parse_link(h, 0); + if (kif_proto) + nl_parse_link(h, 0); break; case RTM_NEWADDR: case RTM_DELADDR: DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type); - nl_parse_addr(h, 0); + if (kif_proto) + nl_parse_addr(h, 0); break; default: DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 49bf9519..5e78586b 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -84,8 +84,8 @@ krt_io_init(void) * Interfaces */ +struct kif_proto *kif_proto; static struct kif_config *kif_cf; -static struct kif_proto *kif_proto; static timer *kif_scan_timer; static bird_clock_t kif_last_shot; diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index aea20102..d4a8717e 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -112,6 +112,8 @@ struct kif_proto { struct kif_state sys; /* Sysdep state */ }; +struct kif_proto *kif_proto; + #define KIF_CF ((struct kif_config *)p->p.cf) struct proto_config * krt_init_config(int class); From 5126380beace4e39578f005fe115917b8e8b8ff3 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 24 Nov 2015 13:47:28 +0100 Subject: [PATCH 041/165] Minor changes to SHA hash functions --- lib/birdlib.h | 1 + lib/sha1.c | 176 +++++++++-------- lib/sha1.h | 72 +++---- lib/sha256.c | 176 ++++++++--------- lib/sha256.h | 42 ++-- lib/sha512.c | 528 +++++++++++++++++++++++++------------------------- lib/sha512.h | 42 ++-- 7 files changed, 517 insertions(+), 520 deletions(-) diff --git a/lib/birdlib.h b/lib/birdlib.h index ad41dca3..16f437ef 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -30,6 +30,7 @@ #define MAX(a,b) MAX_(a,b) #endif +#define U64(c) UINT64_C(c) #define ABS(a) ((a)>=0 ? (a) : -(a)) #define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a)) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) diff --git a/lib/sha1.c b/lib/sha1.c index dd29b5fd..73b4b280 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -15,33 +15,35 @@ #include "lib/sha1.h" #include "lib/unaligned.h" + void -sha1_init(struct sha1_context *hd) +sha1_init(struct sha1_context *ctx) { - hd->h0 = 0x67452301; - hd->h1 = 0xefcdab89; - hd->h2 = 0x98badcfe; - hd->h3 = 0x10325476; - hd->h4 = 0xc3d2e1f0; - hd->nblocks = 0; - hd->count = 0; + ctx->h0 = 0x67452301; + ctx->h1 = 0xefcdab89; + ctx->h2 = 0x98badcfe; + ctx->h3 = 0x10325476; + ctx->h4 = 0xc3d2e1f0; + + ctx->nblocks = 0; + ctx->count = 0; } /* * Transform the message X which consists of 16 32-bit-words */ static void -sha1_transform(struct sha1_context *hd, const byte *data) +sha1_transform(struct sha1_context *ctx, const byte *data) { u32 a,b,c,d,e,tm; u32 x[16]; /* Get values from the chaining vars. */ - a = hd->h0; - b = hd->h1; - c = hd->h2; - d = hd->h3; - e = hd->h4; + a = ctx->h0; + b = ctx->h1; + c = ctx->h2; + d = ctx->h3; + e = ctx->h4; #ifdef CPU_BIG_ENDIAN memcpy(x, data, 64); @@ -69,7 +71,7 @@ sha1_transform(struct sha1_context *hd, const byte *data) do \ { \ e += ROL(a, 5) + f(b, c, d) + k + m; \ - b = ROL( b, 30 ); \ + b = ROL(b, 30); \ } while(0) R( a, b, c, d, e, F1, K1, x[ 0] ); @@ -154,72 +156,72 @@ sha1_transform(struct sha1_context *hd, const byte *data) R( b, c, d, e, a, F4, K4, M(79) ); /* Update chaining vars. */ - hd->h0 += a; - hd->h1 += b; - hd->h2 += c; - hd->h3 += d; - hd->h4 += e; + ctx->h0 += a; + ctx->h1 += b; + ctx->h2 += c; + ctx->h3 += d; + ctx->h4 += e; } /* - * Update the message digest with the contents - * of INBUF with length INLEN. + * Update the message digest with the contents of BUF with length LEN. */ void -sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen) +sha1_update(struct sha1_context *ctx, const byte *buf, uint len) { - if (hd->count == 64) /* flush the buffer */ + if (ctx->count) { - sha1_transform(hd, hd->buf); - hd->count = 0; - hd->nblocks++; - } - if (!inbuf) - return; + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA1_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; - if (hd->count) - { - for (; inlen && hd->count < 64; inlen--) - hd->buf[hd->count++] = *inbuf++; - sha1_update( hd, NULL, 0 ); - if(!inlen) + if (ctx->count < SHA1_BLOCK_SIZE) return; + + /* Process data from internal buffer */ + sha1_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; } - while (inlen >= 64) + if (!len) + return; + + /* Process data from input buffer */ + while (len >= SHA1_BLOCK_SIZE) { - sha1_transform(hd, inbuf); - hd->count = 0; - hd->nblocks++; - inlen -= 64; - inbuf += 64; + sha1_transform(ctx, buf); + ctx->nblocks++; + buf += SHA1_BLOCK_SIZE; + len -= SHA1_BLOCK_SIZE; } - for (; inlen && hd->count < 64; inlen--) - hd->buf[hd->count++] = *inbuf++; + + /* Copy remaining data to internal buffer */ + memcpy(ctx->buf, buf, len); + ctx->count = len; } /* - * The routine final terminates the computation and - * returns the digest. - * The handle is prepared for a new cycle, but adding bytes to the - * handle will the destroy the returned buffer. + * The routine final terminates the computation and returns the digest. The + * handle is prepared for a new cycle, but adding bytes to the handle will the + * destroy the returned buffer. + * * Returns: 20 bytes representing the digest. */ byte * -sha1_final(struct sha1_context *hd) +sha1_final(struct sha1_context *ctx) { u32 t, msb, lsb; - u32 *p; - sha1_update(hd, NULL, 0); /* flush */; + sha1_update(ctx, NULL, 0); /* flush */ - t = hd->nblocks; + t = ctx->nblocks; /* multiply by 64 to make a byte count */ lsb = t << 6; msb = t >> 26; /* add the count */ t = lsb; - if ((lsb += hd->count) < t) + if ((lsb += ctx->count) < t) msb++; /* multiply by 8 to make a bit count */ t = lsb; @@ -227,33 +229,36 @@ sha1_final(struct sha1_context *hd) msb <<= 3; msb |= t >> 29; - if (hd->count < 56) /* enough room */ + if (ctx->count < 56) { - hd->buf[hd->count++] = 0x80; /* pad */ - while (hd->count < 56) - hd->buf[hd->count++] = 0; /* pad */ + /* enough room */ + ctx->buf[ctx->count++] = 0x80; /* pad */ + while (ctx->count < 56) + ctx->buf[ctx->count++] = 0; /* pad */ } - else /* need one extra block */ + else { - hd->buf[hd->count++] = 0x80; /* pad character */ - while (hd->count < 64) - hd->buf[hd->count++] = 0; - sha1_update(hd, NULL, 0); /* flush */; - memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ + /* need one extra block */ + ctx->buf[ctx->count++] = 0x80; /* pad character */ + while (ctx->count < 64) + ctx->buf[ctx->count++] = 0; + sha1_update(ctx, NULL, 0); /* flush */ + memset(ctx->buf, 0, 56); /* fill next block with zeroes */ } + /* append the 64 bit count */ - hd->buf[56] = msb >> 24; - hd->buf[57] = msb >> 16; - hd->buf[58] = msb >> 8; - hd->buf[59] = msb ; - hd->buf[60] = lsb >> 24; - hd->buf[61] = lsb >> 16; - hd->buf[62] = lsb >> 8; - hd->buf[63] = lsb ; - sha1_transform(hd, hd->buf); - - p = (u32*) hd->buf; -#define X(a) do { put_u32(p, hd->h##a); p++; } while(0) + ctx->buf[56] = msb >> 24; + ctx->buf[57] = msb >> 16; + ctx->buf[58] = msb >> 8; + ctx->buf[59] = msb; + ctx->buf[60] = lsb >> 24; + ctx->buf[61] = lsb >> 16; + ctx->buf[62] = lsb >> 8; + ctx->buf[63] = lsb; + sha1_transform(ctx, ctx->buf); + + byte *p = ctx->buf; +#define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0) X(0); X(1); X(2); @@ -261,12 +266,12 @@ sha1_final(struct sha1_context *hd) X(4); #undef X - return hd->buf; + return ctx->buf; } /* - * SHA1-HMAC + * SHA1-HMAC */ /* @@ -292,12 +297,12 @@ sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen) if (keylen <= SHA1_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA1_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA1_BLOCK_SIZE - keylen); } else { sha1_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA1_SIZE, SHA1_BLOCK_SIZE - SHA1_SIZE); + memset(keybuf + SHA1_SIZE, 0, SHA1_BLOCK_SIZE - SHA1_SIZE); } /* Initialize the inner digest */ @@ -321,7 +326,8 @@ sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen) sha1_update(&ctx->ictx, data, datalen); } -byte *sha1_hmac_final(struct sha1_hmac_context *ctx) +byte * +sha1_hmac_final(struct sha1_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha1_final(&ctx->ictx); @@ -334,9 +340,9 @@ byte *sha1_hmac_final(struct sha1_hmac_context *ctx) void sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen) { - struct sha1_hmac_context hd; - sha1_hmac_init(&hd, key, keylen); - sha1_hmac_update(&hd, data, datalen); - byte *osha = sha1_hmac_final(&hd); - memcpy(outbuf, osha, SHA1_SIZE); + struct sha1_hmac_context ctx; + + sha1_hmac_init(&ctx, key, keylen); + sha1_hmac_update(&ctx, data, datalen); + memcpy(outbuf, sha1_hmac_final(&ctx), SHA1_SIZE); } diff --git a/lib/sha1.h b/lib/sha1.h index 425160a0..c019bb49 100644 --- a/lib/sha1.h +++ b/lib/sha1.h @@ -17,70 +17,70 @@ #include "nest/bird.h" + +#define SHA1_SIZE 20 /* Size of the SHA1 hash in its binary representation */ +#define SHA1_HEX_SIZE 41 /* Buffer length for a string containing SHA1 in hexadecimal format. */ +#define SHA1_BLOCK_SIZE 64 /* SHA1 splits input to blocks of this size. */ + + /* * Internal SHA1 state. * You should use it just as an opaque handle only. */ struct sha1_context { - u32 h0,h1,h2,h3,h4; - u32 nblocks; - byte buf[64]; - int count; -} ; + u32 h0, h1, h2, h3, h4; + byte buf[SHA1_BLOCK_SIZE]; + uint nblocks; + uint count; +}; + -void sha1_init(struct sha1_context *hd); /* Initialize new algorithm run in the @hd context. **/ +void sha1_init(struct sha1_context *ctx); /* Initialize new algorithm run in the @ctx context. **/ /* - * Push another @inlen bytes of data pointed to by @inbuf onto the - * SHA1 hash currently in @hd. You can call this any times you want on - * the same hash (and you do not need to reinitialize it by - * @sha1_init()). It has the same effect as concatenating all the data - * together and passing them at once. + * Push another @len bytes of data pointed to by @buf onto the SHA1 hash + * currently in @ctx. You can call this any times you want on the same hash (and + * you do not need to reinitialize it by @sha1_init()). It has the same effect + * as concatenating all the data together and passing them at once. */ -void sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen); +void sha1_update(struct sha1_context *ctx, const byte *buf, uint len); /* - * No more @sha1_update() calls will be done. This terminates the hash - * and returns a pointer to it. - * - * Note that the pointer points into data in the @hd context. If it ceases - * to exist, the pointer becomes invalid. + * No more @sha1_update() calls will be done. This terminates the hash and + * returns a pointer to it. * - * To convert the hash to its usual hexadecimal representation, see - * <>. + * Note that the pointer points into data in the @ctx context. If it ceases to + * exist, the pointer becomes invalid. */ -byte *sha1_final(struct sha1_context *hd); +byte *sha1_final(struct sha1_context *ctx); /* - * A convenience one-shot function for SHA1 hash. - * It is equivalent to this snippet of code: + * A convenience one-shot function for SHA1 hash. It is equivalent to this + * snippet of code: * - * sha1_context hd; - * sha1_init(&hd); - * sha1_update(&hd, buffer, length); - * memcpy(outbuf, sha1_final(&hd), SHA1_SIZE); + * sha1_context ctx; + * sha1_init(&ctx); + * sha1_update(&ctx, buffer, length); + * memcpy(outbuf, sha1_final(&ctx), SHA1_SIZE); */ void sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length); /* - * SHA1 HMAC message authentication. If you provide @key and @data, - * the result will be stored in @outbuf. + * SHA1 HMAC message authentication. If you provide @key and @data, the result + * will be stored in @outbuf. */ void sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen); /* - * The HMAC also exists in a stream version in a way analogous to the - * plain SHA1. Pass this as a context. + * The HMAC also exists in a stream version in a way analogous to the plain + * SHA1. Pass this as a context. */ struct sha1_hmac_context { struct sha1_context ictx; struct sha1_context octx; }; -void sha1_hmac_init(struct sha1_hmac_context *hd, const byte *key, uint keylen); /* Initialize HMAC with context @hd and the given key. See sha1_init(). */ -void sha1_hmac_update(struct sha1_hmac_context *hd, const byte *data, uint datalen); /* Hash another @datalen bytes of data. See sha1_update(). */ -byte *sha1_hmac_final(struct sha1_hmac_context *hd); /* Terminate the HMAC and return a pointer to the allocated hash. See sha1_final(). */ +void sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen); /* Initialize HMAC with context @ctx and the given key. See sha1_init(). */ +void sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen); /* Hash another @datalen bytes of data. See sha1_update(). */ +byte *sha1_hmac_final(struct sha1_hmac_context *ctx); /* Terminate the HMAC and return a pointer to the allocated hash. See sha1_final(). */ -#define SHA1_SIZE 20 /* Size of the SHA1 hash in its binary representation **/ -#define SHA1_HEX_SIZE 41 /* Buffer length for a string containing SHA1 in hexadecimal format. **/ -#define SHA1_BLOCK_SIZE 64 /* SHA1 splits input to blocks of this size. **/ #endif /* _BIRD_SHA1_H_ */ diff --git a/lib/sha256.c b/lib/sha256.c index 2d979f90..440245d5 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -13,7 +13,8 @@ #include "lib/sha256.h" #include "lib/unaligned.h" -static uint sha256_transform(void *ctx, const byte *data, size_t nblks); + +// #define SHA256_UNROLLED void sha256_init(struct sha256_context *ctx) @@ -28,10 +29,7 @@ sha256_init(struct sha256_context *ctx) ctx->h7 = 0x5be0cd19; ctx->nblocks = 0; - ctx->nblocks_high = 0; ctx->count = 0; - ctx->blocksize = 64; - ctx->transform = sha256_transform; } void @@ -47,10 +45,7 @@ sha224_init(struct sha224_context *ctx) ctx->h7 = 0xbefa4fa4; ctx->nblocks = 0; - ctx->nblocks_high = 0; ctx->count = 0; - ctx->blocksize = 64; - ctx->transform = sha256_transform; } /* (4.2) same as SHA-1's F1. */ @@ -70,7 +65,7 @@ f3(u32 x, u32 y, u32 z) /* Bitwise rotation of an uint to the right */ static inline u32 ror(u32 x, int n) { - return ( (x >> (n&(32-1))) | (x << ((32-n)&(32-1))) ); + return ((x >> (n&(32-1))) | (x << ((32-n)&(32-1)))); } /* (4.4) */ @@ -112,7 +107,7 @@ sum1(u32 x) 32-bit-words. See FIPS 180-2 for details. */ static uint -sha256_transform_block(struct sha256_context *ctx, const byte *data) +sha256_transform(struct sha256_context *ctx, const byte *data) { static const u32 K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, @@ -148,52 +143,58 @@ sha256_transform_block(struct sha256_context *ctx, const byte *data) for (i = 0; i < 16; i++) w[i] = get_u32(data + i * 4); + for (; i < 64; i++) w[i] = S1(w[i-2]) + w[i-7] + S0(w[i-15]) + w[i-16]; for (i = 0; i < 64;) { +#ifndef SHA256_UNROLLED + R(a,b,c,d,e,f,g,h,K[i],w[i]); + i++; +#else /* Unrolled */ t1 = h + sum1(e) + f1(e, f, g) + K[i] + w[i]; - t2 = sum0 (a) + f3(a, b, c); + t2 = sum0(a) + f3(a, b, c); d += t1; h = t1 + t2; t1 = g + sum1(d) + f1(d, e, f) + K[i+1] + w[i+1]; - t2 = sum0 (h) + f3(h, a, b); + t2 = sum0(h) + f3(h, a, b); c += t1; g = t1 + t2; t1 = f + sum1(c) + f1(c, d, e) + K[i+2] + w[i+2]; - t2 = sum0 (g) + f3(g, h, a); + t2 = sum0(g) + f3(g, h, a); b += t1; f = t1 + t2; t1 = e + sum1(b) + f1(b, c, d) + K[i+3] + w[i+3]; - t2 = sum0 (f) + f3(f, g, h); + t2 = sum0(f) + f3(f, g, h); a += t1; e = t1 + t2; t1 = d + sum1(a) + f1(a, b, c) + K[i+4] + w[i+4]; - t2 = sum0 (e) + f3(e, f, g); + t2 = sum0(e) + f3(e, f, g); h += t1; d = t1 + t2; t1 = c + sum1(h) + f1(h, a, b) + K[i+5] + w[i+5]; - t2 = sum0 (d) + f3(d, e, f); + t2 = sum0(d) + f3(d, e, f); g += t1; c = t1 + t2; t1 = b + sum1(g) + f1(g, h, a) + K[i+6] + w[i+6]; - t2 = sum0 (c) + f3(c, d, e); + t2 = sum0(c) + f3(c, d, e); f += t1; b = t1 + t2; t1 = a + sum1(f) + f1(f, g, h) + K[i+7] + w[i+7]; - t2 = sum0 (b) + f3(b, c, d); + t2 = sum0(b) + f3(b, c, d); e += t1; a = t1 + t2; i += 8; +#endif } ctx->h0 += a; @@ -211,22 +212,6 @@ sha256_transform_block(struct sha256_context *ctx, const byte *data) #undef S1 #undef R -static uint -sha256_transform(void *ctx, const byte *data, size_t nblks) -{ - struct sha256_context *hd = ctx; - uint burn; - - do - { - burn = sha256_transform_block(hd, data); - data += 64; - } - while (--nblks); - - return burn; -} - /* Common function to write a chunk of data to the transform function of a hash algorithm. Note that the use of the term "block" does not imply a fixed size block. Note that we explicitly allow to use @@ -234,65 +219,56 @@ sha256_transform(void *ctx, const byte *data, size_t nblks) not have any meaning but writing after finalize is sometimes helpful to mitigate timing attacks. */ void -sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len) +sha256_update(struct sha256_context *ctx, const byte *buf, size_t len) { - const uint blocksize = ctx->blocksize; - size_t inblocks; + if (ctx->count) + { + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA256_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; - if (sizeof(ctx->buf) < blocksize) - debug("BUG: in file %s at line %d", __FILE__ , __LINE__); + if (ctx->count < SHA256_BLOCK_SIZE) + return; - if (ctx->count == blocksize) /* Flush the buffer. */ - { - ctx->transform(ctx, ctx->buf, 1); + /* Process data from internal buffer */ + sha256_transform(ctx, ctx->buf); + ctx->nblocks++; ctx->count = 0; - if (!++ctx->nblocks) - ctx->nblocks_high++; } - if (!in_buf) + + if (!len) return; - if (ctx->count) + /* Process data from input buffer */ + while (len >= SHA256_BLOCK_SIZE) { - for (; in_len && ctx->count < blocksize; in_len--) - ctx->buf[ctx->count++] = *in_buf++; - sha256_update(ctx, NULL, 0); - if (!in_len) - return; + sha256_transform(ctx, buf); + ctx->nblocks++; + buf += SHA256_BLOCK_SIZE; + len -= SHA256_BLOCK_SIZE; } - if (in_len >= blocksize) - { - inblocks = in_len / blocksize; - ctx->transform(ctx, in_buf, inblocks); - ctx->count = 0; - ctx->nblocks_high += (ctx->nblocks + inblocks < inblocks); - ctx->nblocks += inblocks; - in_len -= inblocks * blocksize; - in_buf += inblocks * blocksize; - } - for (; in_len && ctx->count < blocksize; in_len--) - ctx->buf[ctx->count++] = *in_buf++; + /* Copy remaining data to internal buffer */ + memcpy(ctx->buf, buf, len); + ctx->count = len; } /* - The routine finally terminates the computation and returns the - digest. The handle is prepared for a new cycle, but adding bytes - to the handle will the destroy the returned buffer. Returns: 32 - bytes with the message the digest. */ -byte* + * The routine finally terminates the computation and returns the digest. The + * handle is prepared for a new cycle, but adding bytes to the handle will the + * destroy the returned buffer. + * + * Returns: 32 bytes with the message the digest. 28 bytes for SHA-224. + */ +byte * sha256_final(struct sha256_context *ctx) { u32 t, th, msb, lsb; - byte *p; - sha256_update(ctx, NULL, 0); /* flush */; + sha256_update(ctx, NULL, 0); /* flush */ t = ctx->nblocks; - if (sizeof t == sizeof ctx->nblocks) - th = ctx->nblocks_high; - else - th = 0; + th = 0; /* multiply by 64 to make a byte count */ lsb = t << 6; @@ -308,26 +284,28 @@ sha256_final(struct sha256_context *ctx) msb |= t >> 29; if (ctx->count < 56) - { /* enough room */ + { + /* enough room */ ctx->buf[ctx->count++] = 0x80; /* pad */ while (ctx->count < 56) ctx->buf[ctx->count++] = 0; /* pad */ } else - { /* need one extra block */ + { + /* need one extra block */ ctx->buf[ctx->count++] = 0x80; /* pad character */ while (ctx->count < 64) ctx->buf[ctx->count++] = 0; sha256_update(ctx, NULL, 0); /* flush */; - memset (ctx->buf, 0, 56 ); /* fill next block with zeroes */ + memset(ctx->buf, 0, 56 ); /* fill next block with zeroes */ } + /* append the 64 bit count */ put_u32(ctx->buf + 56, msb); put_u32(ctx->buf + 60, lsb); - sha256_transform(ctx, ctx->buf, 1); - - p = ctx->buf; + sha256_transform(ctx, ctx->buf); + byte *p = ctx->buf; #define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0) X(0); X(1); @@ -344,17 +322,17 @@ sha256_final(struct sha256_context *ctx) /* - * SHA256-HMAC + * SHA256-HMAC */ static void sha256_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha256_context hd_tmp; + struct sha256_context ctx; - sha256_init(&hd_tmp); - sha256_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha256_final(&hd_tmp), SHA256_SIZE); + sha256_init(&ctx); + sha256_update(&ctx, buffer, length); + memcpy(outbuf, sha256_final(&ctx), SHA256_SIZE); } void @@ -366,12 +344,12 @@ sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA256_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA256_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA256_BLOCK_SIZE - keylen); } else { sha256_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA256_SIZE, SHA256_BLOCK_SIZE - SHA256_SIZE); + memset(keybuf + SHA256_SIZE, 0, SHA256_BLOCK_SIZE - SHA256_SIZE); } /* Initialize the inner digest */ @@ -388,13 +366,15 @@ sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen sha256_update(&ctx->octx, buf, SHA256_BLOCK_SIZE); } -void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha256_update(&ctx->ictx, buf, buflen); } -byte *sha256_hmac_final(struct sha256_hmac_context *ctx) +byte * +sha256_hmac_final(struct sha256_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha256_final(&ctx->ictx); @@ -406,17 +386,17 @@ byte *sha256_hmac_final(struct sha256_hmac_context *ctx) /* - * SHA224-HMAC + * SHA224-HMAC */ static void sha224_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha224_context hd_tmp; + struct sha224_context ctx; - sha224_init(&hd_tmp); - sha224_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha224_final(&hd_tmp), SHA224_SIZE); + sha224_init(&ctx); + sha224_update(&ctx, buffer, length); + memcpy(outbuf, sha224_final(&ctx), SHA224_SIZE); } void @@ -428,12 +408,12 @@ sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA224_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA224_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA224_BLOCK_SIZE - keylen); } else { sha224_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA224_SIZE, SHA224_BLOCK_SIZE - SHA224_SIZE); + memset(keybuf + SHA224_SIZE, 0, SHA224_BLOCK_SIZE - SHA224_SIZE); } /* Initialize the inner digest */ @@ -450,13 +430,15 @@ sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen sha224_update(&ctx->octx, buf, SHA224_BLOCK_SIZE); } -void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha256_update(&ctx->ictx, buf, buflen); } -byte *sha224_hmac_final(struct sha224_hmac_context *ctx) +byte * +sha224_hmac_final(struct sha224_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha224_final(&ctx->ictx); diff --git a/lib/sha256.h b/lib/sha256.h index 848d2176..381200a9 100644 --- a/lib/sha256.h +++ b/lib/sha256.h @@ -15,6 +15,7 @@ #include "nest/bird.h" + #define SHA224_SIZE 28 #define SHA224_HEX_SIZE 57 #define SHA224_BLOCK_SIZE 64 @@ -23,44 +24,44 @@ #define SHA256_HEX_SIZE 65 #define SHA256_BLOCK_SIZE 64 + struct sha256_context { - u32 h0,h1,h2,h3,h4,h5,h6,h7; - byte buf[128]; /* 128 is for SHA384 and SHA512 support, otherwise for SHA224 and SHA256 is 64 enough */ - u32 nblocks; - u32 nblocks_high; - int count; - u32 blocksize; - uint (*transform)(void *c, const byte *blks, size_t nblks); + u32 h0, h1, h2, h3, h4, h5, h6, h7; + byte buf[SHA256_BLOCK_SIZE]; + uint nblocks; + uint count; }; -#define sha224_context sha256_context /* aliasing 'struct sha224_context' to 'struct sha256_context' */ + +#define sha224_context sha256_context + void sha256_init(struct sha256_context *ctx); void sha224_init(struct sha224_context *ctx); -void sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len); -static inline void sha224_update(struct sha224_context *ctx, const byte *in_buf, size_t in_len) -{ - sha256_update(ctx, in_buf, in_len); -} +void sha256_update(struct sha256_context *ctx, const byte *buf, size_t len); +static inline void sha224_update(struct sha224_context *ctx, const byte *buf, size_t len) +{ sha256_update(ctx, buf, len); } + +byte *sha256_final(struct sha256_context *ctx); +static inline byte *sha224_final(struct sha224_context *ctx) +{ return sha256_final(ctx); } -byte* sha256_final(struct sha256_context *ctx); -static inline byte* sha224_final(struct sha224_context *ctx) -{ - return sha256_final(ctx); -} /* * HMAC-SHA256, HMAC-SHA224 */ + struct sha256_hmac_context { struct sha256_context ictx; struct sha256_context octx; }; -#define sha224_hmac_context sha256_hmac_context /* aliasing 'struct sha224_hmac_context' to 'struct sha256_hmac_context' */ + +#define sha224_hmac_context sha256_hmac_context + void sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen); -void sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen); +void sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen); void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen); void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen); @@ -68,4 +69,5 @@ void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t byte *sha256_hmac_final(struct sha256_hmac_context *ctx); byte *sha224_hmac_final(struct sha224_hmac_context *ctx); + #endif /* _BIRD_SHA256_H_ */ diff --git a/lib/sha512.c b/lib/sha512.c index e46e4c98..37e660f7 100644 --- a/lib/sha512.c +++ b/lib/sha512.c @@ -10,57 +10,42 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#include "lib/sha256.h" #include "lib/sha512.h" #include "lib/unaligned.h" -static uint sha512_transform(void *context, const byte *data, size_t nblks); + +// #define SHA512_UNROLLED void sha512_init(struct sha512_context *ctx) { - struct sha512_state *hd = &ctx->state; - - hd->h0 = UINT64_C(0x6a09e667f3bcc908); - hd->h1 = UINT64_C(0xbb67ae8584caa73b); - hd->h2 = UINT64_C(0x3c6ef372fe94f82b); - hd->h3 = UINT64_C(0xa54ff53a5f1d36f1); - hd->h4 = UINT64_C(0x510e527fade682d1); - hd->h5 = UINT64_C(0x9b05688c2b3e6c1f); - hd->h6 = UINT64_C(0x1f83d9abfb41bd6b); - hd->h7 = UINT64_C(0x5be0cd19137e2179); - - ctx->bctx.nblocks = 0; - ctx->bctx.nblocks_high = 0; - ctx->bctx.count = 0; - ctx->bctx.blocksize = 128; - ctx->bctx.transform = sha512_transform; + ctx->h0 = U64(0x6a09e667f3bcc908); + ctx->h1 = U64(0xbb67ae8584caa73b); + ctx->h2 = U64(0x3c6ef372fe94f82b); + ctx->h3 = U64(0xa54ff53a5f1d36f1); + ctx->h4 = U64(0x510e527fade682d1); + ctx->h5 = U64(0x9b05688c2b3e6c1f); + ctx->h6 = U64(0x1f83d9abfb41bd6b); + ctx->h7 = U64(0x5be0cd19137e2179); + + ctx->nblocks = 0; + ctx->count = 0; } void sha384_init(struct sha384_context *ctx) { - struct sha512_state *hd = &ctx->state; - - hd->h0 = UINT64_C(0xcbbb9d5dc1059ed8); - hd->h1 = UINT64_C(0x629a292a367cd507); - hd->h2 = UINT64_C(0x9159015a3070dd17); - hd->h3 = UINT64_C(0x152fecd8f70e5939); - hd->h4 = UINT64_C(0x67332667ffc00b31); - hd->h5 = UINT64_C(0x8eb44a8768581511); - hd->h6 = UINT64_C(0xdb0c2e0d64f98fa7); - hd->h7 = UINT64_C(0x47b5481dbefa4fa4); - - ctx->bctx.nblocks = 0; - ctx->bctx.nblocks_high = 0; - ctx->bctx.count = 0; - ctx->bctx.blocksize = 128; - ctx->bctx.transform = sha512_transform; -} - -void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len) -{ - sha256_update(&ctx->bctx, in_buf, in_len); + ctx->h0 = U64(0xcbbb9d5dc1059ed8); + ctx->h1 = U64(0x629a292a367cd507); + ctx->h2 = U64(0x9159015a3070dd17); + ctx->h3 = U64(0x152fecd8f70e5939); + ctx->h4 = U64(0x67332667ffc00b31); + ctx->h5 = U64(0x8eb44a8768581511); + ctx->h6 = U64(0xdb0c2e0d64f98fa7); + ctx->h7 = U64(0x47b5481dbefa4fa4); + + ctx->nblocks = 0; + ctx->count = 0; } static inline u64 @@ -82,82 +67,82 @@ Maj(u64 x, u64 y, u64 z) } static inline u64 -Sum0(u64 x) +sum0(u64 x) { - return (ROTR (x, 28) ^ ROTR (x, 34) ^ ROTR (x, 39)); + return (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)); } static inline u64 -Sum1 (u64 x) +sum1(u64 x) { - return (ROTR (x, 14) ^ ROTR (x, 18) ^ ROTR (x, 41)); + return (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)); } static const u64 k[] = { - UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), - UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), - UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), - UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), - UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), - UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), - UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), - UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), - UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), - UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), - UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), - UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), - UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), - UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), - UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), - UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), - UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), - UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), - UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), - UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), - UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), - UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), - UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), - UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), - UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), - UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), - UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), - UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), - UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), - UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), - UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), - UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), - UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), - UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), - UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), - UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), - UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), - UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), - UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), - UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) + U64(0x428a2f98d728ae22), U64(0x7137449123ef65cd), + U64(0xb5c0fbcfec4d3b2f), U64(0xe9b5dba58189dbbc), + U64(0x3956c25bf348b538), U64(0x59f111f1b605d019), + U64(0x923f82a4af194f9b), U64(0xab1c5ed5da6d8118), + U64(0xd807aa98a3030242), U64(0x12835b0145706fbe), + U64(0x243185be4ee4b28c), U64(0x550c7dc3d5ffb4e2), + U64(0x72be5d74f27b896f), U64(0x80deb1fe3b1696b1), + U64(0x9bdc06a725c71235), U64(0xc19bf174cf692694), + U64(0xe49b69c19ef14ad2), U64(0xefbe4786384f25e3), + U64(0x0fc19dc68b8cd5b5), U64(0x240ca1cc77ac9c65), + U64(0x2de92c6f592b0275), U64(0x4a7484aa6ea6e483), + U64(0x5cb0a9dcbd41fbd4), U64(0x76f988da831153b5), + U64(0x983e5152ee66dfab), U64(0xa831c66d2db43210), + U64(0xb00327c898fb213f), U64(0xbf597fc7beef0ee4), + U64(0xc6e00bf33da88fc2), U64(0xd5a79147930aa725), + U64(0x06ca6351e003826f), U64(0x142929670a0e6e70), + U64(0x27b70a8546d22ffc), U64(0x2e1b21385c26c926), + U64(0x4d2c6dfc5ac42aed), U64(0x53380d139d95b3df), + U64(0x650a73548baf63de), U64(0x766a0abb3c77b2a8), + U64(0x81c2c92e47edaee6), U64(0x92722c851482353b), + U64(0xa2bfe8a14cf10364), U64(0xa81a664bbc423001), + U64(0xc24b8b70d0f89791), U64(0xc76c51a30654be30), + U64(0xd192e819d6ef5218), U64(0xd69906245565a910), + U64(0xf40e35855771202a), U64(0x106aa07032bbd1b8), + U64(0x19a4c116b8d2d0c8), U64(0x1e376c085141ab53), + U64(0x2748774cdf8eeb99), U64(0x34b0bcb5e19b48a8), + U64(0x391c0cb3c5c95a63), U64(0x4ed8aa4ae3418acb), + U64(0x5b9cca4f7763e373), U64(0x682e6ff3d6b2b8a3), + U64(0x748f82ee5defb2fc), U64(0x78a5636f43172f60), + U64(0x84c87814a1f0ab72), U64(0x8cc702081a6439ec), + U64(0x90befffa23631e28), U64(0xa4506cebde82bde9), + U64(0xbef9a3f7b2c67915), U64(0xc67178f2e372532b), + U64(0xca273eceea26619c), U64(0xd186b8c721c0c207), + U64(0xeada7dd6cde0eb1e), U64(0xf57d4f7fee6ed178), + U64(0x06f067aa72176fba), U64(0x0a637dc5a2c898a6), + U64(0x113f9804bef90dae), U64(0x1b710b35131c471b), + U64(0x28db77f523047d84), U64(0x32caab7b40c72493), + U64(0x3c9ebe0a15c9bebc), U64(0x431d67c49c100d4c), + U64(0x4cc5d4becb3e42b6), U64(0x597f299cfc657e2a), + U64(0x5fcb6fab3ad6faec), U64(0x6c44198c4a475817) }; /* * Transform the message W which consists of 16 64-bit-words */ static uint -sha512_transform_block(struct sha512_state *hd, const byte *data) +sha512_transform(struct sha512_context *ctx, const byte *data) { u64 a, b, c, d, e, f, g, h; u64 w[16]; - int t; + uint t; /* get values from the chaining vars */ - a = hd->h0; - b = hd->h1; - c = hd->h2; - d = hd->h3; - e = hd->h4; - f = hd->h5; - g = hd->h6; - h = hd->h7; - - for ( t = 0; t < 16; t++ ) + a = ctx->h0; + b = ctx->h1; + c = ctx->h2; + d = ctx->h3; + e = ctx->h4; + f = ctx->h5; + g = ctx->h6; + h = ctx->h7; + + for (t = 0; t < 16; t++) w[t] = get_u64(data + t * 8); #define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7)) @@ -175,10 +160,10 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) Unrolled with macros: 350ms Unrolled with inline: 330ms */ -#if 0 /* Not unrolled. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; - w[t%16] += S1 (w[(t - 2)%16]) + w[(t - 7)%16] + S0 (w[(t - 15)%16]); - t2 = Sum0 (a) + Maj(a, b, c); +#ifndef SHA512_UNROLLED + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t%16]; + w[t%16] += S1(w[(t - 2)%16]) + w[(t - 7)%16] + S0(w[(t - 15)%16]); + t2 = sum0(a) + Maj(a, b, c); h = g; g = f; f = e; @@ -188,100 +173,100 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) b = a; a = t1 + t2; t++; -#else /* Unrolled to interweave the chain variables. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; - w[0] += S1 (w[14]) + w[9] + S0 (w[1]); - t2 = Sum0 (a) + Maj(a, b, c); +#else /* Unrolled */ + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0]; + w[0] += S1(w[14]) + w[9] + S0(w[1]); + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; - w[1] += S1 (w[15]) + w[10] + S0 (w[2]); - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+1] + w[1]; + w[1] += S1(w[15]) + w[10] + S0(w[2]); + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; - w[2] += S1 (w[0]) + w[11] + S0 (w[3]); - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+2] + w[2]; + w[2] += S1(w[0]) + w[11] + S0(w[3]); + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; - w[3] += S1 (w[1]) + w[12] + S0 (w[4]); - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+3] + w[3]; + w[3] += S1(w[1]) + w[12] + S0(w[4]); + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; - w[4] += S1 (w[2]) + w[13] + S0 (w[5]); - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+4] + w[4]; + w[4] += S1(w[2]) + w[13] + S0(w[5]); + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; - w[5] += S1 (w[3]) + w[14] + S0 (w[6]); - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+5] + w[5]; + w[5] += S1(w[3]) + w[14] + S0(w[6]); + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; - w[6] += S1 (w[4]) + w[15] + S0 (w[7]); - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+6] + w[6]; + w[6] += S1(w[4]) + w[15] + S0(w[7]); + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; - w[7] += S1 (w[5]) + w[0] + S0 (w[8]); - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+7] + w[7]; + w[7] += S1(w[5]) + w[0] + S0(w[8]); + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; - w[8] += S1 (w[6]) + w[1] + S0 (w[9]); - t2 = Sum0 (a) + Maj(a, b, c); + t1 = h + sum1(e) + Ch(e, f, g) + k[t+8] + w[8]; + w[8] += S1(w[6]) + w[1] + S0(w[9]); + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; - w[9] += S1 (w[7]) + w[2] + S0 (w[10]); - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+9] + w[9]; + w[9] += S1(w[7]) + w[2] + S0(w[10]); + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; - w[10] += S1 (w[8]) + w[3] + S0 (w[11]); - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+10] + w[10]; + w[10] += S1(w[8]) + w[3] + S0(w[11]); + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; - w[11] += S1 (w[9]) + w[4] + S0 (w[12]); - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+11] + w[11]; + w[11] += S1(w[9]) + w[4] + S0(w[12]); + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; - w[12] += S1 (w[10]) + w[5] + S0 (w[13]); - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+12] + w[12]; + w[12] += S1(w[10]) + w[5] + S0(w[13]); + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; - w[13] += S1 (w[11]) + w[6] + S0 (w[14]); - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+13] + w[13]; + w[13] += S1(w[11]) + w[6] + S0(w[14]); + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; - w[14] += S1 (w[12]) + w[7] + S0 (w[15]); - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+14] + w[14]; + w[14] += S1(w[12]) + w[7] + S0(w[15]); + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; - w[15] += S1 (w[13]) + w[8] + S0 (w[0]); - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+15] + w[15]; + w[15] += S1(w[13]) + w[8] + S0(w[0]); + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; @@ -293,9 +278,9 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) { u64 t1, t2; -#if 0 /* Not unrolled. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; - t2 = Sum0 (a) + Maj(a, b, c); +#ifndef SHA512_UNROLLED + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t%16]; + t2 = sum0(a) + Maj(a, b, c); h = g; g = f; f = e; @@ -305,84 +290,84 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) b = a; a = t1 + t2; t++; -#else /* Unrolled to interweave the chain variables. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; - t2 = Sum0 (a) + Maj(a, b, c); +#else /* Unrolled */ + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0]; + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+1] + w[1]; + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+2] + w[2]; + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+3] + w[3]; + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+4] + w[4]; + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+5] + w[5]; + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+6] + w[6]; + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+7] + w[7]; + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; - t2 = Sum0 (a) + Maj(a, b, c); + t1 = h + sum1(e) + Ch(e, f, g) + k[t+8] + w[8]; + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+9] + w[9]; + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+10] + w[10]; + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+11] + w[11]; + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+12] + w[12]; + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+13] + w[13]; + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+14] + w[14]; + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+15] + w[15]; + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; @@ -391,61 +376,77 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) } /* Update chaining vars. */ - hd->h0 += a; - hd->h1 += b; - hd->h2 += c; - hd->h3 += d; - hd->h4 += e; - hd->h5 += f; - hd->h6 += g; - hd->h7 += h; + ctx->h0 += a; + ctx->h1 += b; + ctx->h2 += c; + ctx->h3 += d; + ctx->h4 += e; + ctx->h5 += f; + ctx->h6 += g; + ctx->h7 += h; return /* burn_stack */ (8 + 16) * sizeof(u64) + sizeof(u32) + 3 * sizeof(void*); } -static uint -sha512_transform(void *context, const byte *data, size_t nblks) +void +sha512_update(struct sha512_context *ctx, const byte *buf, size_t len) { - struct sha512_context *ctx = context; - uint burn; + if (ctx->count) + { + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA512_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; + + if (ctx->count < SHA512_BLOCK_SIZE) + return; + + /* Process data from internal buffer */ + sha512_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; + } - do + if (!len) + return; + + /* Process data from input buffer */ + while (len >= SHA512_BLOCK_SIZE) { - burn = sha512_transform_block(&ctx->state, data) + 3 * sizeof(void*); - data += 128; + sha512_transform(ctx, buf); + ctx->nblocks++; + buf += SHA512_BLOCK_SIZE; + len -= SHA512_BLOCK_SIZE; } - while(--nblks); - return burn; + /* Copy remaining data to internal buffer */ + memcpy(ctx->buf, buf, len); + ctx->count = len; } -/* The routine final terminates the computation and - * returns the digest. - * The handle is prepared for a new cycle, but adding bytes to the - * handle will the destroy the returned buffer. - * Returns: 64 bytes representing the digest. When used for sha384, - * we take the leftmost 48 of those bytes. +/* + * The routine final terminates the computation and returns the digest. The + * handle is prepared for a new cycle, but adding bytes to the handle will the + * destroy the returned buffer. + * + * Returns: 64 bytes representing the digest. When used for sha384, we take the + * first 48 of those bytes. */ byte * sha512_final(struct sha512_context *ctx) { u64 t, th, msb, lsb; - byte *p; - sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; + sha512_update(ctx, NULL, 0); /* flush */ - t = ctx->bctx.nblocks; - /* if (sizeof t == sizeof ctx->bctx.nblocks) */ - th = ctx->bctx.nblocks_high; - /* else */ - /* th = ctx->bctx.nblocks >> 64; In case we ever use u128 */ + t = ctx->nblocks; + th = 0; /* multiply by 128 to make a byte count */ lsb = t << 7; msb = (th << 7) | (t >> 57); /* add the count */ t = lsb; - if ((lsb += ctx->bctx.count) < t) + if ((lsb += ctx->count) < t) msb++; /* multiply by 8 to make a bit count */ t = lsb; @@ -453,55 +454,56 @@ sha512_final(struct sha512_context *ctx) msb <<= 3; msb |= t >> 61; - if (ctx->bctx.count < 112) - { /* enough room */ - ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad */ - while(ctx->bctx.count < 112) - ctx->bctx.buf[ctx->bctx.count++] = 0; /* pad */ + if (ctx->count < 112) + { + /* enough room */ + ctx->buf[ctx->count++] = 0x80; /* pad */ + while(ctx->count < 112) + ctx->buf[ctx->count++] = 0; /* pad */ } else - { /* need one extra block */ - ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad character */ - while(ctx->bctx.count < 128) - ctx->bctx.buf[ctx->bctx.count++] = 0; - sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; - memset(ctx->bctx.buf, 0, 112); /* fill next block with zeroes */ + { + /* need one extra block */ + ctx->buf[ctx->count++] = 0x80; /* pad character */ + while(ctx->count < 128) + ctx->buf[ctx->count++] = 0; + sha512_update(ctx, NULL, 0); /* flush */ + memset(ctx->buf, 0, 112); /* fill next block with zeroes */ } + /* append the 128 bit count */ - put_u64(ctx->bctx.buf + 112, msb); - put_u64(ctx->bctx.buf + 120, lsb); - sha512_transform(ctx, ctx->bctx.buf, 1); - - p = ctx->bctx.buf; -#define X(a) do { put_u64(p, ctx->state.h##a); p += 8; } while(0) - X (0); - X (1); - X (2); - X (3); - X (4); - X (5); - /* Note that these last two chunks are included even for SHA384. - We just ignore them. */ - X (6); - X (7); + put_u64(ctx->buf + 112, msb); + put_u64(ctx->buf + 120, lsb); + sha512_transform(ctx, ctx->buf); + + byte *p = ctx->buf; +#define X(a) do { put_u64(p, ctx->h##a); p += 8; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); + X(5); + X(6); + X(7); #undef X - return ctx->bctx.buf; + return ctx->buf; } /* - * SHA512-HMAC + * SHA512-HMAC */ static void sha512_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha512_context hd_tmp; + struct sha512_context ctx; - sha512_init(&hd_tmp); - sha512_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha512_final(&hd_tmp), SHA512_SIZE); + sha512_init(&ctx); + sha512_update(&ctx, buffer, length); + memcpy(outbuf, sha512_final(&ctx), SHA512_SIZE); } void @@ -513,12 +515,12 @@ sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA512_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA512_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA512_BLOCK_SIZE - keylen); } else { sha512_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA512_SIZE, SHA512_BLOCK_SIZE - SHA512_SIZE); + memset(keybuf + SHA512_SIZE, 0, SHA512_BLOCK_SIZE - SHA512_SIZE); } /* Initialize the inner digest */ @@ -535,13 +537,15 @@ sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen sha512_update(&ctx->octx, buf, SHA512_BLOCK_SIZE); } -void sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha512_update(&ctx->ictx, buf, buflen); } -byte *sha512_hmac_final(struct sha512_hmac_context *ctx) +byte * +sha512_hmac_final(struct sha512_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha512_final(&ctx->ictx); @@ -553,17 +557,17 @@ byte *sha512_hmac_final(struct sha512_hmac_context *ctx) /* - * SHA384-HMAC + * SHA384-HMAC */ static void sha384_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha384_context hd_tmp; + struct sha384_context ctx; - sha384_init(&hd_tmp); - sha384_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha384_final(&hd_tmp), SHA384_SIZE); + sha384_init(&ctx); + sha384_update(&ctx, buffer, length); + memcpy(outbuf, sha384_final(&ctx), SHA384_SIZE); } void @@ -575,12 +579,12 @@ sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA384_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA384_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA384_BLOCK_SIZE - keylen); } else { sha384_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA384_SIZE, SHA384_BLOCK_SIZE - SHA384_SIZE); + memset(keybuf + SHA384_SIZE, 0, SHA384_BLOCK_SIZE - SHA384_SIZE); } /* Initialize the inner digest */ @@ -597,13 +601,15 @@ sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen sha384_update(&ctx->octx, buf, SHA384_BLOCK_SIZE); } -void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha384_update(&ctx->ictx, buf, buflen); } -byte *sha384_hmac_final(struct sha384_hmac_context *ctx) +byte * +sha384_hmac_final(struct sha384_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha384_final(&ctx->ictx); diff --git a/lib/sha512.h b/lib/sha512.h index bd998152..1614a3ac 100644 --- a/lib/sha512.h +++ b/lib/sha512.h @@ -13,7 +13,8 @@ #ifndef _BIRD_SHA512_H_ #define _BIRD_SHA512_H_ -#include "lib/sha256.h" +#include "nest/bird.h" + #define SHA384_SIZE 48 #define SHA384_HEX_SIZE 97 @@ -23,43 +24,41 @@ #define SHA512_HEX_SIZE 129 #define SHA512_BLOCK_SIZE 128 -struct sha512_state -{ + +struct sha512_context { u64 h0, h1, h2, h3, h4, h5, h6, h7; + byte buf[SHA512_BLOCK_SIZE]; + uint nblocks; + uint count; }; -struct sha512_context -{ - struct sha256_context bctx; - struct sha512_state state; -}; -#define sha384_context sha512_context /* aliasing 'struct sha384_context' to 'struct sha512_context' */ +#define sha384_context sha512_context void sha512_init(struct sha512_context *ctx); void sha384_init(struct sha384_context *ctx); -void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len); -static inline void sha384_update(struct sha384_context *ctx, const byte *in_buf, size_t in_len) -{ - sha512_update(ctx, in_buf, in_len); -} +void sha512_update(struct sha512_context *ctx, const byte *buf, size_t len); +static inline void sha384_update(struct sha384_context *ctx, const byte *buf, size_t len) +{ sha512_update(ctx, buf, len); } + +byte *sha512_final(struct sha512_context *ctx); +static inline byte *sha384_final(struct sha384_context *ctx) +{ return sha512_final(ctx); } -byte* sha512_final(struct sha512_context *ctx); -static inline byte* sha384_final(struct sha384_context *ctx) -{ - return sha512_final(ctx); -} /* * HMAC-SHA512, HMAC-SHA384 */ + struct sha512_hmac_context { struct sha512_context ictx; struct sha512_context octx; -} ; -#define sha384_hmac_context sha512_hmac_context /* aliasing 'struct sha384_hmac_context' to 'struct sha384_hmac_context' */ +}; + +#define sha384_hmac_context sha512_hmac_context + void sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen); void sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen); @@ -70,4 +69,5 @@ void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t byte *sha512_hmac_final(struct sha512_hmac_context *ctx); byte *sha384_hmac_final(struct sha384_hmac_context *ctx); + #endif /* _BIRD_SHA512_H_ */ From e422ca0f292d08a873deacdbffbb6c6cbd79f88a Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 24 Nov 2015 13:52:26 +0100 Subject: [PATCH 042/165] Some consts for function arguments Patch from Pavel Tvrdik --- lib/ip.c | 6 +++--- lib/ip.h | 4 ++-- lib/patmatch.c | 2 +- lib/string.h | 2 +- lib/unaligned.h | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/ip.c b/lib/ip.c index e1bfba49..2061a1f7 100644 --- a/lib/ip.c +++ b/lib/ip.c @@ -233,7 +233,7 @@ ip6_ntop(ip6_addr a, char *b) } int -ip4_pton(char *a, ip4_addr *o) +ip4_pton(const char *a, ip4_addr *o) { int i; unsigned long int l; @@ -258,11 +258,11 @@ ip4_pton(char *a, ip4_addr *o) } int -ip6_pton(char *a, ip6_addr *o) +ip6_pton(const char *a, ip6_addr *o) { u16 words[8]; int i, j, k, l, hfil; - char *start; + const char *start; if (a[0] == ':') /* Leading :: */ { diff --git a/lib/ip.h b/lib/ip.h index 90bb7f8a..e33adc6e 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -446,8 +446,8 @@ static inline char * ip4_ntox(ip4_addr a, char *b) static inline char * ip6_ntox(ip6_addr a, char *b) { return b + bsprintf(b, "%08x.%08x.%08x.%08x", _I0(a), _I1(a), _I2(a), _I3(a)); } -int ip4_pton(char *a, ip4_addr *o); -int ip6_pton(char *a, ip6_addr *o); +int ip4_pton(const char *a, ip4_addr *o); +int ip6_pton(const char *a, ip6_addr *o); // XXXX these functions must be redesigned or removed #ifdef IPV6 diff --git a/lib/patmatch.c b/lib/patmatch.c index 923e8f86..d2e1e313 100644 --- a/lib/patmatch.c +++ b/lib/patmatch.c @@ -16,7 +16,7 @@ #endif int -MATCH_FUNC_NAME(byte *p, byte *s) +MATCH_FUNC_NAME(const byte *p, const byte *s) { while (*p) { diff --git a/lib/string.h b/lib/string.h index 528a1a19..218f7b1c 100644 --- a/lib/string.h +++ b/lib/string.h @@ -22,6 +22,6 @@ int buffer_vprint(buffer *buf, const char *fmt, va_list args); int buffer_print(buffer *buf, const char *fmt, ...); void buffer_puts(buffer *buf, const char *str); -int patmatch(byte *pat, byte *str); +int patmatch(const byte *pat, const byte *str); #endif diff --git a/lib/unaligned.h b/lib/unaligned.h index a2dbae4f..dc777fbf 100644 --- a/lib/unaligned.h +++ b/lib/unaligned.h @@ -20,7 +20,7 @@ #include "lib/string.h" static inline u16 -get_u16(void *p) +get_u16(const void *p) { u16 x; memcpy(&x, p, 2); @@ -28,7 +28,7 @@ get_u16(void *p) } static inline u32 -get_u32(void *p) +get_u32(const void *p) { u32 x; memcpy(&x, p, 4); From ad27615760e2795da3efe5e97c0e888281d5ca59 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 10 Nov 2015 14:59:41 +0100 Subject: [PATCH 043/165] Netlink: attribute validation before parsing Wanted netlink attributes are defined in a table, specifying their size and neediness. Removing the long conditions that did the validation before. Also parsing IPv4 and IPv6 versions regardless on the IPV6 macro. --- sysdep/linux/netlink.c | 187 +++++++++++++++++++++++++++++++---------- 1 file changed, 141 insertions(+), 46 deletions(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index efbf41a6..640d1877 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -226,24 +226,98 @@ nl_checkin(struct nlmsghdr *h, int lsize) return NLMSG_DATA(h); } +struct nl_want_attrs { + u8 defined:1; + u8 checksize:1; + u8 size; +}; + + +#define BIRD_IFLA_MAX (IFLA_WIRELESS+1) + +static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = { + [IFLA_IFNAME] = { 1, 0, 0 }, + [IFLA_MTU] = { 1, 1, sizeof(u32) }, + [IFLA_WIRELESS] = { 1, 0, 0 }, +}; + + +#define BIRD_IFA_MAX (IFA_ANYCAST+1) + +#ifndef IPV6 +static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = { + [IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) }, + [IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) }, + [IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) }, +}; +#else +static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = { + [IFA_ADDRESS] = { 1, 1, sizeof(ip6_addr) }, + [IFA_LOCAL] = { 1, 1, sizeof(ip6_addr) }, +}; +#endif + + +#define BIRD_RTA_MAX (RTA_TABLE+1) + +static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = { + [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) }, +}; + +#ifndef IPV6 +static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = { + [RTA_DST] = { 1, 1, sizeof(ip4_addr) }, + [RTA_OIF] = { 1, 1, sizeof(u32) }, + [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) }, + [RTA_PRIORITY] = { 1, 1, sizeof(u32) }, + [RTA_PREFSRC] = { 1, 1, sizeof(ip4_addr) }, + [RTA_METRICS] = { 1, 0, 0 }, + [RTA_MULTIPATH] = { 1, 0, 0 }, + [RTA_FLOW] = { 1, 1, sizeof(u32) }, + [RTA_TABLE] = { 1, 1, sizeof(u32) }, +}; +#else +static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = { + [RTA_DST] = { 1, 1, sizeof(ip6_addr) }, + [RTA_IIF] = { 1, 1, sizeof(u32) }, + [RTA_OIF] = { 1, 1, sizeof(u32) }, + [RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) }, + [RTA_PRIORITY] = { 1, 1, sizeof(u32) }, + [RTA_PREFSRC] = { 1, 1, sizeof(ip6_addr) }, + [RTA_METRICS] = { 1, 0, 0 }, + [RTA_FLOW] = { 1, 1, sizeof(u32) }, + [RTA_TABLE] = { 1, 1, sizeof(u32) }, +}; +#endif + + static int -nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) +nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize) { int max = ksize / sizeof(struct rtattr *); bzero(k, ksize); - while (RTA_OK(a, nl_attr_len)) + + for ( ; RTA_OK(a, nl_attr_len); a = RTA_NEXT(a, nl_attr_len)) { - if (a->rta_type < max) - k[a->rta_type] = a; - a = RTA_NEXT(a, nl_attr_len); + if ((a->rta_type >= max) || !want[a->rta_type].defined) + continue; + + if (want[a->rta_type].checksize && (RTA_PAYLOAD(a) != want[a->rta_type].size)) + { + log(L_ERR "nl_parse_attrs: Malformed message received"); + return 0; + } + + k[a->rta_type] = a; } + if (nl_attr_len) { log(L_ERR "nl_parse_attrs: remnant of size %d", nl_attr_len); return 0; } - else - return 1; + + return 1; } static inline u32 rta_get_u32(struct rtattr *a) @@ -350,7 +424,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) static int nh_buf_size; /* in number of structures */ static int nh_buf_used; - struct rtattr *a[RTA_CACHEINFO+1]; + struct rtattr *a[BIRD_RTA_MAX]; struct rtnexthop *nh = RTA_DATA(ra); struct mpnh *rv, *first, **last; int len = RTA_PAYLOAD(ra); @@ -381,12 +455,9 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) /* Nonexistent RTNH_PAYLOAD ?? */ nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0); - nl_parse_attrs(RTNH_DATA(nh), a, sizeof(a)); + nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a)); if (a[RTA_GATEWAY]) { - if (RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) - return NULL; - memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr)); ipa_ntoh(rv->gw); @@ -455,7 +526,7 @@ static void nl_parse_link(struct nlmsghdr *h, int scan) { struct ifinfomsg *i; - struct rtattr *a[IFLA_WIRELESS+1]; + struct rtattr *a[BIRD_IFLA_MAX]; int new = h->nlmsg_type == RTM_NEWLINK; struct iface f = {}; struct iface *ifi; @@ -463,15 +534,23 @@ nl_parse_link(struct nlmsghdr *h, int scan) u32 mtu; uint fl; - if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), a, sizeof(a))) + if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), ifla_attr_want, a, sizeof(a))) return; - if (!a[IFLA_IFNAME] || RTA_PAYLOAD(a[IFLA_IFNAME]) < 2 || - !a[IFLA_MTU] || RTA_PAYLOAD(a[IFLA_MTU]) != 4) + if (!a[IFLA_IFNAME] || (RTA_PAYLOAD(a[IFLA_IFNAME]) < 2) || !a[IFLA_MTU]) { - if (scan || !a[IFLA_WIRELESS]) - log(L_ERR "nl_parse_link: Malformed message received"); + /* + * IFLA_IFNAME and IFLA_MTU are required, in fact, but there may also come + * a message with IFLA_WIRELESS set, where (e.g.) no IFLA_IFNAME exists. + * We simply ignore all such messages with IFLA_WIRELESS without notice. + */ + + if (a[IFLA_WIRELESS]) + return; + + log(L_ERR "KIF: Malformed message received"); return; } + name = RTA_DATA(a[IFLA_IFNAME]); mtu = rta_get_u32(a[IFLA_MTU]); @@ -522,26 +601,40 @@ static void nl_parse_addr(struct nlmsghdr *h, int scan) { struct ifaddrmsg *i; - struct rtattr *a[IFA_ANYCAST+1]; + struct rtattr *a[BIRD_IFA_MAX]; int new = h->nlmsg_type == RTM_NEWADDR; struct ifa ifa; struct iface *ifi; int scope; - if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFA_RTA(i), a, sizeof(a))) + if (!(i = nl_checkin(h, sizeof(*i)))) return; - if (i->ifa_family != BIRD_AF) - return; - if (!a[IFA_ADDRESS] || RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip_addr) -#ifdef IPV6 - || a[IFA_LOCAL] && RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr) + + switch (i->ifa_family) + { +#ifndef IPV6 + case AF_INET: + if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a))) + return; + if (!a[IFA_LOCAL]) + { + log(L_ERR "KIF: Malformed message received (missing IFA_LOCAL)"); + return; + } + break; #else - || !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr) - || (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr)) + case AF_INET6: + if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a))) + return; + break; #endif - ) + default: + return; + } + + if (!a[IFA_ADDRESS]) { - log(L_ERR "nl_parse_addr: Malformed message received"); + log(L_ERR "KIF: Malformed message received (missing IFA_ADDRESS)"); return; } @@ -835,7 +928,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) { struct krt_proto *p; struct rtmsg *i; - struct rtattr *a[RTA_TABLE+1]; + struct rtattr *a[BIRD_RTA_MAX]; int new = h->nlmsg_type == RTM_NEWROUTE; ip_addr dst = IPA_NONE; @@ -843,25 +936,27 @@ nl_parse_route(struct nlmsghdr *h, int scan) u32 table; int src; - if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a))) + if (!(i = nl_checkin(h, sizeof(*i)))) return; - if (i->rtm_family != BIRD_AF) - return; - if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) || -#ifdef IPV6 - (a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) || -#endif - (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) || - (a[RTA_TABLE] && RTA_PAYLOAD(a[RTA_TABLE]) != 4) || - (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) || - (a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) || - (a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) || - (a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_FLOW]) != 4)) + + switch (i->rtm_family) { - log(L_ERR "KRT: Malformed message received"); - return; +#ifndef IPV6 + case AF_INET: + if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want4, a, sizeof(a))) + return; + break; +#else + case AF_INET6: + if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a))) + return; + break; +#endif + default: + return; } + if (a[RTA_DST]) { memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst)); @@ -938,7 +1033,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) { case RTN_UNICAST: - if (a[RTA_MULTIPATH]) + if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET)) { ra.dest = RTD_MULTIPATH; ra.nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]); From 33b4f40acce02c90b4b7766c5c94ebf2d22765c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:08:28 +0100 Subject: [PATCH 044/165] MD5: Mormalize naming style --- lib/md5.c | 442 +++++++++++++++++++++++++------------------- lib/md5.h | 55 ++++-- proto/ospf/packet.c | 31 ++-- proto/rip/packets.c | 18 +- 4 files changed, 322 insertions(+), 224 deletions(-) diff --git a/lib/md5.c b/lib/md5.c index ad284f07..8efa62d6 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -1,154 +1,159 @@ /* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. + * BIRD Library -- MD5 Hash Function and HMAC-MD5 Function * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. + * (c) 2015 CZ.NIC z.s.p.o. * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -/* - * Adapted for BIRD by Martin Mares + * The code was written by Colin Plumb in 1993, no copyright is claimed. + * + * Adapted for BIRD by Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. */ -#include "nest/bird.h" -#include "lib/string.h" -#include "md5.h" +#include "lib/md5.h" #ifdef CPU_LITTLE_ENDIAN #define byteReverse(buf, len) /* Nothing */ #else -void byteReverse(unsigned char *buf, unsigned longs); +void byteReverse(byte *buf, uint longs); /* * Note: this code is harmless on little-endian machines. */ -void byteReverse(unsigned char *buf, unsigned longs) +void byteReverse(byte *buf, uint longs) { - u32 t; - do { - t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(u32 *) buf = t; - buf += 4; - } while (--longs); + u32 t; + do { + t = (u32) ((uint) buf[3] << 8 | buf[2]) << 16 | + ((uint) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); } #endif +static void md5_transform(u32 buf[4], u32 const in[16]); + /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -void MD5Init(struct MD5Context *ctx) +void +md5_init(struct md5_context *ctx) { - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; - ctx->bits[0] = 0; - ctx->bits[1] = 0; + ctx->bits[0] = 0; + ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ -void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +void +md5_update(struct md5_context *ctx, const byte *buf, uint len) { - u32 t; + u32 t; - /* Update bitcount */ + /* Update bitcount */ - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((u32) len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - /* Handle any leading odd-sized chunks */ + /* Handle any leading odd-sized chunks */ + if (t) + { + byte *p = (byte *) ctx->in + t; - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - buf += t; - len -= t; - } - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - buf += 64; - len -= 64; + t = 64 - t; + if (len < t) + { + memcpy(p, buf, len); + return; } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + md5_transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + while (len >= 64) + { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + md5_transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); } /* - * Final wrapup - pad to 64-byte boundary with the bit pattern + * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ -void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +byte * +md5_final(struct md5_context *ctx) { - unsigned count; - unsigned char *p; + uint count; + byte *p; - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; - /* Set the first char of padding to 0x80. This is safe since there is + /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - ((u32 *) ctx->in)[14] = ctx->bits[0]; - ((u32 *) ctx->in)[15] = ctx->bits[1]; + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + md5_transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } + else + { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + md5_transform(ctx->buf, (u32 *) ctx->in); + byteReverse((byte *) ctx->buf, 4); + + return (byte*) ctx->buf; +} - MD5Transform(ctx->buf, (u32 *) ctx->in); - byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +/* I am a hard paranoid */ +void +md5_erase_ctx(struct md5_context *ctx) +{ + memset((char *) ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ @@ -161,92 +166,157 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ -void MD5Transform(u32 buf[4], u32 const in[16]) +void +md5_transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + + +/* + * MD5-HMAC + */ + +static void +md5_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct md5_context hd_tmp; + + md5_init(&hd_tmp); + md5_update(&hd_tmp, buffer, length); + memcpy(outbuf, md5_final(&hd_tmp), MD5_SIZE); +} + +void +md5_hmac_init(struct md5_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[MD5_BLOCK_SIZE], buf[MD5_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= MD5_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, MD5_BLOCK_SIZE - keylen); + } + else + { + md5_hash_buffer(keybuf, key, keylen); + bzero(keybuf + MD5_SIZE, MD5_BLOCK_SIZE - MD5_SIZE); + } + + /* Initialize the inner digest */ + md5_init(&ctx->ictx); + int i; + for (i = 0; i < MD5_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + md5_update(&ctx->ictx, buf, MD5_BLOCK_SIZE); + + /* Initialize the outer digest */ + md5_init(&ctx->octx); + for (i = 0; i < MD5_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + md5_update(&ctx->octx, buf, MD5_BLOCK_SIZE); +} + +void +md5_hmac_update(struct md5_hmac_context *ctx, const byte *buf, size_t buflen) { - register u32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; + /* Just update the inner digest */ + md5_update(&ctx->ictx, buf, buflen); +} + +byte * +md5_hmac_final(struct md5_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = md5_final(&ctx->ictx); + + /* Finish the outer digest */ + md5_update(&ctx->octx, isha, MD5_SIZE); + return md5_final(&ctx->octx); } diff --git a/lib/md5.h b/lib/md5.h index 12586357..034d764c 100644 --- a/lib/md5.h +++ b/lib/md5.h @@ -1,16 +1,47 @@ -#ifndef MD5_H -#define MD5_H +/* + * BIRD Library -- MD5 Hash Function and HMAC-MD5 Function + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Adapted for BIRD by Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - unsigned char in[64]; +#ifndef _BIRD_MD5_H_ +#define _BIRD_MD5_H_ + +#include "nest/bird.h" + + +#define MD5_SIZE 16 +#define MD5_HEX_SIZE 33 +#define MD5_BLOCK_SIZE 64 + + +struct md5_context { + u32 buf[4]; + u32 bits[2]; + byte in[64]; }; -void MD5Init(struct MD5Context *context); -void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -void MD5Final(unsigned char digest[16], struct MD5Context *context); -void MD5Transform(u32 buf[4], u32 const in[16]); +void md5_init(struct md5_context *ctx); +void md5_update(struct md5_context *ctx, const byte *buf, uint len); +byte *md5_final(struct md5_context *ctx); + + +/* + * HMAC-MD5 + */ + +struct md5_hmac_context { + struct md5_context ictx; + struct md5_context octx; +}; + +void md5_hmac_init(struct md5_hmac_context *ctx, const byte *key, size_t keylen); +void md5_hmac_update(struct md5_hmac_context *ctx, const byte *buf, size_t buflen); +byte *md5_hmac_final(struct md5_hmac_context *ctx); + -#endif /* !MD5_H */ +#endif /* _BIRD_MD5_H_ */ diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 5af9e875..faa33664 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -11,6 +11,7 @@ #include "ospf.h" #include "nest/password.h" #include "lib/md5.h" +#include "lib/socket.h" void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) @@ -108,11 +109,11 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) char password[OSPF_AUTH_CRYPT_SIZE]; strncpy(password, passwd->password, sizeof(password)); - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) pkt, plen); - MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); - MD5Final(tail, &ctxt); + struct md5_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); break; default: @@ -174,19 +175,17 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ if (!pass) DROP("no suitable password found", auth->md5.keyid); - void *tail = ((void *) pkt) + plen; - char passwd[OSPF_AUTH_CRYPT_SIZE]; - char md5sum[OSPF_AUTH_CRYPT_SIZE]; - - strncpy(passwd, pass->password, OSPF_AUTH_CRYPT_SIZE); + 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); - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) pkt, plen); - MD5Update(&ctxt, passwd, OSPF_AUTH_CRYPT_SIZE); - MD5Final(md5sum, &ctxt); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, plen + OSPF_AUTH_CRYPT_SIZE); + char *computed = md5_final(&ctx); - if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) + if (memcmp(received, computed, OSPF_AUTH_CRYPT_SIZE)) DROP("wrong MD5 digest", pass->id); if (n) diff --git a/proto/rip/packets.c b/proto/rip/packets.c index be20734f..9f10fd67 100644 --- a/proto/rip/packets.c +++ b/proto/rip/packets.c @@ -241,10 +241,10 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p *plen += sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (byte *) pkt, *plen); - MD5Final(tail->auth_data, &ctxt); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, *plen); + memcpy(tail->auth_data, md5_final(&ctx), RIP_MD5_LENGTH); return; default: @@ -312,15 +312,13 @@ rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_ } char received[RIP_MD5_LENGTH]; - char computed[RIP_MD5_LENGTH]; - memcpy(received, tail->auth_data, RIP_MD5_LENGTH); strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (byte *) pkt, *plen); - MD5Final(computed, &ctxt); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, *plen); + char *computed = md5_final(&ctx); if (memcmp(received, computed, RIP_MD5_LENGTH)) DROP("wrong MD5 digest", pass->id); From 52e21323b6c49af9d076586241451973a7d1e7c6 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 25 Nov 2015 15:52:58 +0100 Subject: [PATCH 045/165] BGP: Update capability number from IANA for extended messages --- proto/bgp/packets.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index ed99f623..72ca3728 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -163,6 +163,14 @@ bgp_put_cap_rr(struct bgp_proto *p UNUSED, byte *buf) return buf; } +static byte * +bgp_put_cap_ext_msg(struct bgp_proto *p UNUSED, byte *buf) +{ + *buf++ = 6; /* Capability 6: Support for extended messages */ + *buf++ = 0; /* Capability data length */ + return buf; +} + static byte * bgp_put_cap_gr1(struct bgp_proto *p, byte *buf) { @@ -223,14 +231,6 @@ bgp_put_cap_err(struct bgp_proto *p UNUSED, byte *buf) return buf; } -static byte * -bgp_put_cap_ext_msg(struct bgp_proto *p UNUSED, byte *buf) -{ - *buf++ = 230; /* Capability TBD: Support for extended messages */ - *buf++ = 0; /* Capability data length */ - return buf; -} - static byte * bgp_create_open(struct bgp_conn *conn, byte *buf) @@ -827,6 +827,12 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) conn->peer_refresh_support = 1; break; + case 6: /* Extended message length capability, draft */ + if (cl != 0) + goto err; + conn->peer_ext_messages_support = 1; + break; + case 64: /* Graceful restart capability, RFC 4724 */ if (cl % 4 != 2) goto err; @@ -867,12 +873,6 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) conn->peer_enhanced_refresh_support = 1; break; - case 230: /* Extended message length capability, draft, cap number TBD */ - if (cl != 0) - goto err; - conn->peer_ext_messages_support = 1; - break; - /* We can safely ignore all other capabilities */ } len -= 2 + cl; From 487c6961cb29046dbe9560262e3e742e38691b83 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 11 Feb 2016 16:38:28 +0100 Subject: [PATCH 046/165] BGP: Fix bug in incoming connection handling When a BGP session was established by an outgoing connection with Graceful Restart behavior negotiated, a pending incoming connection in OpenSent state, and another incoming connection was received, then the outgoing connection (and whole BGP session) was closed, but the old incoming connection was just overwritten by the new one. That later caused a crash when the hold timer from the old connection fired. --- proto/bgp/bgp.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index f549b0ed..11f33b14 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -813,7 +813,13 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) return 0; } - /* We are in proper state and there is no other incoming connection */ + /* + * BIRD should keep multiple incoming connections in OpenSent state (for + * details RFC 4271 8.2.1 par 3), but it keeps just one. Duplicate incoming + * connections are rejected istead. The exception is the case where an + * incoming connection triggers a graceful restart. + */ + acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) && (p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk); @@ -823,6 +829,10 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) bgp_handle_graceful_restart(p); bgp_conn_enter_idle_state(p->conn); acc = 1; + + /* There might be separate incoming connection in OpenSent state */ + if (p->incoming_conn.state > BS_ACTIVE) + bgp_close_conn(&p->incoming_conn); } BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s", From c2106b674ca632f7c0bffd7cab4b1940f74d353c Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 11 Feb 2016 21:53:55 +0100 Subject: [PATCH 047/165] Unix: Fix bug in syslog name handling Pointer to current_log_name has to be changed even if the name is the same, because the old one will be invalid/freed after reconfiguration. --- sysdep/unix/log.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index 7cb26360..b90bbbd2 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -284,17 +284,18 @@ log_switch(int debug, list *l, char *new_syslog_name) current_log_list = l; #ifdef HAVE_SYSLOG - if (current_syslog_name && new_syslog_name && - !strcmp(current_syslog_name, new_syslog_name)) + char *old_syslog_name = current_syslog_name; + current_syslog_name = new_syslog_name; + + if (old_syslog_name && new_syslog_name && + !strcmp(old_syslog_name, new_syslog_name)) return; - if (current_syslog_name) + if (old_syslog_name) closelog(); if (new_syslog_name) openlog(new_syslog_name, LOG_CONS | LOG_NDELAY, LOG_DAEMON); - - current_syslog_name = new_syslog_name; #endif } From 9c9cc35c0273f8bcae10fb8b546d199514b2bbc5 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 16 Feb 2016 17:33:58 +0100 Subject: [PATCH 048/165] Filter: Implement last_nonaggregated operator on bgp_path --- doc/bird.sgml | 5 ++++- filter/config.Y | 3 ++- filter/filter.c | 8 ++++++++ nest/a-path.c | 31 ++++++++++++++++++++++++++++++- nest/attrs.h | 1 + 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 86df0456..c5316d87 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1119,9 +1119,12 @@ foot). returns the last ASN (the source ASN) in path returns the last ASN in the non-aggregated part of the path returns the length of path code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; } | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; } | term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; } + | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; } /* Communities */ /* This causes one shift/reduce conflict diff --git a/filter/filter.c b/filter/filter.c index 55062aca..eddf4228 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1091,6 +1091,14 @@ interpret(struct f_inst *what) res.type = T_INT; res.val.i = as; break; + case P('a','L'): /* Get last ASN from non-aggregated part of AS PATH */ + ONEARG; + if (v1.type != T_PATH) + runtime( "AS path expected" ); + + res.type = T_INT; + res.val.i = as_path_get_last_nonaggregated(v1.val.ad); + break; case 'r': ONEARG; res = v1; diff --git a/nest/a-path.c b/nest/a-path.c index c9c5aefb..32e2d27e 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -220,7 +220,7 @@ as_path_get_last(struct adata *path, u32 *orig_as) p += BS * len; } break; - default: bug("as_path_get_first: Invalid path segment"); + default: bug("Invalid path segment"); } } @@ -229,6 +229,35 @@ as_path_get_last(struct adata *path, u32 *orig_as) return found; } +u32 +as_path_get_last_nonaggregated(struct adata *path) +{ + u8 *p = path->data; + u8 *q = p+path->length; + u32 res = 0; + int len; + + while (p Date: Wed, 20 Jan 2016 15:23:17 +0100 Subject: [PATCH 049/165] All the current pthread implementations are OK and working with us. No more need to disable pthread for specific BSD's. --- configure.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/configure.in b/configure.in index c81709e6..b9220a1d 100644 --- a/configure.in +++ b/configure.in @@ -85,12 +85,12 @@ if test -z "$GCC" ; then fi # Enable threads by default just in Linux and FreeBSD -if test "$enable_pthreads" = try ; then - case "$host_os" in - (linux* | freebsd*) enable_pthreads=try ;; - (*) enable_pthreads=no ;; - esac -fi +#if test "$enable_pthreads" = try ; then +# case "$host_os" in +# (linux* | freebsd* | openbsd* | netbsd* ) enable_pthreads=try ;; +# (*) enable_pthreads=no ;; +# esac +#fi if test "$enable_pthreads" != no ; then BIRD_CHECK_PTHREADS From e3f506f9b53bd8e44976df1c935c7ec417793ace Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 25 Feb 2016 18:16:59 +0100 Subject: [PATCH 050/165] OSPF: Multicast ability is irrelevant for stub interfaces --- proto/ospf/iface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 77ce839a..67ae094d 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -599,10 +599,10 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i if (ospf_is_v2(p) && (ifa->type == OSPF_IT_NBMA) && (addr->flags & IA_PEER)) ifa->type = OSPF_IT_PTMP; - if ((ifa->type == OSPF_IT_BCAST) && !(iface->flags & if_multi_flag)) + if ((ifa->type == OSPF_IT_BCAST) && !(iface->flags & if_multi_flag) && !ifa->stub) ifa->type = OSPF_IT_NBMA; - if ((ifa->type == OSPF_IT_PTP) && !(iface->flags & if_multi_flag)) + if ((ifa->type == OSPF_IT_PTP) && !(iface->flags & if_multi_flag) && !ifa->stub) ifa->type = OSPF_IT_PTMP; if (ifa->type != old_type) From e1c13a5a7b86f2ba09178300bad960224658c833 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Wed, 9 Mar 2016 12:12:02 +0100 Subject: [PATCH 051/165] Unix: Rework of select-loop to poll-loop This should lift the limit of FD_SETSIZE and allow more than 1024 fd's. FD_SETSIZE limit doesn't matter now when creating new sockets. --- sysdep/unix/io.c | 101 ++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index b636e799..bc212de1 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -41,12 +42,12 @@ #include "lib/sysio.h" /* Maximum number of calls of tx handler for one socket in one - * select iteration. Should be small enough to not monopolize CPU by + * poll iteration. Should be small enough to not monopolize CPU by * one protocol instance. */ #define MAX_STEPS 4 -/* Maximum number of calls of rx handler for all sockets in one select +/* Maximum number of calls of rx handler for all sockets in one poll iteration. RX callbacks are often much more costly so we limit this to gen small latencies */ #define MAX_RX_STEPS 4 @@ -1022,7 +1023,6 @@ sk_log_error(sock *s, const char *p) static list sock_list; static struct birdsock *current_sock; static struct birdsock *stored_sock; -static int sock_recalc_fdsets_p; static inline sock * sk_next(sock *s) @@ -1078,7 +1078,6 @@ sk_free(resource *r) if (s == stored_sock) stored_sock = sk_next(s); rem_node(&s->n); - sock_recalc_fdsets_p = 1; } } @@ -1276,7 +1275,6 @@ static void sk_insert(sock *s) { add_tail(&sock_list, &s->n); - sock_recalc_fdsets_p = 1; } static void @@ -1328,18 +1326,6 @@ sk_passive_connected(sock *s, int type) log(L_WARN "SOCK: Cannot get remote IP address for TCP<"); } - if (fd >= FD_SETSIZE) - { - /* FIXME: Call err_hook instead ? */ - log(L_ERR "SOCK: Incoming connection from %I%J (port %d) %s", - t->daddr, ipa_is_link_local(t->daddr) ? t->iface : NULL, - t->dport, "rejected due to FD_SETSIZE limit"); - close(fd); - t->fd = -1; - rfree(t); - return 1; - } - if (sk_setup(t) < 0) { /* FIXME: Call err_hook instead ? */ @@ -1416,9 +1402,6 @@ sk_open(sock *s) if (fd < 0) ERR("socket"); - if (fd >= FD_SETSIZE) - ERR2("FD_SETSIZE limit reached"); - s->af = af; s->fd = fd; @@ -2062,15 +2045,15 @@ static int short_loops = 0; void io_loop(void) { - fd_set rd, wr; - struct timeval timo; + int poll_tout; time_t tout; - int hi, events; + int nfds, events; sock *s; node *n; + int fdmax = 256; + struct pollfd *pfd = xmalloc(fdmax * sizeof(struct pollfd)); watchdog_start1(); - sock_recalc_fdsets_p = 1; for(;;) { events = ev_run_list(&global_event_list); @@ -2081,43 +2064,43 @@ io_loop(void) tm_shot(); continue; } - timo.tv_sec = events ? 0 : MIN(tout - now, 3); - timo.tv_usec = 0; + poll_tout = (events ? 0 : MIN(tout - now, 3)) * 1000; /* Time in milliseconds */ io_close_event(); - if (sock_recalc_fdsets_p) - { - sock_recalc_fdsets_p = 0; - FD_ZERO(&rd); - FD_ZERO(&wr); - } - - hi = 0; + nfds = 0; WALK_LIST(n, sock_list) { + pfd[nfds] = (struct pollfd) { .fd = -1 }; /* everything other set to 0 by this */ s = SKIP_BACK(sock, n, n); if (s->rx_hook) { - FD_SET(s->fd, &rd); - if (s->fd > hi) - hi = s->fd; + pfd[nfds].fd = s->fd; + pfd[nfds].events |= POLLIN; } - else - FD_CLR(s->fd, &rd); if (s->tx_hook && s->ttx != s->tpos) { - FD_SET(s->fd, &wr); - if (s->fd > hi) - hi = s->fd; + pfd[nfds].fd = s->fd; + pfd[nfds].events |= POLLOUT; + } + if (pfd[nfds].fd != -1) + { + s->index = nfds; + nfds++; } else - FD_CLR(s->fd, &wr); + s->index = -1; + + if (nfds >= fdmax) + { + fdmax *= 2; + pfd = xrealloc(pfd, fdmax * sizeof(struct pollfd)); + } } /* * Yes, this is racy. But even if the signal comes before this test - * and entering select(), it gets caught on the next timer tick. + * and entering poll(), it gets caught on the next timer tick. */ if (async_config_flag) @@ -2142,18 +2125,18 @@ io_loop(void) continue; } - /* And finally enter select() to find active sockets */ + /* And finally enter poll() to find active sockets */ watchdog_stop(); - hi = select(hi+1, &rd, &wr, NULL, &timo); + events = poll(pfd, nfds, poll_tout); watchdog_start(); - if (hi < 0) + if (events < 0) { if (errno == EINTR || errno == EAGAIN) continue; - die("select: %m"); + die("poll: %m"); } - if (hi) + if (events) { /* guaranteed to be non-empty */ current_sock = SKIP_BACK(sock, n, HEAD(sock_list)); @@ -2161,11 +2144,17 @@ io_loop(void) while (current_sock) { sock *s = current_sock; + if (s->index == -1) + { + current_sock = sk_next(s); + goto next; + } + int e; int steps; steps = MAX_STEPS; - if ((s->type >= SK_MAGIC) && FD_ISSET(s->fd, &rd) && s->rx_hook) + if ((s->type >= SK_MAGIC) && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) do { steps--; @@ -2177,7 +2166,7 @@ io_loop(void) while (e && s->rx_hook && steps); steps = MAX_STEPS; - if (FD_ISSET(s->fd, &wr)) + if (pfd[s->index].revents & POLLOUT) do { steps--; @@ -2204,13 +2193,17 @@ io_loop(void) while (current_sock && count < MAX_RX_STEPS) { sock *s = current_sock; - int e UNUSED; + if (s->index == -1) + { + current_sock = sk_next(s); + goto next2; + } - if ((s->type < SK_MAGIC) && FD_ISSET(s->fd, &rd) && s->rx_hook) + if ((s->type < SK_MAGIC) && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) { count++; io_log_event(s->rx_hook, s->data); - e = sk_read(s); + sk_read(s); if (s != current_sock) goto next2; } From fd926ed4eea319b94bd0e09e093b90846bcb169b Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 15 Mar 2016 14:57:49 +0100 Subject: [PATCH 052/165] Poll: Prevent the improbable case of EAGAIN after POLLIN --- proto/bfd/io.c | 4 ++-- sysdep/unix/io.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/proto/bfd/io.c b/proto/bfd/io.c index fb150040..79ed9af7 100644 --- a/proto/bfd/io.c +++ b/proto/bfd/io.c @@ -576,7 +576,7 @@ sockets_close_fds(struct birdloop *loop) loop->close_scheduled = 0; } -int sk_read(sock *s); +int sk_read(sock *s, int revents); int sk_write(sock *s); static void @@ -605,7 +605,7 @@ sockets_fire(struct birdloop *loop) if (pfd->revents & POLLIN) while (e && *psk && (*psk)->rx_hook) - e = sk_read(*psk); + e = sk_read(*psk, 0); e = 1; if (pfd->revents & POLLOUT) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index bc212de1..b769de58 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1760,7 +1760,7 @@ sk_send_full(sock *s, unsigned len, struct iface *ifa, /* sk_read() and sk_write() are called from BFD's event loop */ int -sk_read(sock *s) +sk_read(sock *s, int revents) { switch (s->type) { @@ -1779,6 +1779,11 @@ sk_read(sock *s) { if (errno != EINTR && errno != EAGAIN) s->err_hook(s, errno); + else if (errno == EAGAIN && !(revents & POLLIN)) + { + log(L_ERR "Got EAGAIN from read when revents=%x (without POLLIN)", revents); + s->err_hook(s, 0); + } } else if (!c) s->err_hook(s, 0); @@ -2159,7 +2164,7 @@ io_loop(void) { steps--; io_log_event(s->rx_hook, s->data); - e = sk_read(s); + e = sk_read(s, pfd[s->index].revents); if (s != current_sock) goto next; } @@ -2203,7 +2208,7 @@ io_loop(void) { count++; io_log_event(s->rx_hook, s->data); - sk_read(s); + sk_read(s, pfd[s->index].revents); if (s != current_sock) goto next2; } From 79a4f74a65941603cc42680d2e61b00ec88abe97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Tue, 15 Mar 2016 10:29:32 +0100 Subject: [PATCH 053/165] BGP: Add documentaion for extended messages --- doc/bird.sgml | 9 +++++++-- nest/config.Y | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index c5316d87..73061202 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1862,6 +1862,11 @@ using the following configuration parameters: in neighbor's implementation of 4B AS extension. Even when disabled (off), BIRD behaves internally as AS4-aware BGP router. Default: on. + enable extended messages + The BGP protocol uses maximum message length of 4096 bytes. This option + provides an extension to allow extended messages with length up + to 65535 bytes. Default: off. + capabilities Use capability advertisement to advertise optional capabilities. This is standard behavior for newer BGP implementations, but there might be some @@ -2057,7 +2062,7 @@ protocol bgp { multihop; # ... which is connected indirectly export filter { # We use non-trivial export rules if source = RTS_STATIC then { # Export only static routes - # Assign our community + # Assign our community bgp_community.add((65000,64501)); # Artificially increase path length # by advertising local AS number twice @@ -2266,7 +2271,7 @@ these attributes: ip (Linux) The preferred source address. Used in source address selection for - outgoing packets. Has to be one of the IP addresses of the router. + outgoing packets. Has to be one of the IP addresses of the router. int (Linux) The realm of the route. Can be used for traffic classification. diff --git a/nest/config.Y b/nest/config.Y index 799a09f9..87827c10 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -111,7 +111,7 @@ idval: $$ = ipa_to_u32(SYM_VAL($1).px.ip); #endif else - cf_error("Number of IPv4 address constant expected"); + cf_error("Number or IPv4 address constant expected"); } ; From 9036bbf2b7cc781c87f2a6b3979198f77ec6ada1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Tue, 15 Mar 2016 14:55:40 +0100 Subject: [PATCH 054/165] RIP: fix typo in configuration at rx length opt --- proto/rip/config.Y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/rip/config.Y b/proto/rip/config.Y index 29ea7eb1..083d2e91 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -131,7 +131,7 @@ rip_iface_item: | TIMEOUT TIME expr { RIP_IFACE->timeout_time = $3; if ($3<=0) cf_error("Timeout time must be positive"); } | GARBAGE TIME expr { RIP_IFACE->garbage_time = $3; if ($3<=0) cf_error("Garbage time must be positive"); } | ECMP WEIGHT expr { RIP_IFACE->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); } - | RX BUFFER expr { RIP_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); } + | RX BUFFER expr { RIP_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX length must be in range 256-65535"); } | TX LENGTH expr { RIP_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); } | TX tos { RIP_IFACE->tx_tos = $2; } | TX PRIORITY expr { RIP_IFACE->tx_priority = $3; } From 9c92f69272de3795f7289969e815d99a93d0d9b3 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Fri, 18 Mar 2016 11:44:28 +0100 Subject: [PATCH 055/165] Unix: Substituted select -> poll also in congestion checker It does strange things when even one fd larger than FD_SETSIZE is passed to select(). --- sysdep/unix/io.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index b769de58..eb1c1cad 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1679,19 +1679,12 @@ sk_maybe_write(sock *s) int sk_rx_ready(sock *s) { - fd_set rd, wr; - struct timeval timo; int rv; - - FD_ZERO(&rd); - FD_ZERO(&wr); - FD_SET(s->fd, &rd); - - timo.tv_sec = 0; - timo.tv_usec = 0; + struct pollfd pfd = { .fd = s->fd }; + pfd.events |= POLLIN; redo: - rv = select(s->fd+1, &rd, &wr, NULL, &timo); + rv = poll(&pfd, 1, 0); if ((rv < 0) && (errno == EINTR || errno == EAGAIN)) goto redo; From a459f4df16eca156cb7c6def4a758fd89c9fa504 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 9 Mar 2016 17:37:44 +0100 Subject: [PATCH 056/165] OSPF: Fix reading from freed memory Thanks to Pavel Tvrdik for noticing it. --- proto/ospf/neighbor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index c5d44dec..0223ccdf 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -108,6 +108,7 @@ ospf_neigh_down(struct ospf_neighbor *n) { struct ospf_iface *ifa = n->ifa; struct ospf_proto *p = ifa->oa->po; + u32 rid = n->rid; if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) { @@ -121,7 +122,7 @@ ospf_neigh_down(struct ospf_neighbor *n) rem_node(NODE n); rfree(n->pool); - OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", n->rid, ifa->ifname); + OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", rid, ifa->ifname); } /** From 0a505706bc909b2625d308ffb2eaed3dc8398538 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 9 Mar 2016 17:51:50 +0100 Subject: [PATCH 057/165] Minor changes in documentation --- doc/bird.sgml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 73061202..5e5aeee4 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -318,8 +318,9 @@ protocol rip {

include " This statement causes inclusion of a new file. - On every multiple access network (e.g., the Ethernet) Designed Router - and Backup Designed router are elected. These routers have some special + On every multiple access network (e.g., the Ethernet) Designated Router + and Backup Designated router are elected. These routers have some special functions in the flooding process. Higher priority increases preferences in this election. Routers with priority 0 are not eligible. Default value is 1. From 39a6b19d6d7e420805bd75783b77bf442745bccb Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 22 Mar 2016 12:51:31 +0100 Subject: [PATCH 058/165] OSPF: Fix bogus LSA ID collisions between received and originated LSAs After restart, LSAs locally originated by the previous instance are received from neighbors. They are installed to LSA db and flushed. If export of a route triggers origination of a new external LSA before flush of the received one is complete, the check in ospf_originate_lsa() causes origination to fail (because en->nf is NULL for the old LSA and non-NULL for the new LSA). The patch fixes this by updating the en->nf for LSAs being flushed (as is already done for empty ones). Generally, en->nf field deserves some better description in the code. Thanks to Jigar Mehta for analyzing the problem. --- proto/ospf/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 8119cfa6..7558d4a0 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -278,7 +278,7 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa) if (!SNODE_VALID(en)) s_add_tail(&p->lsal, SNODE en); - if (en->lsa_body == NULL) + if (!en->nf || !en->lsa_body) en->nf = lsa->nf; if (en->nf != lsa->nf) From 665b8e5283df4f64eb44d8fb434489be1474b5d4 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 22 Mar 2016 13:35:40 +0100 Subject: [PATCH 059/165] Birdlib: Do cleanups after remove/free To avoid byzantine behavior in case of some errors, linked lists are cleared after rem_node() and resource headers are cleared after rfree(). --- lib/lists.c | 19 +------------------ lib/lists.h | 1 - lib/resource.c | 12 +++--------- proto/bfd/bfd.c | 2 +- 4 files changed, 5 insertions(+), 29 deletions(-) diff --git a/lib/lists.c b/lib/lists.c index d323a4b6..20a9a072 100644 --- a/lib/lists.c +++ b/lib/lists.c @@ -88,7 +88,7 @@ insert_node(node *n, node *after) * rem_node - remove a node from a list * @n: node to be removed * - * Removes a node @n from the list it's linked in. + * Removes a node @n from the list it's linked in. Afterwards, node @n is cleared. */ LIST_INLINE void rem_node(node *n) @@ -96,23 +96,6 @@ rem_node(node *n) node *z = n->prev; node *x = n->next; - z->next = x; - x->prev = z; -} - -/** - * rem2_node - remove a node from a list, with cleanup - * @n: node to be removed - * - * Removes a node @n from the list it's linked in and resets its pointers to NULL. - * Useful if you want to distinguish between linked and unlinked nodes. - */ -LIST_INLINE void -rem2_node(node *n) -{ - node *z = n->prev; - node *x = n->next; - z->next = x; x->prev = z; n->next = NULL; diff --git a/lib/lists.h b/lib/lists.h index 80a4dc93..4204cbc5 100644 --- a/lib/lists.h +++ b/lib/lists.h @@ -61,7 +61,6 @@ typedef struct list { /* In fact two overlayed nodes */ void add_tail(list *, node *); void add_head(list *, node *); void rem_node(node *); -void rem2_node(node *); void add_tail_list(list *, list *); void init_list(list *); void insert_node(node *, node *); diff --git a/lib/resource.c b/lib/resource.c index 64f9a39c..68718dfb 100644 --- a/lib/resource.c +++ b/lib/resource.c @@ -163,6 +163,7 @@ rfree(void *res) if (r->n.next) rem_node(&r->n); r->class->free(r); + r->class = NULL; xfree(r); } @@ -383,16 +384,9 @@ mb_allocz(pool *p, unsigned size) void * mb_realloc(void *m, unsigned size) { - struct mblock *ob = NULL; - - if (m) - { - ob = SKIP_BACK(struct mblock, data, m); - if (ob->r.n.next) - rem_node(&ob->r.n); - } + struct mblock *b = SKIP_BACK(struct mblock, data, m); - struct mblock *b = xrealloc(ob, sizeof(struct mblock) + size); + b = xrealloc(b, sizeof(struct mblock) + size); replace_node(&b->r.n, &b->r.n); b->size = size; return b->data; diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 7a085791..5de2f556 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -872,7 +872,7 @@ bfd_notify_hook(sock *sk, int len) WALK_LIST_FIRST(s, tmp_list) { bfd_lock_sessions(p); - rem2_node(&s->n); + rem_node(&s->n); state = s->loc_state; diag = s->loc_diag; bfd_unlock_sessions(p); From 54bb032d21d25a2221877e15325e79add10278ec Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Wed, 23 Mar 2016 01:45:37 +0100 Subject: [PATCH 060/165] Birdlib: Modify lists to avoid problems with pointer aliasing rules The old linked list implementation used some wild typecasts and required GCC option -fno-strict-aliasing to work properly. This patch fixes that. However, we still keep the option due to other potential problems. (Commited by Ondrej Santiago Zajicek) --- lib/lists.c | 10 +++++----- lib/lists.h | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/lists.c b/lib/lists.c index 20a9a072..12ef3cc6 100644 --- a/lib/lists.c +++ b/lib/lists.c @@ -41,7 +41,7 @@ add_tail(list *l, node *n) { node *z = l->tail; - n->next = (node *) &l->null; + n->next = &l->tail_node; n->prev = z; z->next = n; l->tail = n; @@ -60,7 +60,7 @@ add_head(list *l, node *n) node *z = l->head; n->next = z; - n->prev = (node *) &l->head; + n->prev = &l->head_node; z->prev = n; l->head = n; } @@ -133,9 +133,9 @@ replace_node(node *old, node *new) LIST_INLINE void init_list(list *l) { - l->head = (node *) &l->null; + l->head = &l->tail_node; l->null = NULL; - l->tail = (node *) &l->head; + l->tail = &l->head_node; } /** @@ -155,6 +155,6 @@ add_tail_list(list *to, list *l) p->next = q; q->prev = p; q = l->tail; - q->next = (node *) &to->null; + q->next = &to->tail_node; to->tail = q; } diff --git a/lib/lists.h b/lib/lists.h index 4204cbc5..51856b05 100644 --- a/lib/lists.h +++ b/lib/lists.h @@ -26,10 +26,23 @@ typedef struct node { struct node *next, *prev; } node; -typedef struct list { /* In fact two overlayed nodes */ - struct node *head, *null, *tail; +typedef union list { /* In fact two overlayed nodes */ + struct { /* Head node */ + struct node head_node; + void *head_padding; + }; + struct { /* Tail node */ + void *tail_padding; + struct node tail_node; + }; + struct { /* Split to separate pointers */ + struct node *head; + struct node *null; + struct node *tail; + }; } list; + #define NODE (node *) #define HEAD(list) ((void *)((list).head)) #define TAIL(list) ((void *)((list).tail)) From ea0a8be2ff5afb8385a69cc0df70984e0fd3a570 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Wed, 30 Mar 2016 16:21:32 +0200 Subject: [PATCH 061/165] IO/Poll: fix mistaken variable merge The events variable is used in the short loop decision. The reasons are not much clear, keeping this to keep the former behaviour. --- sysdep/unix/io.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index eb1c1cad..112ebe91 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -2045,7 +2045,7 @@ io_loop(void) { int poll_tout; time_t tout; - int nfds, events; + int nfds, events, pout; sock *s; node *n; int fdmax = 256; @@ -2125,16 +2125,16 @@ io_loop(void) /* And finally enter poll() to find active sockets */ watchdog_stop(); - events = poll(pfd, nfds, poll_tout); + pout = poll(pfd, nfds, poll_tout); watchdog_start(); - if (events < 0) + if (pout < 0) { if (errno == EINTR || errno == EAGAIN) continue; die("poll: %m"); } - if (events) + if (pout) { /* guaranteed to be non-empty */ current_sock = SKIP_BACK(sock, n, HEAD(sock_list)); From e86cfd41d975122cc944db68383aef4028da9575 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 23 Mar 2016 18:25:15 +0100 Subject: [PATCH 062/165] KRT: Fix route learn scan when route changed When a kernel route changed, function krt_learn_scan() noticed that and replaced the route in internal kernel FIB, but after that, function krt_learn_prune() failed to propagate the new route to the nest, because it confused the new route with the (removed) old best route and decided that the best route did not changed. Wow, the original code (and the bug) is almost 17 years old. --- nest/route.h | 2 +- sysdep/bsd/krt-sock.c | 5 ++--- sysdep/linux/netlink.c | 3 ++- sysdep/unix/krt.c | 46 ++++++++++++++++++++++++++---------------- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/nest/route.h b/nest/route.h index c435b9e0..9368808f 100644 --- a/nest/route.h +++ b/nest/route.h @@ -223,8 +223,8 @@ typedef struct rte { struct { /* Routes generated by krt sync (both temporary and inherited ones) */ s8 src; /* Alleged route source (see krt.h) */ u8 proto; /* Kernel source protocol ID */ - u8 type; /* Kernel route type */ u8 seen; /* Seen during last scan */ + u8 best; /* Best route in network, propagated to core */ u32 metric; /* Kernel metric */ } krt; } u; diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 29203d1b..6ff3b2b7 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -493,9 +493,8 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) e->net = net; e->u.krt.src = src; e->u.krt.proto = src2; - - /* These are probably too Linux-specific */ - e->u.krt.type = 0; + e->u.krt.seen = 0; + e->u.krt.best = 0; e->u.krt.metric = 0; if (scan) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 640d1877..1ffdff07 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1102,7 +1102,8 @@ nl_parse_route(struct nlmsghdr *h, int scan) e->net = net; e->u.krt.src = src; e->u.krt.proto = i->rtm_protocol; - e->u.krt.type = i->rtm_type; + e->u.krt.seen = 0; + e->u.krt.best = 0; e->u.krt.metric = 0; if (a[RTA_PRIORITY]) diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 5e78586b..f5dee877 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -417,46 +417,58 @@ krt_learn_prune(struct krt_proto *p) net *n = (net *) f; rte *e, **ee, *best, **pbest, *old_best; - old_best = n->routes; + /* + * Note that old_best may be NULL even if there was an old best route in + * the previous step, because it might be replaced in krt_learn_scan(). + * But in that case there is a new valid best route. + */ + + old_best = NULL; best = NULL; pbest = NULL; ee = &n->routes; while (e = *ee) { + if (e->u.krt.best) + old_best = e; + if (!e->u.krt.seen) { *ee = e->next; rte_free(e); continue; } + if (!best || best->u.krt.metric > e->u.krt.metric) { best = e; pbest = ee; } + e->u.krt.seen = 0; + e->u.krt.best = 0; ee = &e->next; } if (!n->routes) { DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen); if (old_best) - { - krt_learn_announce_delete(p, n); - n->n.flags &= ~KRF_INSTALLED; - } + krt_learn_announce_delete(p, n); + FIB_ITERATE_PUT(&fit, f); fib_delete(fib, f); goto again; } + + best->u.krt.best = 1; *pbest = best->next; best->next = n->routes; n->routes = best; - if (best != old_best || !(n->n.flags & KRF_INSTALLED) || p->reload) + + if ((best != old_best) || p->reload) { DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); krt_learn_announce_update(p, best); - n->n.flags |= KRF_INSTALLED; } else DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); @@ -515,31 +527,31 @@ krt_learn_async(struct krt_proto *p, rte *e, int new) best = n->routes; bestp = &n->routes; for(gg=&n->routes; g=*gg; gg=&g->next) + { if (best->u.krt.metric > g->u.krt.metric) { best = g; bestp = gg; } + + g->u.krt.best = 0; + } + if (best) { + best->u.krt.best = 1; *bestp = best->next; best->next = n->routes; n->routes = best; } + if (best != old_best) { DBG("krt_learn_async: distributing change\n"); if (best) - { - krt_learn_announce_update(p, best); - n->n.flags |= KRF_INSTALLED; - } + krt_learn_announce_update(p, best); else - { - n->routes = NULL; - krt_learn_announce_delete(p, n); - n->n.flags &= ~KRF_INSTALLED; - } + krt_learn_announce_delete(p, n); } } @@ -564,7 +576,7 @@ krt_dump(struct proto *P) static void krt_dump_attrs(rte *e) { - debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type); + debug(" [m=%d,p=%d]", e->u.krt.metric, e->u.krt.proto); } #endif From 9e7b3ebdf9556d7464911dd39e862b1c003319b3 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 6 Apr 2016 11:49:34 +0200 Subject: [PATCH 063/165] IO: Replace RX priority heuristic with explicit mark In BIRD, RX has lower priority than TX with the exception of RX from control socket. The patch replaces heuristic based on socket type with explicit mark and uses it for both control socket and BGP session waiting to be established. This should avoid an issue when during heavy load, outgoing connection could connect (TX event), send open, but then failed to receive OPEN / establish in time, not sending notifications between and therefore got hold timer expired error from the neighbor immediately after it finally established the connection. --- lib/socket.h | 1 + proto/bgp/bgp.c | 3 +++ sysdep/unix/io.c | 4 ++-- sysdep/unix/main.c | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/socket.h b/lib/socket.h index fbea92aa..0327e9e5 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -28,6 +28,7 @@ typedef struct birdsock { struct iface *iface; /* Interface; specify this for broad/multicast sockets */ byte *rbuf, *rpos; /* NULL=allocate automatically */ + uint fast_rx; /* RX has higher priority in event loop */ uint rbsize; int (*rx_hook)(struct birdsock *, int size); /* NULL=receiving turned off, returns 1 to clear rx buffer */ diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 11f33b14..7328cb79 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -374,6 +374,8 @@ bgp_conn_enter_established_state(struct bgp_conn *conn) if (ipa_zero(p->source_addr)) p->source_addr = conn->sk->saddr; + conn->sk->fast_rx = 0; + p->conn = conn; p->last_error_class = 0; p->last_error_code = 0; @@ -696,6 +698,7 @@ bgp_setup_sk(struct bgp_conn *conn, sock *s) { s->data = conn; s->err_hook = bgp_sock_err; + s->fast_rx = 1; conn->sk = s; } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 112ebe91..078fe73c 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -2152,7 +2152,7 @@ io_loop(void) int steps; steps = MAX_STEPS; - if ((s->type >= SK_MAGIC) && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) + if (s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) do { steps--; @@ -2197,7 +2197,7 @@ io_loop(void) goto next2; } - if ((s->type < SK_MAGIC) && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) + if (!s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) { count++; io_log_event(s->rx_hook, s->data); diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 24d34f60..5d5586a0 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -450,6 +450,7 @@ cli_connect(sock *s, int size UNUSED) s->err_hook = cli_err; s->data = c = cli_new(s); s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */ + s->fast_rx = 1; c->rx_pos = c->rx_buf; c->rx_aux = NULL; rmove(s, c->pool); @@ -466,6 +467,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid) s->type = SK_UNIX_PASSIVE; s->rx_hook = cli_connect; s->rbsize = 1024; + s->fast_rx = 1; /* Return value intentionally ignored */ unlink(path_control_socket); From bd22d7f41d37dec8f7b8b845f6a18c775e66ddfc Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 6 Apr 2016 11:57:28 +0200 Subject: [PATCH 064/165] IO: Avoid multiple event cycles in one loop cycle. Event cycle may took too much time and trigger next timer events, so avoid cycling between timer and event cycles inside the loop cycle. --- proto/bgp/bgp.c | 4 ++++ sysdep/unix/io.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 7328cb79..94c8e5c2 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -668,6 +668,10 @@ bgp_keepalive_timeout(timer *t) DBG("BGP: Keepalive timer\n"); bgp_schedule_packet(conn, PKT_KEEPALIVE); + + /* Kick TX a bit faster */ + if (ev_active(conn->tx_ev)) + ev_run(conn->tx_ev); } static void diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 078fe73c..5955dbfe 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -2055,12 +2055,13 @@ io_loop(void) for(;;) { events = ev_run_list(&global_event_list); + timers: update_times(); tout = tm_first_shot(); if (tout <= now) { tm_shot(); - continue; + goto timers; } poll_tout = (events ? 0 : MIN(tout - now, 3)) * 1000; /* Time in milliseconds */ From 06edbb67ed807811654e7fd8f0f9b83766430216 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 7 Apr 2016 01:10:24 +0200 Subject: [PATCH 065/165] Nest: Reset export route counter during graceful restart Counter exp_routes is increased during initial route feed after GR recovery, so it has to start with zero, otherwise BIRD will end with double value in exp_routes. --- nest/proto.c | 1 + 1 file changed, 1 insertion(+) diff --git a/nest/proto.c b/nest/proto.c index d04da333..436377f1 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -1260,6 +1260,7 @@ proto_want_export_down(struct proto *p) rt_feed_baby_abort(p); p->export_state = ES_DOWN; + p->stats.exp_routes = 0; proto_unlink_ahooks(p); } From f2ae2badff37c008ba8217a12f8ee6dc6a3c5a39 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 7 Apr 2016 12:20:45 +0200 Subject: [PATCH 066/165] Main: Add local option Add option that changes default paths for config file and control socket to the current working directory. --- client/client.c | 10 ++++++++-- doc/bird.sgml | 5 +++++ lib/string.h | 6 ++++++ sysdep/unix/main.c | 16 +++++++++++++--- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/client/client.c b/client/client.c index b938f344..4075b9e6 100644 --- a/client/client.c +++ b/client/client.c @@ -37,7 +37,7 @@ #define SERVER_READ_BUF_LEN 4096 -static char *opt_list = "s:vr"; +static char *opt_list = "s:vrl"; static int verbose, restricted, once; static char *init_cmd; @@ -59,13 +59,14 @@ int term_lns, term_cls; static void usage(char *name) { - fprintf(stderr, "Usage: %s [-s ] [-v] [-r]\n", name); + fprintf(stderr, "Usage: %s [-s ] [-v] [-r] [-l]\n", name); exit(1); } static void parse_args(int argc, char **argv) { + int server_changed = 0; int c; while ((c = getopt(argc, argv, opt_list)) >= 0) @@ -73,6 +74,7 @@ parse_args(int argc, char **argv) { case 's': server_path = optarg; + server_changed = 1; break; case 'v': verbose++; @@ -80,6 +82,10 @@ parse_args(int argc, char **argv) case 'r': restricted = 1; break; + case 'l': + if (!server_changed) + server_path = xbasename(server_path); + break; default: usage(argv[0]); } diff --git a/doc/bird.sgml b/doc/bird.sgml index 5e5aeee4..5e943d7c 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -171,6 +171,11 @@ BIRD executable by configuring out routing protocols you don't use, and -f run bird in foreground. + -l + look for a configuration file and a communication socket in the current + working directory instead of in default system paths. However, paths + specified by options -R apply graceful restart recovery after start. diff --git a/lib/string.h b/lib/string.h index 218f7b1c..0f249d37 100644 --- a/lib/string.h +++ b/lib/string.h @@ -24,4 +24,10 @@ void buffer_puts(buffer *buf, const char *str); int patmatch(const byte *pat, const byte *str); +static inline char *xbasename(const char *str) +{ + char *s = strrchr(str, '/'); + return s ? s+1 : (char *) str; +} + #endif diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 5d5586a0..11a4acc6 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -617,7 +617,7 @@ signal_init(void) * Parsing of command-line arguments */ -static char *opt_list = "c:dD:ps:P:u:g:fR"; +static char *opt_list = "c:dD:ps:P:u:g:flR"; static int parse_and_exit; char *bird_name; static char *use_user; @@ -627,7 +627,7 @@ static int run_in_foreground = 0; static void usage(void) { - fprintf(stderr, "Usage: %s [-c ] [-d] [-D ] [-p] [-s ] [-P ] [-u ] [-g ] [-f] [-R]\n", bird_name); + fprintf(stderr, "Usage: %s [-c ] [-d] [-D ] [-p] [-s ] [-P ] [-u ] [-g ] [-f] [-l] [-R]\n", bird_name); exit(1); } @@ -677,7 +677,7 @@ get_gid(const char *s) if (!s) return 0; - + errno = 0; rv = strtol(s, &endptr, 10); @@ -694,6 +694,8 @@ get_gid(const char *s) static void parse_args(int argc, char **argv) { + int config_changed = 0; + int socket_changed = 0; int c; bird_name = get_bird_name(argv[0], "bird"); @@ -712,6 +714,7 @@ parse_args(int argc, char **argv) { case 'c': config_name = optarg; + config_changed = 1; break; case 'd': debug_flag |= 1; @@ -725,6 +728,7 @@ parse_args(int argc, char **argv) break; case 's': path_control_socket = optarg; + socket_changed = 1; break; case 'P': pid_file = optarg; @@ -738,6 +742,12 @@ parse_args(int argc, char **argv) case 'f': run_in_foreground = 1; break; + case 'l': + if (!config_changed) + config_name = xbasename(config_name); + if (!socket_changed) + path_control_socket = xbasename(path_control_socket); + break; case 'R': graceful_restart_recovery(); break; From e90dd656cc9126e1fbcc45fb77a10bf1baa2a1b5 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Fri, 8 Apr 2016 15:10:57 +0200 Subject: [PATCH 067/165] Direct: Implement check link for direct protocol When enabled, direct protocol generates routes only if the underlying link state is up. --- doc/bird.sgml | 8 +++++++- nest/config.Y | 1 + nest/rt-dev.c | 28 +++++++++++++++++++++++++++- nest/rt-dev.h | 1 + 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 5e943d7c..829b148a 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2155,7 +2155,7 @@ conditions, because a lower priority IGP route for the same network is not exported to the kernel routing table. This is an issue on BSD systems only, as on Linux systems BIRD cannot change non-BIRD route in the kernel routing table. -

The only configurable thing about direct is what interfaces it watches: +

There are just few configuration options for the Direct protocol:

interface @@ -2166,6 +2166,12 @@ on Linux systems BIRD cannot change non-BIRD route in the kernel routing table. interfaces), just use this clause. See common option for detailed description. The Direct protocol uses extended interface clauses. + + check link + If enabled, a hardware link state (reported by OS) is taken into + consideration. Routes for directly connected networks are generated only + if link up is reported and they are withdrawn when link disappears + (e.g., an ethernet cable is unplugged). Default value is no.

Direct device routes don't contain any specific attributes. diff --git a/nest/config.Y b/nest/config.Y index 87827c10..4566971f 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -335,6 +335,7 @@ dev_proto: dev_proto_start proto_name '{' | dev_proto proto_item ';' | dev_proto dev_iface_patt ';' + | dev_proto CHECK LINK bool ';' { DIRECT_CFG->check_link = $4; } ; dev_iface_init: diff --git a/nest/rt-dev.c b/nest/rt-dev.c index f6bc1432..ed6c06af 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -64,6 +64,9 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip); + if (P->check_link && !(ad->iface->flags & IF_LINK_UP)) + return; + /* Use iface ID as local source ID */ struct rte_src *src = rt_get_source(p, ad->iface->index); @@ -85,11 +88,31 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) } } +static void +dev_if_notify(struct proto *p, uint c, struct iface *iface) +{ + struct rt_dev_config *cf = (void *) p->cf; + + if (c & (IF_CHANGE_UP | IF_CHANGE_DOWN)) + return; + + if ((c & IF_CHANGE_LINK) && cf->check_link) + { + uint ac = (iface->flags & IF_LINK_UP) ? IF_CHANGE_UP : IF_CHANGE_DOWN; + + struct ifa *a; + WALK_LIST(a, iface->addrs) + dev_ifa_notify(p, ac, a); + } +} + + static struct proto * dev_init(struct proto_config *c) { struct proto *p = proto_new(c, sizeof(struct proto)); + p->if_notify = dev_if_notify; p->ifa_notify = dev_ifa_notify; return p; } @@ -100,7 +123,8 @@ dev_reconfigure(struct proto *p, struct proto_config *new) struct rt_dev_config *o = (struct rt_dev_config *) p->cf; struct rt_dev_config *n = (struct rt_dev_config *) new; - return iface_patts_equal(&o->iface_list, &n->iface_list, NULL); + return iface_patts_equal(&o->iface_list, &n->iface_list, NULL) && + (o->check_link == n->check_link); } static void @@ -115,6 +139,8 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src) * old nodes cannot be modified (although they contain internal lists). */ cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct iface_patt)); + + d->check_link = s->check_link; } struct protocol proto_device = { diff --git a/nest/rt-dev.h b/nest/rt-dev.h index c36d0742..191b9a02 100644 --- a/nest/rt-dev.h +++ b/nest/rt-dev.h @@ -12,6 +12,7 @@ struct rt_dev_config { struct proto_config c; list iface_list; /* list of struct iface_patt */ + int check_link; }; #endif From 43fc6bb0fb720762f12124076e2241855741ceb5 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 11 Apr 2016 00:41:10 +0200 Subject: [PATCH 068/165] Documentation update --- doc/bird.sgml | 98 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 829b148a..653e0bb5 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -173,7 +173,7 @@ BIRD executable by configuring out routing protocols you don't use, and -l look for a configuration file and a communication socket in the current - working directory instead of in default system paths. However, paths + working directory instead of in default system locations. However, paths specified by options -R @@ -717,6 +717,10 @@ This argument can be omitted if there exists only a single instance. Show router status, that is BIRD version, uptime and time from last reconfiguration. + show interfaces [summary] + Show the list of interfaces. For each interface, print its type, state, + MTU and addresses assigned. + show protocols [all] Show list of protocol instances along with tables they are connected to and protocol status, possibly giving verbose information, if show rip interfaces [ + Show detailed information about RIP interfaces. + + show rip neighbors [ + Show a list of RIP neighbors and associated state. + show static [ Show detailed information about static routes. show bfd sessions [ Show information about BFD sessions. - show interfaces [summary] - Show the list of interfaces. For each interface, print its type, state, - MTU and addresses assigned. - show symbols [table|filter|function|protocol|template|roa| Show the list of symbols defined in the configuration (names of protocols, routing tables etc.). @@ -3376,6 +3382,11 @@ protocol rip [<name>] { RIP, the option is not supported for RIPng, as no further versions are defined. + version only + Regardless of RIP version configured for the interface, BIRD accepts + incoming packets of any RIP version. This option restrict accepted + packets to the configured version. Default: no. + split horizon Split horizon is a scheme for preventing routing loops. When split horizon is active, routes are not regularly propagated back to the @@ -3520,7 +3531,7 @@ default route to prevent routing loops). packets to a neighboring router, multipath routes specifying several (possibly weighted) neighboring routers, device routes specifying forwarding to hosts on a directly connected network, recursive routes computing their nexthops by doing -route table lookups for a given IP and special routes (sink, blackhole etc.) +route table lookups for a given IP, and special routes (sink, blackhole etc.) which specify a special action to be done instead of forwarding the packet.

When the particular destination is not available (the interface is down or @@ -3528,8 +3539,26 @@ the next hop of the route is not a neighbor at the moment), Static just uninstalls the route from the table it is connected to and adds it again as soon as the destination becomes adjacent again. -

The Static protocol does not have many configuration options. The definition -of the protocol contains mainly a list of static routes: +

There are three classes of definitions in Static protocol configuration -- +global options, static route definitions, and per-route options. Usually, the +definition of the protocol contains mainly a list of static routes. + +

Global options: + + + check link + If set, hardware link states of network interfaces are taken into + consideration. When link disappears (e.g. ethernet cable is unplugged), + static routes directing to that interface are removed. It is possible + that some hardware drivers or platforms do not implement this feature. + Default: off. + + igp table + Specifies a table that is used for route table lookups of recursive + routes. Default: the same table as the protocol is connected to. + + +

Route definitions (each may also contain a block of per-route options): route @@ -3537,7 +3566,7 @@ of the protocol contains mainly a list of static routes: interface can be specified as a part of the address (e.g., route + route Static multipath route. Contains several nexthops (gateways), possibly with their weights. @@ -3553,17 +3582,33 @@ of the protocol contains mainly a list of static routes: Special routes specifying to silently drop the packet, return it as unreachable or return it as administratively prohibited. First two targets are also known as - check link - If set, hardware link states of network interfaces are taken into - consideration. When link disappears (e.g. ethernet cable is unplugged), - static routes directing to that interface are removed. It is possible - that some hardware drivers or platforms do not implement this feature. - Default: off. +

Per-route options: - igp table - Specifies a table that is used for route table lookups of recursive - routes. Default: the same table as the protocol is connected to. + + bfd + The Static protocol could use BFD protocol for next hop liveness + detection. If enabled, a BFD session to the route next hop is created + and the static route is BFD-controlled -- the static route is announced + only if the next hop liveness is confirmed by BFD. If the BFD session + fails, the static route is removed. Note that this is a bit different + compared to other protocols, which may use BFD as an advisory mechanism + for fast failure detection but ignores it if a BFD session is not even + established. + + This option can be used for static routes with a direct next hop, or + also for for individual next hops in a static multipath route (see + above). Note that BFD protocol also has to be configured, see + section for details. Default value is no. + + + This is a special option that allows filter expressions to be configured + on per-route basis. Can be used multiple times. These expressions are + evaluated when the route is originated, similarly to the import filter + of the static protocol. This is especially useful for configuring route + attributes, e.g.,

Static routes have no specific attributes. @@ -3572,14 +3617,23 @@ of the protocol contains mainly a list of static routes:

protocol static { - table testable; # Connect to a non-default routing table + table testable; # Connect to a non-default routing table + check link; # Advertise routes only if link is up route 0.0.0.0/0 via 198.51.100.130; # Default route - route 10.0.0.0/8 multipath # Multipath route + route 10.0.0.0/8 multipath # Multipath route via 198.51.100.10 weight 2 - via 198.51.100.20 + via 198.51.100.20 bfd # BFD-controlled next hop via 192.0.2.1; route 203.0.113.0/24 unreachable; # Sink route - route 10.2.0.0/24 via "arc0"; # Secondary network + route 10.2.0.0/24 via "arc0"; # Secondary network + route 192.168.10.0/24 via 198.51.100.100 { + ospf_metric1 = 20; # Set extended attribute + } + route 192.168.10.0/24 via 198.51.100.100 { + ospf_metric2 = 100; # Set extended attribute + ospf_tag = 2; # Set extended attribute + bfd; # BFD-controlled route + } } From a7baa09862e6b4856cd66197c6bd74c7df336b8f Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 13 Apr 2016 14:30:28 +0200 Subject: [PATCH 069/165] BSD: Add the IPsec SA/SP database entries control Add code for manipulation with TCP-MD5 keys in the IPsec SA/SP database at FreeBSD systems. Now, BGP MD5 authentication (RFC 2385) keys are handled automatically on both Linux and FreeBSD. Based on patches from Pavel Tvrdik. --- doc/bird.sgml | 17 ++++- lib/socket.h | 2 +- proto/bgp/bgp.c | 6 +- proto/bgp/bgp.h | 1 + proto/bgp/config.Y | 4 +- sysdep/bsd/Modules | 1 + sysdep/bsd/setkey.h | 170 +++++++++++++++++++++++++++++++++++++++++++ sysdep/bsd/sysio.h | 28 +++---- sysdep/linux/sysio.h | 6 +- sysdep/unix/io.c | 25 +++++-- 10 files changed, 226 insertions(+), 34 deletions(-) create mode 100644 sysdep/bsd/setkey.h diff --git a/doc/bird.sgml b/doc/bird.sgml index 653e0bb5..1a5fbaff 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1764,9 +1764,20 @@ using the following configuration parameters: only. Default: disabled. password - Use this password for MD5 authentication of BGP sessions. Default: no - authentication. Password has to be set by external utility - (e.g. setkey(8)) on BSD systems. + Use this password for MD5 authentication of BGP sessions (RFC 2385). + When used on BSD systems, see also setkey + On BSD systems, keys for TCP MD5 authentication are stored in the global + SA/SP database, which can be accessed by external utilities (e.g. + setkey(8)). BIRD configures security associations in the SA/SP database + automatically based on passive Standard BGP behavior is both initiating outgoing connections and diff --git a/lib/socket.h b/lib/socket.h index 0327e9e5..6babfa7b 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -87,7 +87,7 @@ int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface int sk_setup_broadcast(sock *s); int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */ int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */ -int sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd); +int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey); int sk_set_ipv6_checksum(sock *s, int offset); int sk_set_icmp6_filter(sock *s, int p1, int p2); void sk_log_error(sock *s, const char *p); diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 94c8e5c2..2014525e 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -121,7 +121,8 @@ bgp_open(struct bgp_proto *p) bgp_counter++; if (p->cf->password) - if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password) < 0) + if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip, + p->cf->iface, p->cf->password, p->cf->setkey) < 0) { sk_log_error(bgp_listen_sk, p->p.name); bgp_close(p, 0); @@ -191,7 +192,8 @@ bgp_close(struct bgp_proto *p, int apply_md5) bgp_counter--; if (p->cf->password && apply_md5) - if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL) < 0) + if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip, + p->cf->iface, NULL, p->cf->setkey) < 0) sk_log_error(bgp_listen_sk, p->p.name); if (!bgp_counter) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 274794f1..b1cca2d9 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -51,6 +51,7 @@ struct bgp_config { int add_path; /* Use ADD-PATH extension [draft] */ int allow_local_as; /* Allow that number of local ASNs in incoming AS_PATHs */ int gr_mode; /* Graceful restart mode (BGP_GR_*) */ + int setkey; /* Set MD5 password to system SA/SP database */ unsigned gr_time; /* Graceful restart timeout */ unsigned connect_delay_time; /* Minimum delay between connect attempts */ unsigned connect_retry_time; /* Timeout for connect attempts */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 85b93a6b..f3ba0e16 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) + CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY) CF_GRAMMAR @@ -54,6 +54,7 @@ bgp_proto_start: proto_start BGP { BGP_CFG->default_local_pref = 100; BGP_CFG->gr_mode = BGP_GR_AWARE; BGP_CFG->gr_time = 120; + BGP_CFG->setkey = 1; } ; @@ -112,6 +113,7 @@ bgp_proto: | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } | bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; } + | bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; } | bgp_proto ROUTE LIMIT expr ';' { this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit)); this_proto->in_limit->limit = $4; diff --git a/sysdep/bsd/Modules b/sysdep/bsd/Modules index 96455db7..39db88e9 100644 --- a/sysdep/bsd/Modules +++ b/sysdep/bsd/Modules @@ -2,3 +2,4 @@ krt-sock.c krt-sock.Y krt-sys.h sysio.h +setkey.h diff --git a/sysdep/bsd/setkey.h b/sysdep/bsd/setkey.h new file mode 100644 index 00000000..b417faca --- /dev/null +++ b/sysdep/bsd/setkey.h @@ -0,0 +1,170 @@ +/* + * BIRD -- Manipulation the IPsec SA/SP database using setkey(8) utility + * + * (c) 2016 CZ.NIC z.s.p.o. + */ + +#include +#include +#include +#include +#include + +#include "nest/bird.h" +#include "lib/unix.h" + + +/* + * Open a socket for manage the IPsec SA/SP database entries + */ +static int +setkey_open_socket(void) +{ + int s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (s < 0) + { + log(L_ERR "SETKEY: socket: %m"); + return -1; + } + + return s; +} + +static int +setkey_send(struct sadb_msg *msg, uint len) +{ + int s = setkey_open_socket(); + if (s < 0) + return -1; + + if (msg->sadb_msg_type == SADB_ADD) + { + /* Delete possible current key in the IPsec SA/SP database */ + msg->sadb_msg_type = SADB_DELETE; + send(s, msg, len, 0); + msg->sadb_msg_type = SADB_ADD; + } + + if (send(s, msg, len, 0) < 0) + { + log(L_ERR "SETKEY: send: %m"); + close(s); + return -1; + } + + close(s); + return 0; +} + +/* + * Perform setkey(8)-like operation for set the password for TCP MD5 Signature. + * Could be called with SABD_ADD or SADB_DELETE argument. Note that SADB_ADD + * argument is internally processed as a pair of SADB_ADD and SADB_DELETE + * operations to implement replace. + */ +static int +setkey_md5(sockaddr *src, sockaddr *dst, char *passwd, uint type) +{ + uint passwd_len = passwd ? strlen(passwd) : 0; + + uint total = + sizeof(struct sadb_msg) + + sizeof(struct sadb_key) + PFKEY_ALIGN8(passwd_len) + + sizeof(struct sadb_sa) + + sizeof(struct sadb_x_sa2) + + sizeof(struct sadb_address) + PFKEY_ALIGN8(src->sa.sa_len) + + sizeof(struct sadb_address) + PFKEY_ALIGN8(dst->sa.sa_len); + + char *buf = alloca(total); + char *pos = buf; + uint len; + + memset(buf, 0, total); + + struct sadb_msg *msg = (void *) pos; + len = sizeof(struct sadb_msg); + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = type; + msg->sadb_msg_satype = SADB_X_SATYPE_TCPSIGNATURE; + msg->sadb_msg_len = 0; /* Fix it later */ + msg->sadb_msg_pid = getpid(); + pos += len; + + /* Set authentication algorithm and password */ + struct sadb_key *key = (void *) pos; + len = sizeof(struct sadb_key) + PFKEY_ALIGN8(passwd_len); + key->sadb_key_len = PFKEY_UNIT64(len); + key->sadb_key_exttype = SADB_EXT_KEY_AUTH; + key->sadb_key_bits = passwd_len * 8; + memcpy(pos + sizeof(struct sadb_key), passwd, passwd_len); + pos += len; + + struct sadb_sa *sa = (void *) pos; + len = sizeof(struct sadb_sa); + sa->sadb_sa_len = PFKEY_UNIT64(len); + sa->sadb_sa_exttype = SADB_EXT_SA; + sa->sadb_sa_spi = htonl((u32) TCP_SIG_SPI); + sa->sadb_sa_auth = SADB_X_AALG_TCP_MD5; + sa->sadb_sa_encrypt = SADB_EALG_NONE; + sa->sadb_sa_flags = SADB_X_EXT_CYCSEQ; + pos += len; + + struct sadb_x_sa2 *sa2 = (void *) pos; + len = sizeof(struct sadb_x_sa2); + sa2->sadb_x_sa2_len = PFKEY_UNIT64(len); + sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2; + sa2->sadb_x_sa2_mode = IPSEC_MODE_ANY; + pos += len; + + /* Set source address */ + struct sadb_address *saddr = (void *) pos; + len = sizeof(struct sadb_address) + PFKEY_ALIGN8(src->sa.sa_len); + saddr->sadb_address_len = PFKEY_UNIT64(len); + saddr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; + saddr->sadb_address_proto = IPSEC_ULPROTO_ANY; + saddr->sadb_address_prefixlen = MAX_PREFIX_LENGTH; + memcpy(pos + sizeof(struct sadb_address), &src->sa, src->sa.sa_len); + pos += len; + + /* Set destination address */ + struct sadb_address *daddr = (void *) pos; + len = sizeof(struct sadb_address) + PFKEY_ALIGN8(dst->sa.sa_len); + daddr->sadb_address_len = PFKEY_UNIT64(len); + daddr->sadb_address_exttype = SADB_EXT_ADDRESS_DST; + daddr->sadb_address_proto = IPSEC_ULPROTO_ANY; + daddr->sadb_address_prefixlen = MAX_PREFIX_LENGTH; + memcpy(pos + sizeof(struct sadb_address), &dst->sa, dst->sa.sa_len); + pos += len; + + len = pos - buf; + msg->sadb_msg_len = PFKEY_UNIT64(len); + + return setkey_send(msg, len); +} + +/* + * Manipulation with the IPsec SA/SP database + */ +static int +sk_set_md5_in_sasp_db(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd) +{ + sockaddr src, dst; + sockaddr_fill(&src, s->af, local, ifa, 0); + sockaddr_fill(&dst, s->af, remote, ifa, 0); + + if (passwd && *passwd) + { + int len = strlen(passwd); + if (len > TCP_KEYLEN_MAX) + ERR_MSG("The password for TCP MD5 Signature is too long"); + + if (setkey_md5(&src, &dst, passwd, SADB_ADD) < 0) + ERR_MSG("Cannot add TCP-MD5 password into the IPsec SA/SP database"); + } + else + { + if (setkey_md5(&src, &dst, NULL, SADB_DELETE) < 0) + ERR_MSG("Cannot delete TCP-MD5 password from the IPsec SA/SP database"); + } + return 0; +} diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index c82d7a1e..6c20733f 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -189,30 +189,26 @@ sk_prepare_ip_header(sock *s, void *hdr, int dlen) #ifndef TCP_KEYLEN_MAX #define TCP_KEYLEN_MAX 80 #endif + #ifndef TCP_SIG_SPI #define TCP_SIG_SPI 0x1000 #endif -/* - * FIXME: Passwords has to be set by setkey(8) command. This is the same - * behaviour like Quagga. We need to add code for SA/SP entries - * management. - */ +#if defined(__FreeBSD__) +#define USE_MD5SIG_SETKEY +#include "lib/setkey.h" +#endif int -sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) +sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey UNUSED) { - int enable = 0; - - if (passwd && *passwd) - { - int len = strlen(passwd); - enable = TCP_SIG_SPI; - - if (len > TCP_KEYLEN_MAX) - ERR_MSG("MD5 password too long"); - } +#ifdef USE_MD5SIG_SETKEY + if (setkey) + if (sk_set_md5_in_sasp_db(s, local, remote, ifa, passwd) < 0) + return -1; +#endif + int enable = (passwd && *passwd) ? TCP_SIG_SPI : 0; if (setsockopt(s->fd, IPPROTO_TCP, TCP_MD5SIG, &enable, sizeof(enable)) < 0) { if (errno == ENOPROTOOPT) diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index c1561cbf..58644417 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -179,19 +179,19 @@ sk_prepare_cmsgs4(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) */ int -sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) +sk_set_md5_auth(sock *s, ip_addr local UNUSED, ip_addr remote, struct iface *ifa, char *passwd, int setkey UNUSED) { struct tcp_md5sig md5; memset(&md5, 0, sizeof(md5)); - sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, a, ifa, 0); + sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, remote, ifa, 0); if (passwd) { int len = strlen(passwd); if (len > TCP_MD5SIG_MAXKEYLEN) - ERR_MSG("MD5 password too long"); + ERR_MSG("The password for TCP MD5 Signature is too long"); md5.tcpm_keylen = len; memcpy(&md5.tcpm_key, passwd, len); diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 5955dbfe..d918d321 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -951,23 +951,32 @@ sk_set_min_ttl(sock *s, int ttl) /** * sk_set_md5_auth - add / remove MD5 security association for given socket * @s: socket - * @a: IP address of the other side + * @local: IP address of local side + * @remote: IP address of remote side * @ifa: Interface for link-local IP address - * @passwd: password used for MD5 authentication + * @passwd: Password used for MD5 authentication + * @setkey: Update also system SA/SP database * - * In TCP MD5 handling code in kernel, there is a set of pairs (address, - * password) used to choose password according to address of the other side. - * This function is useful for listening socket, for active sockets it is enough - * to set s->password field. + * In TCP MD5 handling code in kernel, there is a set of security associations + * used for choosing password and other authentication parameters according to + * the local and remote address. This function is useful for listening socket, + * for active sockets it may be enough to set s->password field. * * When called with passwd != NULL, the new pair is added, * When called with passwd == NULL, the existing pair is removed. * + * Note that while in Linux, the MD5 SAs are specific to socket, in BSD they are + * stored in global SA/SP database (but the behavior also must be enabled on + * per-socket basis). In case of multiple sockets to the same neighbor, the + * socket-specific state must be configured for each socket while global state + * just once per src-dst pair. The @setkey argument controls whether the global + * state (SA/SP database) is also updated. + * * Result: 0 for success, -1 for an error. */ int -sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) +sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey) { DUMMY; } #endif @@ -1437,7 +1446,7 @@ sk_open(sock *s) } if (s->password) - if (sk_set_md5_auth(s, s->daddr, s->iface, s->password) < 0) + if (sk_set_md5_auth(s, s->saddr, s->daddr, s->iface, s->password, 0) < 0) goto err; switch (s->type) From 937e75d8f1d203b637ba0ea050026f9af92485f3 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 28 Apr 2016 18:01:40 +0200 Subject: [PATCH 070/165] Add the Babel routing protocol (RFC 6126) This patch implements the IPv6 subset of the Babel routing protocol. Based on the patch from Toke Hoiland-Jorgensen, with some heavy modifications and bugfixes. Thanks to Toke Hoiland-Jorgensen for the original patch. --- configure.in | 3 + doc/bird.sgml | 96 ++ doc/reply_codes | 3 + lib/birdlib.h | 1 + lib/bitops.h | 3 +- lib/ip.h | 1 + lib/printf.c | 31 +- lib/string.h | 2 + nest/proto.c | 3 + nest/protocol.h | 2 +- nest/route.h | 11 +- proto/Doc | 1 + proto/babel/Doc | 2 + proto/babel/Makefile | 5 + proto/babel/babel.c | 2055 +++++++++++++++++++++++++++++++++++++++++ proto/babel/babel.h | 335 +++++++ proto/babel/config.Y | 129 +++ proto/babel/packets.c | 1093 ++++++++++++++++++++++ sysdep/autoconf.h.in | 1 + 19 files changed, 3767 insertions(+), 10 deletions(-) create mode 100644 proto/babel/Doc create mode 100644 proto/babel/Makefile create mode 100644 proto/babel/babel.c create mode 100644 proto/babel/babel.h create mode 100644 proto/babel/config.Y create mode 100644 proto/babel/packets.c diff --git a/configure.in b/configure.in index b9220a1d..16a0b414 100644 --- a/configure.in +++ b/configure.in @@ -206,6 +206,9 @@ fi AC_SUBST(iproutedir) all_protocols="$proto_bfd bgp ospf pipe $proto_radv rip static" +if test "$ip" = ipv6 ; then + all_protocols="$all_protocols babel" +fi all_protocols=`echo $all_protocols | sed 's/ /,/g'` if test "$with_protocols" = all ; then diff --git a/doc/bird.sgml b/doc/bird.sgml index 1a5fbaff..6c6ff3db 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1380,6 +1380,102 @@ corresponding protocol sections. Protocols +Babel + +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. + +

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 +subset of the protocol. No Babel extensions are implemented, but the BIRD +implementation can coexist with implementations using the extensions (and will +just ignore extension messages). + +

The Babel protocol implementation in BIRD is currently in alpha stage. + +Configuration + +

Babel supports no global configuration options apart from those common to all +other protocols, but supports the following per-interface configuration options: + + +protocol babel [] { + interface { + type ; + rxcost ; + hello interval ; + update interval ; + port ; + tx class|dscp ; + tx priority ; + rx buffer ; + tx length ; + check link ; + }; +} + + + + type wired|wireless + This option specifies the interface type: Wired or wireless. Wired + interfaces are considered more reliable, and so the default hello + interval is higher, and a neighbour is considered unreachable after only + a small number of "hello" packets are lost. On wireless interfaces, + hello packets are sent more often, and the ETX link quality estimation + technique is used to compute the metrics of routes discovered over this + interface. This technique will gradually degrade the metric of routes + when packets are lost rather than the more binary up/down mechanism of + wired type links. Default: rxcost + This specifies the RX cost of the interface. The route metrics will be + computed from this value with a mechanism determined by the interface + hello interval + Interval at which periodic "hello" messages are sent on this interface, + in seconds. Default: 4 seconds. + + update interval + Interval at which periodic (full) updates are sent. Default: 4 times the + hello interval. + + port + This option selects an UDP port to operate on. The default is to operate + on port 6696 as specified in the Babel RFC. + + tx class|dscp|priority + These options specify the ToS/DiffServ/Traffic class/Priority of the + outgoing Babel packets. See common + option for detailed description. + + rx buffer + This option specifies the size of buffers used for packet processing. + The buffer size should be bigger than maximal size of received packets. + The default value is the interface MTU, and the value will be clamped to a + minimum of 512 bytes + IP packet overhead. + + tx length + This option specifies the maximum length of generated Babel packets. To + avoid IP fragmentation, it should not exceed the interface MTU value. + The default value is the interface MTU value, and the value will be + clamped to a minimum of 512 bytes + IP packet overhead. + + check link + If set, the hardware link state (as reported by OS) is taken into + 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: + yes. + + +

A nonprivileged user (as an argument to An unprivileged user (as an argument to @@ -1622,7 +1622,7 @@ protocol bfd [<name>] { Default: 5. passive - Generally, both BFD session endpoinds try to establish the session by + Generally, both BFD session endpoints try to establish the session by sending control packets to the other side. This option allows to enable passive mode, which means that the router does not send BFD packets until it has received one from the other side. Default: disabled. @@ -2758,7 +2758,7 @@ protocol ospf <name> { dead num When the router does not receive any messages from a neighbor in secondary switch On BSD systems, older versions of BIRD supported OSPFv2 only for the From 8e433d6a529a883d566dc1d5a4afe0f1e2750baf Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Thu, 12 May 2016 15:49:44 +0200 Subject: [PATCH 076/165] Prog Doc: Complete several missing parameters --- conf/cf-lex.l | 1 + lib/Doc | 2 +- nest/neighbor.c | 2 +- nest/proto-hooks.c | 1 + nest/proto.c | 1 + nest/rt-attr.c | 2 +- nest/rt-table.c | 9 +++++++-- proto/babel/Doc | 2 +- proto/ospf/dbdes.c | 1 + proto/ospf/lsalib.c | 3 ++- proto/ospf/packet.c | 2 +- sysdep/unix/io.c | 1 + sysdep/unix/log.c | 1 + 13 files changed, 20 insertions(+), 8 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 5a2a4d6b..727dc017 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -575,6 +575,7 @@ cf_lex_init_kh(void) /** * cf_lex_init - initialize the lexer * @is_cli: true if we're going to parse CLI command, false for configuration + * @c: configuration structure * * cf_lex_init() initializes the lexical analyzer and prepares it for * parsing of a new input. diff --git a/lib/Doc b/lib/Doc index 8f513821..8af1c669 100644 --- a/lib/Doc +++ b/lib/Doc @@ -1,5 +1,5 @@ H Library functions -S ip.c ipv4.c ipv6.c +S ip.c S lists.c S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c D resource.sgml diff --git a/nest/neighbor.c b/nest/neighbor.c index 1685d67e..d974fa51 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -339,7 +339,7 @@ neigh_if_link(struct iface *i) /** * neigh_ifa_update: notify neighbor cache about interface address add or remove event - * @ifa: interface address in question + * @a: interface address in question * * Tell the neighbor cache that an address was added or removed. * diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c index e80f87ea..7395b45e 100644 --- a/nest/proto-hooks.c +++ b/nest/proto-hooks.c @@ -148,6 +148,7 @@ void get_route_info(rte *e, byte *buf, ea_list *attrs) * get_attr - get attribute information * @a: an extended attribute * @buf: buffer to be filled with attribute information + * @buflen: a length of the @buf parameter * * The get_attr() hook is called by the core to obtain a user friendly * representation of an extended route attribute. It can either leave diff --git a/nest/proto.c b/nest/proto.c index 3e97e9da..1091b321 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -718,6 +718,7 @@ graceful_restart_init(void) /** * graceful_restart_done - finalize graceful restart + * @t: unused * * When there are no locks on graceful restart, the functions finalizes the * graceful restart recovery. Protocols postponing route export until the end of diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 7fa05d6d..f181b452 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -405,7 +405,7 @@ ea_find(ea_list *e, unsigned id) * for first occurrences of attributes with ID in specified interval from @id to * (@id + @max - 1), returning pointers to found &eattr structures, storing its * walk state in @s for subsequent calls. - + * * The function ea_walk() is supposed to be called in a loop, with initially * zeroed walk state structure @s with filled the initial extended attribute * list, returning one found attribute in each call or %NULL when no other diff --git a/nest/rt-table.c b/nest/rt-table.c index 57c8b8e0..c02e6dc9 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -717,16 +717,20 @@ rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_ * @net: network in question * @new: the new route to be announced * @old: the previous route for the same network + * @new_best: the new best route for the same network + * @old_best: the previous best route for the same network + * @before_old: The previous route before @old for the same network. + * If @before_old is NULL @old was the first. * * This function gets a routing table update and announces it * to all protocols that acccepts given type of route announcement * and are connected to the same table by their announcement hooks. * - * Route announcement of type RA_OPTIMAL si generated when optimal + * Route announcement of type %RA_OPTIMAL si generated when optimal * route (in routing table @tab) changes. In that case @old stores the * old optimal route. * - * Route announcement of type RA_ANY si generated when any route (in + * Route announcement of type %RA_ANY si generated when any route (in * routing table @tab) changes In that case @old stores the old route * from the same protocol. * @@ -1616,6 +1620,7 @@ rt_prune_step(rtable *tab, int *limit) /** * rt_prune_table - prune a routing table + * @tab: a routing table for pruning * * This function scans the routing table @tab and removes routes belonging to * flushing protocols, discarded routes and also stale network entries, in a diff --git a/proto/babel/Doc b/proto/babel/Doc index 2480239e..80026f91 100644 --- a/proto/babel/Doc +++ b/proto/babel/Doc @@ -1,2 +1,2 @@ S babel.c -S packet.c +S packets.c diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 65bdb3ec..195b03ad 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -192,6 +192,7 @@ ospf_do_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n) /** * ospf_send_dbdes - transmit database description packet + * @p: OSPF protocol instance * @n: neighbor * * Sending of a database description packet is described in 10.8 of RFC 2328. diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index 66a3a23d..1bbd1372 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -554,12 +554,13 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body) /** * lsa_validate - check whether given LSA is valid * @lsa: LSA header + * @lsa_type: one of %LSA_T_xxx + * @ospf2: %true means OSPF version 2, %false means OSPF version 3 * @body: pointer to LSA body * * Checks internal structure of given LSA body (minimal length, * consistency). Returns true if valid. */ - int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body) { diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index faa33664..04f0d47c 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -207,7 +207,7 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ /** * ospf_rx_hook * @sk: socket we received the packet. - * @size: size of the packet + * @len: size of the packet * * This is the entry point for messages from neighbors. Many checks (like * authentication, checksums, size) are done before the packet is passed to diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 82f6c69e..486319ff 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -448,6 +448,7 @@ tm_format_reltime(char *x, struct tm *tm, bird_clock_t delta) /** * tm_format_datetime - convert date and time to textual representation * @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE + * @fmt_spec: specification of resulting textual representation of the time * @t: time * * This function formats the given relative time value @t to a textual diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index b90bbbd2..6665d035 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -89,6 +89,7 @@ static char *class_names[] = { /** * log_commit - commit a log message * @class: message class information (%L_DEBUG to %L_BUG, see |lib/birdlib.h|) + * @buf: message to write * * This function writes a message prepared in the log buffer to the * log file (as specified in the configuration). The log buffer is From 3f2c7600fa2e35b1028c755aa06092b5991e1a8e Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 12 May 2016 21:29:04 +0200 Subject: [PATCH 077/165] We don't need bvsnprintf() in BIRD client --- client/birdc.c | 2 +- client/birdcl.c | 7 ++++--- client/client.c | 29 ++++++++--------------------- client/client.h | 3 +++ client/commands.c | 4 ++-- client/util.c | 7 +++++-- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/client/birdc.c b/client/birdc.c index ccf758be..8aa01c17 100644 --- a/client/birdc.c +++ b/client/birdc.c @@ -153,7 +153,7 @@ input_init(void) // readline library does strange things when stdin is nonblocking. // if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) - // die("fcntl: %m"); + // DIE("fcntl"); } static void diff --git a/client/birdcl.c b/client/birdcl.c index 2d5e1067..7b567a9f 100644 --- a/client/birdcl.c +++ b/client/birdcl.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -109,7 +110,7 @@ more_begin(void) tty.c_lflag &= (~ICANON); if (tcsetattr (0, TCSANOW, &tty) < 0) - die("tcsetattr: %m"); + DIE("tcsetattr"); more_active = 1; } @@ -120,7 +121,7 @@ more_end(void) more_active = 0; if (tcsetattr (0, TCSANOW, &stored_tty) < 0) - die("tcsetattr: %m"); + DIE("tcsetattr"); } static void @@ -137,7 +138,7 @@ input_init(void) return; if (tcgetattr(0, &stored_tty) < 0) - die("tcgetattr: %m"); + DIE("tcgetattr"); if (signal(SIGINT, sig_handler) == SIG_IGN) signal(SIGINT, SIG_IGN); diff --git a/client/client.c b/client/client.c index 4075b9e6..0d4bdf3e 100644 --- a/client/client.c +++ b/client/client.c @@ -248,7 +248,7 @@ server_connect(void) server_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (server_fd < 0) - die("Cannot create socket: %m"); + DIE("Cannot create socket"); if (strlen(server_path) >= sizeof(sa.sun_path)) die("server_connect: path too long"); @@ -257,9 +257,9 @@ server_connect(void) sa.sun_family = AF_UNIX; strcpy(sa.sun_path, server_path); if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - die("Unable to connect to server control socket (%s): %m", server_path); + DIE("Unable to connect to server control socket (%s)", server_path); if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) - die("fcntl: %m"); + DIE("fcntl"); } @@ -309,13 +309,13 @@ server_read(void) redo: c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); if (!c) - die("Connection closed by server."); + die("Connection closed by server"); if (c < 0) { if (errno == EINTR) goto redo; else - die("Server read error: %m"); + DIE("Server read error"); } start = server_read_buf; @@ -366,7 +366,7 @@ select_loop(void) if (errno == EINTR) continue; else - die("select: %m"); + DIE("select"); } if (FD_ISSET(0, &select_fds)) @@ -399,7 +399,7 @@ wait_for_write(int fd) if (errno == EINTR) continue; else - die("select: %m"); + DIE("select"); } if (FD_ISSET(server_fd, &set)) @@ -426,7 +426,7 @@ server_send(char *cmd) else if (errno == EINTR) continue; else - die("Server write error: %m"); + DIE("Server write error"); } else { @@ -436,19 +436,6 @@ server_send(char *cmd) } } - -/* XXXX - - get_term_size(); - - if (tcgetattr(0, &tty_save) != 0) - { - perror("tcgetattr error"); - return(EXIT_FAILURE); - } - } - - */ int main(int argc, char **argv) { diff --git a/client/client.h b/client/client.h index b194a772..f9693def 100644 --- a/client/client.h +++ b/client/client.h @@ -34,3 +34,6 @@ char *cmd_expand(char *cmd); /* client.c */ void submit_command(char *cmd_raw); + +/* die() with system error messages */ +#define DIE(x, y...) die(x ": %s", ##y, strerror(errno)) diff --git a/client/commands.c b/client/commands.c index 226ae048..2dae23e1 100644 --- a/client/commands.c +++ b/client/commands.c @@ -60,7 +60,7 @@ cmd_build_tree(void) if (!new) { int size = sizeof(struct cmd_node) + c-d; - new = xmalloc(size); + new = malloc(size); bzero(new, size); *old->plastson = new; old->plastson = &new->sibling; @@ -314,7 +314,7 @@ cmd_expand(char *cmd) puts("No such command. Press `?' for help."); return NULL; } - b = xmalloc(strlen(n->cmd->command) + strlen(args) + 1); + b = malloc(strlen(n->cmd->command) + strlen(args) + 1); sprintf(b, "%s%s", n->cmd->command, args); return b; } diff --git a/client/util.c b/client/util.c index 050224b9..c35cf8f4 100644 --- a/client/util.c +++ b/client/util.c @@ -21,8 +21,11 @@ vlog(const char *msg, va_list args) { char buf[1024]; - if (bvsnprintf(buf, sizeof(buf)-1, msg, args) < 0) - bsprintf(buf + sizeof(buf) - 100, " ... "); + int n = vsnprintf(buf, sizeof(buf), msg, args); + if (n < 0) + snprintf(buf, sizeof(buf), "???"); + if (n >= sizeof(buf)) + snprintf(buf + sizeof(buf) - 100, 100, " ... "); fputs(buf, stderr); fputc('\n', stderr); } From 9dbcb11cb50167e959536e5a564ee9aafae509c6 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Mon, 30 May 2016 14:28:22 +0200 Subject: [PATCH 078/165] Unix IO: Tried to fix strange behavior after POLLHUP or POLLERR. --- sysdep/unix/io.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 486319ff..8198743d 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1853,6 +1853,20 @@ sk_write(sock *s) } } +void +sk_err(sock *s, int revents) +{ + int se = 0, sse = sizeof(se); + if (revents & POLLERR) + if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &se, &sse) < 0) + { + log(L_ERR "IO: Socket error: SO_ERROR: %m"); + se = 0; + } + + s->err_hook(s, se); +} + void sk_dump_all(void) { @@ -2163,7 +2177,7 @@ io_loop(void) int steps; steps = MAX_STEPS; - if (s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) + if (s->fast_rx && (pfd[s->index].revents & POLLIN) && s->rx_hook) do { steps--; @@ -2185,6 +2199,7 @@ io_loop(void) goto next; } while (e && steps); + current_sock = sk_next(s); next: ; } @@ -2208,18 +2223,26 @@ io_loop(void) goto next2; } - if (!s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) + if (!s->fast_rx && (pfd[s->index].revents & POLLIN) && s->rx_hook) { count++; io_log_event(s->rx_hook, s->data); sk_read(s, pfd[s->index].revents); if (s != current_sock) - goto next2; + goto next2; + } + + if (pfd[s->index].revents & (POLLHUP | POLLERR)) + { + sk_err(s, pfd[s->index].revents); + goto next2; } + current_sock = sk_next(s); next2: ; } + stored_sock = current_sock; } } From 90dc0f08434323535f84d64e113dae84675c46b2 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Sat, 4 Jun 2016 12:38:06 +0200 Subject: [PATCH 079/165] Small typo in documentation example, submitted by Felix Eckhofer. --- doc/bird.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 014225d1..f3c34a14 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1035,7 +1035,7 @@ foot). or ipaddress/netmask. There are two special operators on prefixes: 1.2.0.0/16.pxlen = 16 is true. + So 1.2.0.0/16.len = 16 is true. Date: Wed, 8 Jun 2016 16:22:44 +0200 Subject: [PATCH 080/165] Add AS# ranges to bgpmask. --- doc/bird.sgml | 6 ++++-- filter/config.Y | 1 + filter/filter.c | 4 ++++ filter/test.conf | 9 ++++++--- nest/a-path.c | 26 ++++++++++++++++++-------- nest/attrs.h | 2 ++ 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index f3c34a14..de7041a9 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1165,8 +1165,10 @@ foot). is 4 3 2 1, then: bgp_path ˜ [= * 4 3 * =] is true, but bgp_path ˜ [= * 4 5 * =] is false. BGP mask expressions can also contain integer expressions enclosed in parenthesis - and integer variables, for example [= * 4 (1+2) a =]. There is - also old syntax that uses / .. / instead of [= .. =] and ? instead of *. + and integer variables, for example [= * 4 (1+2) a =]. You can + also use ranges, for example [= * 3..5 2 100..200 * =]. + There is also old (deprecated) syntax that uses / .. / instead of [= .. =] + and ? instead of *. next = $2; $$->kind = PM_ASN; $$->val = $1; } + | NUM DDOT NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; } | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } | '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val = 0; } | bgp_path_expr bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; } diff --git a/filter/filter.c b/filter/filter.c index eddf4228..dce98e4d 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -79,6 +79,10 @@ pm_format(struct f_path_mask *p, buffer *buf) buffer_puts(buf, "* "); break; + case PM_ASN_RANGE: + buffer_print(buf, "%u..%u ", p->val, p->val2); + break; + case PM_ASN_EXPR: buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val)); break; diff --git a/filter/test.conf b/filter/test.conf index a99d0a51..ad8f9386 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -90,20 +90,23 @@ clist l2; eclist el; eclist el2; { + print "Entering path test..."; pm1 = / 4 3 2 1 /; - pm2 = [= 4 3 2 1 =]; + pm2 = [= 3..6 3 2 1..2 =]; print "Testing path masks: ", pm1, " ", pm2; p2 = prepend( + empty +, 1 ); p2 = prepend( p2, 2 ); p2 = prepend( p2, 3 ); p2 = prepend( p2, 4 ); - print "Testing paths: ", p2; + print "Testing path: (4 3 2 1) = ", p2; print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 3 ~ p2, " ", p2 ~ [2, 10..20], " ", p2 ~ [4, 10..20]; print "4 = ", p2.len; p2 = prepend( p2, 5 ); - print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 10 ~ p2, " ", p2 ~ [8, ten..(2*ten)]; + print "Testing path: (5 4 3 2 1) = ", p2; + print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 10 ~ p2, " ", p2 ~ [8, ten..(2*ten)], " ", p2 ~ [= 1..4 4 3 2 1 =], " ", p2 ~ [= 5 4 4..100 2 1 =]; print "Should be true: ", p2 ~ / ? 4 3 2 1 /, " ", p2, " ", / ? 4 3 2 1 /; print "Should be true: ", p2 ~ [= * 4 3 * 1 =], " ", p2, " ", [= * 4 3 * 1 =]; + print "Should be true: ", p2 ~ [= 5..6 4..10 1..3 1..3 1..65536 =]; print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4); print "Should be true: ", p2.len = 5, " ", p2.first = 5, " ", p2.last = 1; print "5 = ", p2.len; diff --git a/nest/a-path.c b/nest/a-path.c index 32e2d27e..e1031b7b 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -435,18 +435,23 @@ parse_path(struct adata *path, struct pm_pos *pos) static int -pm_match(struct pm_pos *pos, u32 asn) +pm_match(struct pm_pos *pos, u32 asn, u32 asn2) { + u32 gas; if (! pos->set) - return pos->val.asn == asn; + return ((pos->val.asn >= asn) && (pos->val.asn <= asn2)); u8 *p = pos->val.sp; int len = *p++; int i; for (i = 0; i < len; i++) - if (get_as(p + i * BS) == asn) + { + gas = get_as(p + i * BS); + + if ((gas >= asn) && (gas <= asn2)) return 1; + } return 0; } @@ -490,7 +495,7 @@ pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh) * next part of mask, we advance each marked state. * We start with marked first position, when we * run out of marked positions, we reject. When - * we process the whole mask, we accept iff final position + * we process the whole mask, we accept if final position * (auxiliary position after last real position in AS path) * is marked. */ @@ -502,6 +507,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask) int plen = parse_path(path, pos); int l, h, i, nh, nl; u32 val = 0; + u32 val2 = 0; /* l and h are bound of interval of positions where are marked states */ @@ -525,12 +531,16 @@ as_path_match(struct adata *path, struct f_path_mask *mask) h = plen; break; - case PM_ASN: - val = mask->val; + case PM_ASN: /* Define single ASN as ASN..ASN - very narrow interval */ + val2 = val = mask->val; goto step; case PM_ASN_EXPR: - val = f_eval_asn((struct f_inst *) mask->val); + val2 = val = f_eval_asn((struct f_inst *) mask->val); goto step; + case PM_ASN_RANGE: + val = mask->val; + val2 = mask->val2; + goto step; case PM_QUESTION: step: nh = nl = -1; @@ -538,7 +548,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask) if (pos[i].mark) { pos[i].mark = 0; - if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val)) + if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val, val2)) pm_mark(pos, i, plen, &nl, &nh); } diff --git a/nest/attrs.h b/nest/attrs.h index 0171c6a8..670b048f 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -45,11 +45,13 @@ struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_ #define PM_QUESTION 1 #define PM_ASTERISK 2 #define PM_ASN_EXPR 3 +#define PM_ASN_RANGE 4 struct f_path_mask { struct f_path_mask *next; int kind; uintptr_t val; + uintptr_t val2; }; int as_path_match(struct adata *path, struct f_path_mask *mask); From 122deb6d5b14f46f3cfb25bf3f5726005d6a3b3e Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 9 Jun 2016 00:30:41 +0200 Subject: [PATCH 081/165] Filters: Fixes pm_same() w.r.t. ASN ranges and ASN expressions This is necessary for proper detection of filter changes during reconfigurations. --- filter/config.Y | 16 ++++++++-------- filter/filter.c | 17 ++++++++++++++--- filter/test.conf | 3 +++ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index e53d8def..da605710 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -626,17 +626,17 @@ bgp_path: ; bgp_path_tail1: - NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } - | NUM DDOT NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; } - | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } - | '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val = 0; } - | bgp_path_expr bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; } - | { $$ = NULL; } + NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } + | NUM DDOT NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; } + | '*' bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; } + | '?' bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; } + | bgp_path_expr bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; } + | { $$ = NULL; } ; bgp_path_tail2: - NUM bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } - | '?' bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } + NUM bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } + | '?' bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; } | { $$ = NULL; } ; diff --git a/filter/filter.c b/filter/filter.c index dce98e4d..c61377b7 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -163,13 +163,24 @@ val_compare(struct f_val v1, struct f_val v2) } static int -pm_path_same(struct f_path_mask *m1, struct f_path_mask *m2) +pm_same(struct f_path_mask *m1, struct f_path_mask *m2) { while (m1 && m2) { - if ((m1->kind != m2->kind) || (m1->val != m2->val)) + if (m1->kind != m2->kind) return 0; + if (m1->kind == PM_ASN_EXPR) + { + if (!i_same((struct f_inst *) m1->val, (struct f_inst *) m2->val)) + return 0; + } + else + { + if ((m1->val != m2->val) || (m1->val2 != m2->val2)) + return 0; + } + m1 = m1->next; m2 = m2->next; } @@ -199,7 +210,7 @@ val_same(struct f_val v1, struct f_val v2) switch (v1.type) { case T_PATH_MASK: - return pm_path_same(v1.val.path_mask, v2.val.path_mask); + return pm_same(v1.val.path_mask, v2.val.path_mask); case T_PATH: case T_CLIST: case T_ECLIST: diff --git a/filter/test.conf b/filter/test.conf index ad8f9386..f61f0658 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -109,6 +109,9 @@ eclist el2; print "Should be true: ", p2 ~ [= 5..6 4..10 1..3 1..3 1..65536 =]; print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4); print "Should be true: ", p2.len = 5, " ", p2.first = 5, " ", p2.last = 1; + print "Should be true: ", pm1 = [= 4 3 2 1 =], " ", pm1 != [= 4 3 1 2 =], " ", + pm2 = [= 3..6 3 2 1..2 =], " ", pm2 != [= 3..6 3 2 1..3 =], " ", + [= 1 2 (1+2) =] = [= 1 2 (1+2) =], " ", [= 1 2 (1+2) =] != [= 1 2 (2+1) =]; print "5 = ", p2.len; print "Delete 3: ", delete(p2, 3); print "Filter 1-3: ", filter(p2, [1..3]); From 8f01879c5629bd714dfeec968337cfcd4dbe6a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Tue, 29 Mar 2016 10:37:31 +0200 Subject: [PATCH 082/165] cppcheck: fix va_end() functions --- client/util.c | 2 ++ conf/conf.c | 1 + sysdep/unix/log.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/client/util.c b/client/util.c index c35cf8f4..2d6c074d 100644 --- a/client/util.c +++ b/client/util.c @@ -40,6 +40,7 @@ bug(const char *msg, ...) fputs("Internal error: ", stderr); vlog(msg, args); vfprintf(stderr, msg, args); + va_end(args); exit(1); } @@ -51,5 +52,6 @@ die(const char *msg, ...) va_start(args, msg); cleanup(); vlog(msg, args); + va_end(args); exit(1); } diff --git a/conf/conf.c b/conf/conf.c index 825a8e9f..efaeedeb 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -512,6 +512,7 @@ cf_error(char *msg, ...) va_start(args, msg); if (bvsnprintf(buf, sizeof(buf), msg, args) < 0) strcpy(buf, ""); + va_end(args); new_config->err_msg = cfg_strdup(buf); new_config->err_lino = ifs->lino; new_config->err_file_name = ifs->file_name; diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index 6665d035..631bd691 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -209,6 +209,7 @@ bug(const char *msg, ...) va_start(args, msg); vlog(L_BUG[0], msg, args); + va_end(args); abort(); } @@ -226,6 +227,7 @@ die(const char *msg, ...) va_start(args, msg); vlog(L_FATAL[0], msg, args); + va_end(args); exit(1); } From 775a5a81958e66a69663803dd2e731e3800da9e7 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 29 Jun 2016 14:11:03 +0200 Subject: [PATCH 083/165] BGP: Skip empty path segments in received AS_PATH Although RFC 4271 does not forbid empty path segments, they are useless and some implementations consider them invalid. It is clarified in RFC 7606, specifying that AS_PATH with empty segment is considered malformed. --- proto/bgp/attrs.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index d85afa8f..b8371f32 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -118,7 +118,7 @@ validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ileng { int res = 0; u8 *a, *dst; - int len, plen, copy; + int len, plen; dst = a = idata; len = *ilength; @@ -132,15 +132,20 @@ validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ileng if (len < plen) return -1; + if (a[1] == 0) + { + log(L_WARN "%s: %s_PATH attribute contains empty segment, skipping it", + p->p.name, as_path ? "AS" : "AS4"); + goto skip; + } + switch (a[0]) { case AS_PATH_SET: - copy = 1; res++; break; case AS_PATH_SEQUENCE: - copy = 1; res += a[1]; break; @@ -154,20 +159,17 @@ validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ileng log(L_WARN "%s: %s_PATH attribute contains AS_CONFED_* segment, skipping segment", p->p.name, as_path ? "AS" : "AS4"); - copy = 0; - break; + goto skip; default: return -1; } - if (copy) - { - if (dst != a) - memmove(dst, a, plen); - dst += plen; - } + if (dst != a) + memmove(dst, a, plen); + dst += plen; + skip: len -= plen; a += plen; } From f1f39bb9d8e2260fe181240dd8194b06bdcfb54f Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Fri, 1 Jul 2016 11:03:13 +0200 Subject: [PATCH 084/165] Filter: Fixes reconfiguration with last_nonaggregated operator --- filter/filter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filter/filter.c b/filter/filter.c index c61377b7..30571e5a 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1461,7 +1461,8 @@ i_same(struct f_inst *f1, struct f_inst *f2) case P('A','p'): TWOARGS; break; case P('C','a'): TWOARGS; break; case P('a','f'): - case P('a','l'): ONEARG; break; + case P('a','l'): + case P('a','L'): ONEARG; break; case P('R','C'): TWOARGS; /* Does not really make sense - ROA check resuls may change anyway */ From f0b822a831d0f0f593bbedf0a7f15b94c3ca1d43 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 11 Jul 2016 20:22:55 +0200 Subject: [PATCH 085/165] Log: Fix error handling of debug file open Logging is not yet initialized, we have to use fprintf() here. Thanks to Pavel Tvrdik for noticing and debugging it. --- sysdep/unix/log.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index 631bd691..1fd64426 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "nest/bird.h" #include "nest/cli.h" @@ -314,7 +315,11 @@ log_init_debug(char *f) else if (!*f) dbgf = stderr; else if (!(dbgf = fopen(f, "a"))) - log(L_ERR "Error opening debug file `%s': %m", f); + { + /* Cannot use die() nor log() here, logging is not yet initialized */ + fprintf(stderr, "bird: Unable to open debug file %s: %s\n", f, strerror(errno)); + exit(1); + } if (dbgf) setvbuf(dbgf, NULL, _IONBF, 0); } From ea0cb652e91d871e7808e93aa15ef34fc453c4fc Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 11 Jul 2016 20:41:32 +0200 Subject: [PATCH 086/165] BGP: Fix extended messages Change attribute length limit to handle extended (64 kB) messages. Do not mix updates and withdraws (RFC 7606). --- proto/bgp/packets.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 72ca3728..0cf38edf 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -369,7 +369,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) } put_u16(buf, wd_size); - if (remains >= 3072) + if (!wd_size) { while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next) { @@ -382,7 +382,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) } DBG("Processing bucket %p\n", buck); - a_size = bgp_encode_attrs(p, w+2, buck->eattrs, 2048); + a_size = bgp_encode_attrs(p, w+2, buck->eattrs, remains - 1024); if (a_size < 0) { @@ -461,8 +461,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) w += size; remains -= size; } - - if (remains >= 3072) + else { while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next) { @@ -478,7 +477,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) rem_stored = remains; w_stored = w; - size = bgp_encode_attrs(p, w, buck->eattrs, 2048); + size = bgp_encode_attrs(p, w, buck->eattrs, remains - 1024); if (size < 0) { log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); From 6887f409f0766c60dd8c3d3fdf01a1d989c090a5 Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Sun, 17 Jul 2016 14:54:52 +0200 Subject: [PATCH 087/165] Prepare for longer interface names - clean up of the code. --- nest/rt-table.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index c02e6dc9..50c01c88 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -50,7 +50,7 @@ static linpool *rte_update_pool; static list routing_tables; -static void rt_format_via(rte *e, byte *via); +static byte *rt_format_via(rte *e); static void rt_free_hostcache(rtable *tab); static void rt_notify_hostcache(rtable *tab, net *net); static void rt_update_hostcache(rtable *tab); @@ -227,10 +227,7 @@ rte_mergable(rte *pri, rte *sec) static void rte_trace(struct proto *p, rte *e, int dir, char *msg) { - byte via[STD_ADDRESS_P_LENGTH+32]; - - rt_format_via(e, via); - log(L_TRACE "%s %c %s %I/%d %s", p->name, dir, msg, e->net->n.prefix, e->net->n.pxlen, via); + log(L_TRACE "%s %c %s %I/%d %s", p->name, dir, msg, e->net->n.prefix, e->net->n.pxlen, rt_format_via(e)); } static inline void @@ -2364,11 +2361,14 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_add * CLI commands */ -static void -rt_format_via(rte *e, byte *via) +static byte * +rt_format_via(rte *e) { rta *a = e->attrs; + /* Max text length w/o IP addr and interface name is 16 */ + static byte via[STD_ADDRESS_P_LENGTH+sizeof(a->iface->name)+16]; + switch (a->dest) { case RTD_ROUTER: bsprintf(via, "via %I on %s", a->gw, a->iface->name); break; @@ -2379,12 +2379,13 @@ rt_format_via(rte *e, byte *via) case RTD_MULTIPATH: bsprintf(via, "multipath"); break; default: bsprintf(via, "???"); } + return via; } static void rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa) { - byte via[STD_ADDRESS_P_LENGTH+32], from[STD_ADDRESS_P_LENGTH+8]; + byte from[STD_ADDRESS_P_LENGTH+8]; byte tm[TM_DATETIME_BUFFER_SIZE], info[256]; rta *a = e->attrs; int primary = (e->net->routes == e); @@ -2392,7 +2393,6 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); struct mpnh *nh; - rt_format_via(e, via); tm_format_datetime(tm, &config->tf_route, e->lastmod); if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw)) bsprintf(from, " from %I", a->from); @@ -2413,7 +2413,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm get_route_info(e, info, tmpa); else bsprintf(info, " (%d)", e->pref); - cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, via, a->src->proto->name, + cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); for (nh = a->nexthops; nh; nh = nh->next) cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1); From 321ff8c4049ec6c2fa198858b4a7f1814ce05e39 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 19 Jul 2016 11:57:20 +0200 Subject: [PATCH 088/165] Babel: Make sure intervals do not overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Intervals are carried as 16-bit centisecond values, but kept internally in 16-bit second values, which causes a potential for overflow. This adds some checks to make sure this does not happen. Signed-off-by: Toke Høiland-Jørgensen --- proto/babel/babel.h | 4 +++- proto/babel/config.Y | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/proto/babel/babel.h b/proto/babel/babel.h index aea0dd88..7b5037ab 100644 --- a/proto/babel/babel.h +++ b/proto/babel/babel.h @@ -50,10 +50,12 @@ #define BABEL_INITIAL_HOP_COUNT 255 #define BABEL_MAX_SEND_INTERVAL 5 #define BABEL_TIME_UNITS 100 /* On-wire times are counted in centiseconds */ - #define BABEL_SEQNO_REQUEST_EXPIRY 60 #define BABEL_GARBAGE_INTERVAL 300 +/* Max interval that will not overflow when carried as 16-bit centiseconds */ +#define BABEL_MAX_INTERVAL (0xFFFF/BABEL_TIME_UNITS) + #define BABEL_OVERHEAD (SIZE_OF_IP_HEADER+UDP_HEADER_LENGTH) #define BABEL_MIN_MTU (512 + BABEL_OVERHEAD) diff --git a/proto/babel/config.Y b/proto/babel/config.Y index e7ce6a93..b6170852 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -77,17 +77,18 @@ babel_iface_finish: BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED; } + /* Make sure we do not overflow the 16-bit centisec fields */ if (!BABEL_IFACE->update_interval) - BABEL_IFACE->update_interval = BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR; - BABEL_IFACE->ihu_interval = BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR; + BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL); + BABEL_IFACE->ihu_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR, BABEL_MAX_INTERVAL); }; babel_iface_item: | PORT expr { BABEL_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); } | RXCOST expr { BABEL_IFACE->rxcost = $2; if (($2<1) || ($2>65535)) cf_error("Invalid rxcost"); } - | HELLO INTERVAL expr { BABEL_IFACE->hello_interval = $3; if (($3<1) || ($3>65535)) cf_error("Invalid hello interval"); } - | UPDATE INTERVAL expr { BABEL_IFACE->update_interval = $3; if (($3<1) || ($3>65535)) cf_error("Invalid hello interval"); } + | HELLO INTERVAL expr { BABEL_IFACE->hello_interval = $3; if (($3<1) || ($3>BABEL_MAX_INTERVAL)) cf_error("Invalid hello interval"); } + | UPDATE INTERVAL expr { BABEL_IFACE->update_interval = $3; if (($3<1) || ($3>BABEL_MAX_INTERVAL)) cf_error("Invalid update interval"); } | TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; } | TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; } | RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); } From 12640c149976e1eca54d9c22c593d07a27c49d42 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 19 Jul 2016 12:16:51 +0200 Subject: [PATCH 089/165] Babel: Documentation updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This updates the documentation to correctly mention Babel when protocols are listed, and adds examples and route attribute documentation to the Babel section of the docs. Signed-off-by: Toke Høiland-Jørgensen --- doc/bird.sgml | 34 +++++++++++++++++++++++++++++++--- nest/config.Y | 2 +- nest/rt-attr.c | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index de7041a9..b90f65d1 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -610,8 +610,8 @@ agreement"). options, in that case for given interface the first matching interface option is used. - This option is allowed in BFD, Direct, OSPF, RAdv and RIP protocols, but - in OSPF protocol it is used in the Route type (Currently ] { yes. +Attributes + +

Babel defines just one attribute: the internal babel metric of the route. It +is exposed as the Example + +

+protocol babel { + interface "eth*" { + type wired; + }; + interface "wlan0", "wlan1" { + type wireless; + hello interval 1; + rxcost 512; + }; + interface "tap0"; + + # This matches the default of babeld: redistribute all addresses + # configured on local interfaces, plus re-distribute all routes received + # from other babel peers. + + export where (source = RTS_DEVICE) || (source = RTS_BABEL); +} + +

In Linux, there is also a plenty of obscure route attributes mostly focused diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index 96688e34..6d6586d1 100644 --- a/sysdep/linux/krt-sys.h +++ b/sysdep/linux/krt-sys.h @@ -36,6 +36,7 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; } #define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0x10) #define EA_KRT_REALM EA_CODE(EAP_KRT, 0x11) +#define EA_KRT_SCOPE EA_CODE(EAP_KRT, 0x12) #define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */ diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index a1c22f3e..f577244d 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -10,7 +10,7 @@ CF_HDR CF_DECLS -CF_KEYWORDS(KERNEL, TABLE, METRIC, KRT_PREFSRC, KRT_REALM, KRT_MTU, KRT_WINDOW, +CF_KEYWORDS(KERNEL, TABLE, METRIC, KRT_PREFSRC, KRT_REALM, KRT_SCOPE, KRT_MTU, KRT_WINDOW, KRT_RTT, KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING, KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK, KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR, @@ -28,6 +28,7 @@ kern_sys_item: CF_ADDTO(dynamic_attr, KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); }) CF_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); }) +CF_ADDTO(dynamic_attr, KRT_SCOPE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SCOPE); }) CF_ADDTO(dynamic_attr, KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU); }) CF_ADDTO(dynamic_attr, KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); }) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 9bdcc0d2..1490213e 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -899,7 +899,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d r.r.rtm_family = BIRD_AF; r.r.rtm_dst_len = net->n.pxlen; r.r.rtm_protocol = RTPROT_BIRD; - r.r.rtm_scope = RT_SCOPE_UNIVERSE; + r.r.rtm_scope = RT_SCOPE_NOWHERE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); /* @@ -928,6 +928,12 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d if (op == NL_OP_DELETE) goto dest; + /* Default scope is LINK for device routes, UNIVERSE otherwise */ + if (ea = ea_find(eattrs, EA_KRT_SCOPE)) + r.r.rtm_scope = ea->u.data; + else + r.r.rtm_scope = (dest == RTD_DEVICE) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; + if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); @@ -1135,6 +1141,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) u32 oif = ~0; u32 table; u32 priority = 0; + u32 def_scope = RT_SCOPE_UNIVERSE; int src; if (!(i = nl_checkin(h, sizeof(*i)))) @@ -1157,7 +1164,6 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) return; } - if (a[RTA_DST]) { memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst)); @@ -1195,11 +1201,6 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) SKIP("strange class/scope\n"); - // ignore rtm_scope, it is not a real scope - // if (i->rtm_scope != RT_SCOPE_UNIVERSE) - // SKIP("scope %u\n", i->rtm_scope); - - switch (i->rtm_protocol) { case RTPROT_UNSPEC: @@ -1286,6 +1287,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) else { ra->dest = RTD_DEVICE; + def_scope = RT_SCOPE_LINK; } break; @@ -1304,6 +1306,19 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) return; } + if (i->rtm_scope != def_scope) + { + ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr)); + ea->next = ra->eattrs; + ra->eattrs = ea; + ea->flags = EALF_SORTED; + ea->count = 1; + ea->attrs[0].id = EA_KRT_SCOPE; + ea->attrs[0].flags = 0; + ea->attrs[0].type = EAF_TYPE_INT; + ea->attrs[0].u.data = i->rtm_scope; + } + if (a[RTA_PREFSRC]) { ip_addr ps; @@ -1633,6 +1648,10 @@ krt_sys_get_attr(eattr *a, byte *buf, int buflen UNUSED) bsprintf(buf, "realm"); return GA_NAME; + case EA_KRT_SCOPE: + bsprintf(buf, "scope"); + return GA_NAME; + case EA_KRT_LOCK: buf += bsprintf(buf, "lock:"); ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX); From 75ac3d199d1fd5b199dd753915234b8634c272e5 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 21 Sep 2016 12:07:59 +0200 Subject: [PATCH 117/165] Remove cvsignore We have gitignore --- .cvsignore | 1 - .gitignore | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) delete mode 100644 .cvsignore diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index dc557db3..00000000 --- a/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -bird.conf diff --git a/.gitignore b/.gitignore index 77dea498..52510836 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +/autom4te.cache/ +/doc/*.html +/obj/ /Makefile /bird /birdc @@ -5,4 +8,5 @@ /config.log /config.status /configure -/obj/ +/bird.conf +/bird.log From 768d5e1058693d2bfb7c3bcbe04306097c3246a0 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Tue, 20 Sep 2016 15:13:01 +0200 Subject: [PATCH 118/165] Add !~ operator to filter grammar --- conf/cf-lex.l | 1 + conf/confbase.Y | 2 +- doc/bird.sgml | 38 +++++++++++++++++++------------------- filter/config.Y | 1 + filter/filter.c | 10 ++++++++++ 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 61ea4527..d2aa6402 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -246,6 +246,7 @@ else: { . \!\= return NEQ; +\!\~ return NMA; \<\= return LEQ; \>\= return GEQ; \&\& return AND; diff --git a/conf/confbase.Y b/conf/confbase.Y index 5f487c1d..c14c23c7 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -82,7 +82,7 @@ CF_DECLS %nonassoc PREFIX_DUMMY %left AND OR -%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ PO PC +%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC %left '+' '-' %left '*' '/' '%' %left '!' diff --git a/doc/bird.sgml b/doc/bird.sgml index af5b7980..07a2d470 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1016,9 +1016,9 @@ foot). of type @@ -1213,19 +1213,19 @@ foot).

The filter language supports common integer operators (+,-,*,/), parentheses There is one operator related to ROA infrastructure - ' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; } | term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; } | term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; } + | term NMA term { $$ = f_new_inst(); $$->code = P('!','~'); $$->a1.p = $1; $$->a2.p = $3; } | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; } | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; } diff --git a/filter/filter.c b/filter/filter.c index ccdfed36..2f5f00d8 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -716,6 +716,16 @@ interpret(struct f_inst *what) runtime( "~ applied on unknown type pair" ); res.val.i = !!res.val.i; break; + + case P('!','~'): + TWOARGS; + res.type = T_BOOL; + res.val.i = val_in_range(v1, v2); + if (res.val.i == CMP_ERROR) + runtime( "!~ applied on unknown type pair" ); + res.val.i = !res.val.i; + break; + case P('d','e'): ONEARG; res.type = T_BOOL; From 79e2293ab2abbd92bb3326a95759a4ca32d9af81 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 22 Sep 2016 13:34:56 +0200 Subject: [PATCH 119/165] NEWS and version update --- NEWS | 13 +++++++++++++ misc/bird.spec | 2 +- sysdep/config.h | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index f689d195..525d7007 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,16 @@ +Version 1.6.1 (2016-09-22) + o Support for IPv6 ECMP + o Better handling of IPv6 tentative addresses + o Several updates and fixes in Babel protocol + o Filter: New !~ operator + o Filter: ASN ranges in bgpmask + o KRT: New kernel protocol option 'metric' + o KRT: New route attribute 'krt_scope' + o Improved BIRD help messages + o Fixes memory leak in BGP multipath + o Fixes handling of empty path segments in BGP AS_PATH + o Several bug fixes + Version 1.6.0 (2016-04-29) o Major RIP protocol redesign o New Babel routing protocol diff --git a/misc/bird.spec b/misc/bird.spec index 857f03e5..89531d64 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 1.6.0 +Version: 1.6.1 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/sysdep/config.h b/sysdep/config.h index a8d58349..e83a934f 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -7,7 +7,7 @@ #define _BIRD_CONFIG_H_ /* BIRD version */ -#define BIRD_VERSION "1.6.0" +#define BIRD_VERSION "1.6.1" /* Include parameters determined by configure script */ #include "sysdep/autoconf.h" From ccd2a3eda24230df550e9880f4340fc6341c8f52 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Thu, 29 Sep 2016 12:00:53 +0200 Subject: [PATCH 120/165] Kernel socket missing err_hook fix Thanks to Tim Weippert for bugreport. --- sysdep/bsd/krt-sock.c | 7 +++++++ sysdep/linux/netlink.c | 7 +++++++ sysdep/unix/io.c | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 6ff3b2b7..f2ae81c3 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -911,6 +911,12 @@ krt_sock_hook(sock *sk, int size UNUSED) return 0; } +static void +krt_sock_err_hook(sock *sk, int e UNUSED) +{ + krt_sock_hook(sk, 0); +} + static sock * krt_sock_open(pool *pool, void *data, int table_id) { @@ -932,6 +938,7 @@ krt_sock_open(pool *pool, void *data, int table_id) sk = sk_new(pool); sk->type = SK_MAGIC; sk->rx_hook = krt_sock_hook; + sk->err_hook = krt_sock_err_hook; sk->fd = fd; sk->data = data; diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 1490213e..79dd1405 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1525,6 +1525,12 @@ nl_async_hook(sock *sk, int size UNUSED) return 1; } +static void +nl_async_err_hook(sock *sk, int e UNUSED) +{ + nl_async_hook(sk, 0); +} + static void nl_open_async(void) { @@ -1563,6 +1569,7 @@ nl_open_async(void) sk = nl_async_sk = sk_new(krt_pool); sk->type = SK_MAGIC; sk->rx_hook = nl_async_hook; + sk->err_hook = nl_async_err_hook; sk->fd = fd; if (sk_open(sk) < 0) bug("Netlink: sk_open failed"); diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 120eb906..3ceadd98 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1857,7 +1857,7 @@ void sk_err(sock *s, int revents) { int se = 0, sse = sizeof(se); - if (revents & POLLERR) + if ((s->type != SK_MAGIC) && (revents & POLLERR)) if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &se, &sse) < 0) { log(L_ERR "IO: Socket error: SO_ERROR: %m"); From 5bf35a9aee448ce7e3493ec7b80c84c8f5f39242 Mon Sep 17 00:00:00 2001 From: Pavel Tvrdik Date: Thu, 29 Sep 2016 13:28:18 +0200 Subject: [PATCH 121/165] Docs: fix BFD label BFD headline will appear in Table of Contents again. --- doc/bird.sgml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 07a2d470..26673f03 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1506,7 +1506,8 @@ protocol babel { -

+ ".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 131/165] 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 132/165] 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 133/165] 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 134/165] 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 138/165] 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 139/165] 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 155/165] 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 156/165] 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; + + ASSERT(*plen < ifa->sk->tbsize); - void *tail = ((void *) pkt) + plen; - char password[OSPF_AUTH_CRYPT_SIZE]; - strncpy(password, passwd->password, sizeof(password)); + auth->c32.zero = 0; + auth->c32.keyid = pass->id; + auth->c32.len = auth_len; + auth->c32.csn = htonl(ifa->csn); - 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); + /* 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); + + uint auth_len = mac_type_length(pass->alg); - if (plen + OSPF_AUTH_CRYPT_SIZE > len) - DROP("length mismatch", len); + if (plen + auth->c32.len > len) + DROP("packet length mismatch", len); - u32 rcv_csn = ntohl(auth->md5.csn); + 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 *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); + byte *auth_tail = ((byte *) pkt) + plen; + byte *auth_data = alloca(auth_len); + memcpy(auth_data, auth_tail, auth_len); - struct hash_context ctx; - md5_init(&ctx); - md5_update(&ctx, (byte *) pkt, plen + OSPF_AUTH_CRYPT_SIZE); - char *computed = md5_final(&ctx); + /* 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); - 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 157/165] 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 158/165] 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 159/165] 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 | 13 ++++++++++++- proto/ospf/rt.c | 4 ++-- sysdep/linux/syspriv.h | 9 +++++++++ sysdep/unix/io.c | 5 ++++- sysdep/unix/main.c | 4 +++- 13 files changed, 81 insertions(+), 42 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 From 261816b0d4f3d4549a4402b95541b82fc7f10a4b Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 15 Nov 2016 16:24:39 +0100 Subject: [PATCH 160/165] BGP: Cluster list item should be prepended Commit 3c09af41... changed behavior of int_set_add() from prepend to append, which makes more sense for community list, but prepend must be used for cluster list. Add int_set_prepend() and use it in cluster list handling code. --- nest/a-set.c | 23 +++++++++++++++++++++-- nest/attrs.h | 1 + proto/bgp/attrs.c | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/nest/a-set.c b/nest/a-set.c index bd244e2e..a6c07f45 100644 --- a/nest/a-set.c +++ b/nest/a-set.c @@ -231,6 +231,26 @@ lc_set_contains(struct adata *list, lcomm val) return 0; } +struct adata * +int_set_prepend(struct linpool *pool, struct adata *list, u32 val) +{ + struct adata *res; + int len; + + if (int_set_contains(list, val)) + return list; + + len = list ? list->length : 0; + res = lp_alloc(pool, sizeof(struct adata) + len + 4); + res->length = len + 4; + + if (list) + memcpy(res->data + 4, list->data, list->length); + + * (u32 *) res->data = val; + + return res; +} struct adata * int_set_add(struct linpool *pool, struct adata *list, u32 val) @@ -248,8 +268,7 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val) if (list) memcpy(res->data, list->data, list->length); - u32 *c = (u32 *) (res->data + len); - *c = val; + * (u32 *) (res->data + len) = val; return res; } diff --git a/nest/attrs.h b/nest/attrs.h index 548d71a9..a34e64d3 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -132,6 +132,7 @@ 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_prepend(struct linpool *pool, struct adata *list, u32 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); diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 0309c1f7..aa2a3b46 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1077,7 +1077,7 @@ static inline void bgp_cluster_list_prepend(rte *e, ea_list **attrs, struct linpool *pool, u32 cid) { eattr *a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); - bgp_attach_attr(attrs, pool, BA_CLUSTER_LIST, (uintptr_t) int_set_add(pool, a ? a->u.ptr : NULL, cid)); + bgp_attach_attr(attrs, pool, BA_CLUSTER_LIST, (uintptr_t) int_set_prepend(pool, a ? a->u.ptr : NULL, cid)); } static int From ed1a908e535e4333b358d83b472453a2ad6d3f51 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Fri, 25 Nov 2016 11:51:38 +0100 Subject: [PATCH 161/165] BGP: Fix memory leak in graceful restart code Prefix and bucket tables are initialized when entering established state but not explicitly freed when leaving it (that is handled by protocol restart). With graceful restart, BGP may enter and leave established state multiple times without hard protocol restart causing memory leak. --- lib/hash.h | 6 ++++++ proto/bgp/attrs.c | 26 ++++++++++++++++++++++++++ proto/bgp/bgp.c | 3 +++ proto/bgp/bgp.h | 2 ++ 4 files changed, 37 insertions(+) diff --git a/lib/hash.h b/lib/hash.h index 4239b1d8..6995bbc8 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -25,6 +25,12 @@ (v).data = mb_allocz(pool, HASH_SIZE(v) * sizeof(* (v).data)); \ }) +#define HASH_FREE(v) \ + ({ \ + mb_free((v).data); \ + (v) = (typeof(v)){ }; \ + }) + #define HASH_FIND(v,id,key...) \ ({ \ u32 _h = HASH_FN(v, id, key); \ diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index aa2a3b46..9d23374a 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -934,6 +934,15 @@ bgp_init_prefix_table(struct bgp_proto *p, u32 order) p->prefix_slab = sl_new(p->p.pool, sizeof(struct bgp_prefix)); } +void +bgp_free_prefix_table(struct bgp_proto *p) +{ + HASH_FREE(p->prefix_hash); + + rfree(p->prefix_slab); + p->prefix_slab = NULL; +} + static struct bgp_prefix * bgp_get_prefix(struct bgp_proto *p, ip_addr prefix, int pxlen, u32 path_id) { @@ -1940,6 +1949,23 @@ bgp_init_bucket_table(struct bgp_proto *p) // fib_init(&p->prefix_fib, p->p.pool, sizeof(struct bgp_prefix), 0, bgp_init_prefix); } +void +bgp_free_bucket_table(struct bgp_proto *p) +{ + mb_free(p->bucket_hash); + p->bucket_hash = NULL; + + struct bgp_bucket *b; + WALK_LIST_FIRST(b, p->bucket_queue) + { + rem_node(&b->send_node); + mb_free(b); + } + + mb_free(p->withdraw_bucket); + p->withdraw_bucket = NULL; +} + void bgp_get_route_info(rte *e, byte *buf, ea_list *attrs) { diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 8ef4b990..0f1c9446 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -416,6 +416,9 @@ bgp_conn_leave_established_state(struct bgp_proto *p) BGP_TRACE(D_EVENTS, "BGP session closed"); p->conn = NULL; + bgp_free_prefix_table(p); + bgp_free_bucket_table(p); + if (p->p.proto_state == PS_UP) bgp_stop(p, 0); } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index b4067f3a..d028bef4 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -253,8 +253,10 @@ int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_be void bgp_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs); int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *); void bgp_init_bucket_table(struct bgp_proto *); +void bgp_free_bucket_table(struct bgp_proto *p); void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck); void bgp_init_prefix_table(struct bgp_proto *p, u32 order); +void bgp_free_prefix_table(struct bgp_proto *p); void bgp_free_prefix(struct bgp_proto *p, struct bgp_prefix *bp); uint bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains); void bgp_get_route_info(struct rte *, byte *buf, struct ea_list *attrs); From 9e7d3a781075b39a7e0f97e63b6f313955daa661 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 13 Dec 2016 17:34:42 +0100 Subject: [PATCH 162/165] OSPF: Fix net-summary origination combined with stubnet option Stubnet nodes in OSPF FIB were removed during rt_sync(), but the pointer remained in top_hash_entry.nf, so net-summary LSA origination was confused, reported 'LSA ID collision' and net-summary LSAs were not originated properly. Thanks to Naveen Chowdary Yerramneni for bugreport and analysis. --- proto/ospf/rt.c | 17 ++++++++++------- proto/ospf/rt.h | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 19f2d074..21aaf144 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -429,10 +429,9 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_ if (en == oa->rt) { /* - * Local stub networks does not have proper iface in en->nhi - * (because they all have common top_hash_entry en). - * We have to find iface responsible for that stub network. - * Configured stubnets does not have any iface. They will + * Local stub networks do not have proper iface in en->nhi (because they all + * have common top_hash_entry en). We have to find iface responsible for + * that stub network. Configured stubnets do not have any iface. They will * be removed in rt_sync(). */ @@ -1560,6 +1559,7 @@ ospf_rt_reset(struct ospf_proto *p) { ri = (ort *) nftmp; ri->area_net = 0; + ri->keep = 0; reset_ri(ri); } FIB_WALK_END; @@ -1929,9 +1929,12 @@ rt_sync(struct ospf_proto *p) } } - /* Remove configured stubnets */ - if (!nf->n.nhs) + /* Remove configured stubnets but keep the entries */ + if (nf->n.type && !nf->n.nhs) + { reset_ri(nf); + nf->keep = 1; + } if (nf->n.type) /* Add the route */ { @@ -1991,7 +1994,7 @@ rt_sync(struct ospf_proto *p) } /* Remove unused rt entry, some special entries are persistent */ - if (!nf->n.type && !nf->external_rte && !nf->area_net) + if (!nf->n.type && !nf->external_rte && !nf->area_net && !nf->keep) { FIB_ITERATE_PUT(&fit, nftmp); fib_delete(fib, nftmp); diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index 30332f3b..73b28375 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -84,6 +84,7 @@ typedef struct ort rta *old_rta; u8 external_rte; u8 area_net; + u8 keep; } ort; From 7d95c44572d79ef15ec8b0220950b4e4374c6bc6 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Thu, 15 Dec 2016 15:31:25 +0100 Subject: [PATCH 163/165] OSPF: Fix ECMP external merging The variable nfa is not cleaned before each loop iteration and can have a wrong value of nfa.nhs_reuse from the previous step. Thanks to Bernardo Figueiredo for the bugreport and analysis. --- proto/ospf/rt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 21aaf144..368e3d05 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1405,7 +1405,6 @@ ospf_ext_spf(struct ospf_proto *p) struct top_hash_entry *en; struct ospf_lsa_ext_local rt; ort *nf1, *nf2; - orta nfa = {}; ip_addr rtid; u32 br_metric; struct ospf_area *atmp; @@ -1414,6 +1413,8 @@ ospf_ext_spf(struct ospf_proto *p) WALK_SLIST(en, p->lsal) { + orta nfa = {}; + /* 16.4. (1) */ if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_NSSA)) continue; From 2c33da507046c25d87741fe0ce7947985c8c7a10 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 20 Dec 2016 20:13:08 +0100 Subject: [PATCH 164/165] Netlink: fix occasional netlink hangs on busy machines --- sysdep/linux/netlink.c | 1 + sysdep/unix/io.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 368e0ef9..22313f43 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1497,6 +1497,7 @@ nl_async_hook(sock *sk, uint size UNUSED) * One day we might react to it by asking for route table * scan in near future. */ + log(L_WARN "Kernel dropped some netlink messages, will resync on next scan."); return 1; /* More data are likely to be ready */ } else if (errno != EWOULDBLOCK) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 644a4fcd..8773f4c4 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -2238,7 +2238,8 @@ io_loop(void) if (pfd[s->index].revents & (POLLHUP | POLLERR)) { sk_err(s, pfd[s->index].revents); - goto next2; + if (s != current_sock) + goto next2; } current_sock = sk_next(s); From 017da76b729cc36c4a3416995b06386235660f42 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 21 Dec 2016 16:46:47 +0100 Subject: [PATCH 165/165] NEWS and version update --- NEWS | 7 +++++++ misc/bird.spec | 2 +- sysdep/config.h | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 26314d82..a9bd6a72 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,10 @@ +Version 1.6.3 (2016-12-21) + o Large BGP communities + o BFD authentication (MD5, SHA1) + o SHA1 and SHA2 authentication for RIP and OSPF + o Improved documentation + o Several bug fixes + Version 1.6.2 (2016-09-29) o Fixes serious bug introduced in the previous version diff --git a/misc/bird.spec b/misc/bird.spec index ebc0c942..bfbfc484 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 1.6.2 +Version: 1.6.3 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/sysdep/config.h b/sysdep/config.h index c7f63e69..84085f9c 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -7,7 +7,7 @@ #define _BIRD_CONFIG_H_ /* BIRD version */ -#define BIRD_VERSION "1.6.2" +#define BIRD_VERSION "1.6.3" /* Include parameters determined by configure script */ #include "sysdep/autoconf.h"