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/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 diff --git a/doc/bird.sgml b/doc/bird.sgml index df83aacd..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.

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 +3268,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|quiet|nolisten|version1 - This option selects the mode for RIP to use on the interface. If nothing - is specified, RIP runs in multicast mode. 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. + + 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 +3433,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 +3468,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/filter/config.Y b/filter/config.Y index 7eb2c0a3..b94f5dff 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -283,7 +283,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, LEN, DEFINED, ADD, DELETE, CONTAINS, RESET, - PREPEND, FIRST, LAST, MATCH, + PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH, ROA_CHECK, EMPTY, FILTER, WHERE, EVAL) @@ -752,6 +752,7 @@ term: | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->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/lib/birdlib.h b/lib/birdlib.h index 24e769db..e2186a05 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -32,6 +32,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/ip.c b/lib/ip.c index 70f33783..58e1d911 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 :: */ { @@ -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..5389c44a 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 @@ -446,8 +453,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/lists.c b/lib/lists.c index 54cd5de8..f2532a62 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; } @@ -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; @@ -150,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; } /** @@ -172,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 d4aeccbb..f76c564e 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)) @@ -62,7 +75,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/md5.c b/lib/md5.c index 497ff408..8efa62d6 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -3,16 +3,14 @@ * * (c) 2015 CZ.NIC z.s.p.o. * - * 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 */ @@ -57,7 +55,7 @@ md5_init(struct md5_context *ctx) * of bytes. */ void -md5_update(struct md5_context *ctx, byte const *buf, uint len) +md5_update(struct md5_context *ctx, const byte *buf, uint len) { u32 t; @@ -71,7 +69,6 @@ md5_update(struct md5_context *ctx, byte const *buf, uint len) t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ - if (t) { byte *p = (byte *) ctx->in + t; @@ -88,8 +85,8 @@ md5_update(struct md5_context *ctx, byte const *buf, uint len) buf += t; len -= t; } - /* Process data in 64-byte chunks */ + /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); @@ -100,12 +97,11 @@ md5_update(struct md5_context *ctx, byte const *buf, uint len) } /* 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) */ byte * diff --git a/lib/md5.h b/lib/md5.h index 3592b117..034d764c 100644 --- a/lib/md5.h +++ b/lib/md5.h @@ -3,7 +3,7 @@ * * (c) 2015 CZ.NIC z.s.p.o. * - * Adapted for BIRD by Martin Mares + * Adapted for BIRD by Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -11,26 +11,30 @@ #ifndef _BIRD_MD5_H_ #define _BIRD_MD5_H_ -#define MD5_SIZE 16 -#define MD5_HEX_SIZE 33 -#define MD5_BLOCK_SIZE 64 +#include "nest/bird.h" -struct md5_context -{ + +#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 md5_init(struct md5_context *context); -void md5_update(struct md5_context *context, byte const *buf, uint len); -byte *md5_final(struct md5_context *context); +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_hmac_context { struct md5_context ictx; struct md5_context octx; }; @@ -39,4 +43,5 @@ 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 /* _BIRD_MD5_H_ */ 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/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; + struct mblock *b = SKIP_BACK(struct mblock, data, m); - if (m) - { - ob = SKIP_BACK(struct mblock, data, m); - if (ob->r.n.next) - rem_node(&ob->r.n); - } - - 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/lib/sha1.c b/lib/sha1.c index 457a0ed5..73b4b280 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -12,39 +12,38 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#include - -#include "lib/null.h" #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); @@ -72,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] ); @@ -157,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++; + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA1_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; + + if (ctx->count < SHA1_BLOCK_SIZE) + return; + + /* Process data from internal buffer */ + sha1_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; } - if (!inbuf) + + if (!len) return; - if (hd->count) + /* Process data from input buffer */ + while (len >= SHA1_BLOCK_SIZE) { - for (; inlen && hd->count < 64; inlen--) - hd->buf[hd->count++] = *inbuf++; - sha1_update( hd, NULL, 0 ); - if(!inlen) - return; + sha1_transform(ctx, buf); + ctx->nblocks++; + buf += SHA1_BLOCK_SIZE; + len -= SHA1_BLOCK_SIZE; } - 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++; + /* 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; @@ -230,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) + /* append the 64 bit count */ + 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); @@ -264,12 +266,12 @@ sha1_final(struct sha1_context *hd) X(4); #undef X - return hd->buf; + return ctx->buf; } /* - * SHA1-HMAC + * SHA1-HMAC */ /* @@ -295,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 */ @@ -324,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); @@ -337,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 f089e147..c019bb49 100644 --- a/lib/sha1.h +++ b/lib/sha1.h @@ -15,72 +15,72 @@ #ifndef _BIRD_SHA1_H_ #define _BIRD_SHA1_H_ -#include "sysdep/config.h" +#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. + * 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 - * <>. + * 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 61da0f39..440245d5 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -10,16 +10,11 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#include -#include -#include -#include - #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) @@ -34,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 @@ -53,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. */ @@ -76,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) */ @@ -118,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, @@ -154,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; @@ -217,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 @@ -240,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 (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) + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA256_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; + + if (ctx->count < SHA256_BLOCK_SIZE) return; + + /* Process data from internal buffer */ + sha256_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; } - if (in_len >= blocksize) + if (!len) + return; + + /* Process data from input buffer */ + while (len >= SHA256_BLOCK_SIZE) { - 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; + sha256_transform(ctx, buf); + ctx->nblocks++; + buf += SHA256_BLOCK_SIZE; + len -= SHA256_BLOCK_SIZE; } - 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; @@ -314,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); @@ -350,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 @@ -372,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 */ @@ -394,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); @@ -412,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 @@ -434,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 */ @@ -456,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 148bf7f5..381200a9 100644 --- a/lib/sha256.h +++ b/lib/sha256.h @@ -13,7 +13,8 @@ #ifndef _BIRD_SHA256_H_ #define _BIRD_SHA256_H_ -#include "sysdep/config.h" +#include "nest/bird.h" + #define SHA224_SIZE 28 #define SHA224_HEX_SIZE 57 @@ -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 60c3ffb1..37e660f7 100644 --- a/lib/sha512.c +++ b/lib/sha512.c @@ -10,64 +10,42 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#include -#include -#include -#include -#include - -#include "lib/sha256.h" -#include "lib/sha256.c" /* REMOVE ME */ #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; + 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); - 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->nblocks = 0; + ctx->count = 0; } void sha384_init(struct sha384_context *ctx) { - struct sha512_state *hd = &ctx->state; + 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); - 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->nblocks = 0; + ctx->count = 0; } static inline u64 @@ -89,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; + 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++ ) + for (t = 0; t < 16; t++) w[t] = get_u64(data + t * 8); #define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7)) @@ -182,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; @@ -195,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; @@ -300,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; @@ -312,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; @@ -398,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; - - do + if (ctx->count) { - burn = sha512_transform_block(&ctx->state, data) + 3 * sizeof(void*); - data += 128; - } - while(--nblks); + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA512_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; - return burn; + if (ctx->count < SHA512_BLOCK_SIZE) + return; + + /* Process data from internal buffer */ + sha512_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; + } + + if (!len) + return; + + /* Process data from input buffer */ + while (len >= SHA512_BLOCK_SIZE) + { + sha512_transform(ctx, buf); + ctx->nblocks++; + buf += SHA512_BLOCK_SIZE; + len -= SHA512_BLOCK_SIZE; + } + + /* 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; @@ -460,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); + /* append the 128 bit count */ + 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 @@ -520,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 */ @@ -542,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); @@ -560,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 @@ -582,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 */ @@ -604,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 06212b0e..1614a3ac 100644 --- a/lib/sha512.h +++ b/lib/sha512.h @@ -13,8 +13,8 @@ #ifndef _BIRD_SHA512_H_ #define _BIRD_SHA512_H_ -#include "sysdep/config.h" -#include "lib/sha256.h" +#include "nest/bird.h" + #define SHA384_SIZE 48 #define SHA384_HEX_SIZE 97 @@ -24,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); @@ -71,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_ */ 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/lib/string.h b/lib/string.h index d23c1d94..33bb73ea 100644 --- a/lib/string.h +++ b/lib/string.h @@ -24,6 +24,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/nest/a-path.c b/nest/a-path.c index 808ac354..e6d8bfc3 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 (paccfrom <= 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/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); } diff --git a/nest/route.h b/nest/route.h index d0f8c688..9368808f 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 @@ -217,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/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 10ce400a..57c8b8e0 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/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); 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/proto/bgp/bgp.c b/proto/bgp/bgp.c index f549b0ed..94c8e5c2 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; @@ -666,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 @@ -696,6 +702,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; } @@ -813,7 +820,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 +836,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", 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; 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) 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); } /** diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index bfabceb4..faa33664 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -109,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 md5_context ctxt; - md5_init(&ctxt); - md5_update(&ctxt, (char *) pkt, plen); - md5_update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); - memcpy((byte *) tail, md5_final(&ctxt), MD5_SIZE); + 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: @@ -175,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]; + 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); - strncpy(passwd, pass->password, OSPF_AUTH_CRYPT_SIZE); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, plen + OSPF_AUTH_CRYPT_SIZE); + char *computed = md5_final(&ctx); - struct md5_context ctxt; - md5_init(&ctxt); - md5_update(&ctxt, (char *) pkt, plen); - md5_update(&ctxt, passwd, OSPF_AUTH_CRYPT_SIZE); - memcpy(md5sum, md5_final(&ctxt), MD5_SIZE); - - if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) + if (memcmp(received, computed, OSPF_AUTH_CRYPT_SIZE)) DROP("wrong MD5 digest", pass->id); if (n) @@ -224,7 +222,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/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) 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 70fe14e4..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 md5_context ctxt; - char md5sum_packet[16]; - char *md5sum_computed; - 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); - - md5_init(&ctxt); - md5_update(&ctxt, (char *) packet, ntohs(block->packetlen) + sizeof(struct rip_block_auth) ); - md5sum_computed = md5_final(&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 md5_context 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); - md5_init(&ctxt); - md5_update(&ctxt, (char *) packet, PACKETLEN(num) + sizeof(struct rip_md5_tail)); - memcpy(tail->md5, md5_final(&ctxt), MD5_SIZE); - 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..083d2e91 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->rip2 = RIP_IS_V2; + RIP_CFG->infinity = RIP_DEFAULT_INFINITY; + + 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_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_proto_opts: + /* empty */ + | rip_proto_opts rip_proto_item ';' ; -rip_auth: - PLAINTEXT { $$=AT_PLAINTEXT; } - | MD5 { $$=AT_MD5; } - | NONE { $$=AT_NONE; } - ; +rip_proto: + rip_proto_start proto_name '{' rip_proto_opts '}'; -rip_mode: - BROADCAST { $$=IM_BROADCAST; } - | MULTICAST { $$=0; } - | QUIET { $$=IM_QUIET; } - | NOLISTEN { $$=IM_NOLISTEN; } - | VERSION1 { $$=IM_VERSION1 | IM_BROADCAST; } - ; +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("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; } + | 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..9f10fd67 --- /dev/null +++ b/proto/rip/packets.c @@ -0,0 +1,770 @@ +/* + * 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 md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, *plen); + memcpy(tail->auth_data, md5_final(&ctx), RIP_MD5_LENGTH); + 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]; + memcpy(received, tail->auth_data, RIP_MD5_LENGTH); + strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); + + 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); + + *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) * - * RIP is a pretty simple protocol, so about a half of its code is interface - * with the core. + * 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. * - * 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(). + * 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. * - * 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 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). * - * We are not going to honor requests for sending part of - * routing table. That would need to turn split horizon off etc. + * 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(). * - * 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. + * 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. + * + * 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. + * + * 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. + * + * 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. + * + * 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 +static struct rip_rte * +rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src) +{ + struct rip_rte *rt = sl_alloc(p->rte_slab); + + memcpy(rt, src, sizeof(struct rip_rte)); + rt->next = *rp; + *rp = rt; + + rip_lock_neighbor(rt->from); + + return rt; +} + +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 * - * 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 ) -{ - 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 ); - - 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 - - ipa_hton( b->network ); - - return pos+1; -} - -/* - * rip_tx - send one rip packet to the 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; + struct rip_rte *rt = en->routes; - DBG( "Sending to %I\n", s->daddr ); - do { + /* Find first valid rte */ + while (rt && !rip_valid_rte(rt)) + rt = rt->next; - if (c->done) - goto done; + if (rt) + { + /* Update */ + net *n = net_get(p->p.table, en->n.prefix, en->n.pxlen); - DBG( "Preparing packet to send: " ); + rta a0 = { + .src = p->p.main_source, + .source = RTS_RIP, + .scope = SCOPE_UNIVERSE, + .cast = RTC_UNICAST + }; - packet->heading.command = RIPCMD_RESPONSE; -#ifndef IPV6 - packet->heading.version = RIP_V2; -#else - packet->heading.version = RIP_NG; -#endif - packet->heading.unused = 0; + u8 rt_metric = rt->metric; + u16 rt_tag = rt->tag; + struct rip_rte *rt2 = rt->next; - 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; + /* Find second valid rte */ + while (rt2 && !rip_valid_rte(rt2)) + rt2 = rt2->next; - 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; - } + if (p->ecmp && rt2) + { + /* ECMP route */ + struct mpnh *nhs = NULL; + struct mpnh **nhp = &nhs; + int num = 0; + + 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; } - } FIB_ITERATE_END(z); - c->done = 1; - break_loop: - - packetlen = rip_outgoing_authentication(p, (void *) &packet->block[0], packet, i); - - DBG( ", sending %d blocks, ", i ); - if (nullupdate) { - DBG( "not sending NULL update\n" ); - c->done = 1; - goto done; + 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 ); + { + /* Unipath route */ + a0.dest = RTD_ROUTER; + a0.gw = rt->next_hop; + a0.iface = rt->from->nbr->iface; + a0.from = rt->from->nbr->addr; + } - DBG( "it wants more\n" ); - - } while (i>0); - - if (i<0) rip_tx_err( s, i ); - DBG( "blocked\n" ); - return; + rta *a = rta_lookup(&a0); + rte *e = rte_get_temp(a); -done: - DBG( "Looks like I'm" ); - c->rif->busy = NULL; - rem_node(NODE c); - mb_free(c); - DBG( " done\n" ); - 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); + } + else + { + /* Withdraw */ + net *n = net_find(p->p.table, en->n.prefix, en->n.pxlen); + rte_update(&p->p, n, NULL); + } } -/* - * 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. +/** + * 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_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; + } + + /* 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); +} + +/** + * 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 + * + * 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; + + if (!en) + return; + + /* Find the old route */ + for (rp = &en->routes; rt = *rp; rp = &rt->next) + if (rt->from == from) + break; + + 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); +} + +/* + * rip_rt_notify - core tells us about new route, so store + * it into our data structures. */ static void -rip_sendto( struct proto *p, ip_addr daddr, int dport, struct rip_interface *rif ) +rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, struct rte *new, + struct rte *old UNUSED, struct ea_list *attrs) { - struct iface *iface = rif->iface; - struct rip_connection *c; - static int num = 0; + struct rip_proto *p = (struct rip_proto *) P; + struct rip_entry *en; + int old_metric; - if (rif->busy) { - log (L_WARN "%s: Interface %s is much too slow, dropping request", p->name, iface->name); + 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); + + 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; + } + + /* Activate triggered updates */ + if (en->metric != old_metric) + { + en->changed = now; + rip_trigger_update(p); + } +} + + +/* + * 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 +rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n) +{ + neighbor *nbr = n->nbr; + + TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->iface->name); + + 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); +} + +static inline void +rip_lock_neighbor(struct rip_neighbor *n) +{ + n->uc++; +} + +static inline void +rip_unlock_neighbor(struct rip_neighbor *n) +{ + 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; + + 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; + + 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); + } + + if (!use_bfd && n->bfd_req) + { + rfree(n->bfd_req); + n->bfd_req = NULL; + } +} + + +/* + * RIP interfaces + */ + +static void +rip_iface_start(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + + TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name); + + 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; + + if (!ifa->cf->passive) + rip_send_request(ifa->rip, ifa); +} + +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); + + rip_reset_tx_session(p, ifa); + + WALK_LIST_FIRST(n, ifa->neigh_list) + rip_remove_neighbor(p, n); + + tm_stop(ifa->timer); + ifa->up = 0; +} + +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_iface_update_state(struct rip_iface *ifa) +{ + int up = ifa->sk && rip_iface_link_up(ifa); + + if (up == ifa->up) + return; + + if (up) + rip_iface_start(ifa); + else + rip_iface_stop(ifa); +} + +static void +rip_iface_update_buffers(struct rip_iface *ifa) +{ + if (!ifa->sk) + return; + + uint rbsize = ifa->cf->rx_buffer ?: ifa->iface->mtu; + uint tbsize = ifa->cf->tx_length ?: ifa->iface->mtu; + rbsize = MAX(rbsize, tbsize); + + sk_set_rbsize(ifa->sk, rbsize); + sk_set_tbsize(ifa->sk, tbsize); + + uint headers = (rip_is_v2(ifa->rip) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH) + UDP_HEADER_LENGTH; + ifa->tx_plen = tbsize - headers; + + if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) + ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH; +} + +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; + + if (!rip_open_socket(ifa)) + { + log(L_ERR "%s: Cannot open socket for %s", p->p.name, ifa->iface->name); return; } - 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); + rip_iface_update_buffers(ifa); + rip_iface_update_state(ifa); } -static struct rip_interface* -find_interface(struct proto *p, struct iface *what) -{ - struct rip_interface *i; - WALK_LIST (i, P->interfaces) - if (i->iface == what) - return i; +static struct rip_iface * +rip_find_iface(struct rip_proto *p, struct iface *what) +{ + struct rip_iface *ifa; + + WALK_LIST(ifa, p->iface_list) + if (ifa->iface == what) + return ifa; + return NULL; } -/* - * Input processing - * - * This part is responsible for any updates that come from network - */ - -static int rip_rte_better(struct rte *new, struct rte *old); - static void -rip_rte_update_if_better(rtable *tab, net *net, struct proto *p, rte *new) +rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config *ic) { - rte *old; + struct rip_iface *ifa; - 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); + 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); } -/* - * 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. - */ static void -advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme, struct iface *iface ) +rip_remove_iface(struct rip_proto *p, struct rip_iface *ifa) { - rta *a, A; - rte *r; - net *n; - neighbor *neighbor; - struct rip_interface *rif; - int pxlen; + rip_iface_stop(ifa); - 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; + TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name); - /* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */ + rem_node(NODE ifa); - 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; - } + rfree(ifa->sk); + rfree(ifa->lock); + rfree(ifa->timer); - A.iface = neighbor->iface; - if (!(rif = neighbor->data)) { - rif = neighbor->data = find_interface(p, A.iface); - } - 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; - } - 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" ); + mb_free(ifa); } -/* - * process_block - do some basic check and pass block to advertise_entry - */ -static void -process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme, struct iface *iface ) -{ - 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; - } - - advertise_entry( p, block, whotoldme, iface ); -} - -#define BAD( x ) { log( L_REMOTE "%s: " x, p->name ); return 1; } - -/* - * 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 ) +rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_iface_config *new) { - int i; - int authenticated = 0; - neighbor *neighbor; + struct rip_iface_config *old = ifa->cf; - switch( packet->heading.version ) { - case RIP_V1: DBG( "Rip1: " ); break; - case RIP_V2: DBG( "Rip2: " ); break; - default: BAD( "Unknown version" ); - } + /* 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; - 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" ); + TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name); - 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; - } + ifa->cf = new; - 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; - } + if (ifa->next_regular > (now + new->update_time)) + ifa->next_regular = now + (random() % new->update_time) + 1; - 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 ((new->tx_length != old->tx_length) || (new->rx_buffer != old->rx_buffer)) + rip_iface_update_buffers(ifa); - return 0; -} + if (new->check_link != old->check_link) + rip_iface_update_state(ifa); -/* - * rip_rx - Receive hook: do basic checks and pass packet to rip_process_packet - */ -static int -rip_rx(sock *s, int size) -{ - struct rip_interface *i = s->data; - struct proto *p = i->proto; - struct iface *iface = NULL; - int num; + if (new->bfd != old->bfd) + rip_iface_update_bfd(ifa); - /* In non-listening mode, just ignore packet */ - if (i->mode & IM_NOLISTEN) - return 1; + if (ifa->up) + rip_iface_kick_timer(ifa); -#ifdef IPV6 - if (! i->iface || s->lifindex != i->iface->index) - return 1; - - iface = i->iface; -#endif - - 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; - } - - - 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" ); - - if (ipa_equal(i->iface->addr->ip, s->faddr)) { - DBG("My own packet\n"); - return 1; - } - - rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport, iface ); return 1; } -/* - * Interface to BIRD core - */ - static void -rip_dump_entry( struct rip_entry *e ) +rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf) { - 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" ); + 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); + + 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); + } } -/** - * 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. +static void +rip_if_notify(struct proto *P, unsigned flags, struct iface *iface) +{ + 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 */ +/** + * rip_timer - RIP main timer hook + * @t: timer + * + * 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 void rip_timer(timer *t) { - struct proto *p = t->data; - struct fib_node *e, *et; + 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; - 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 ); + TRACE(D_EVENTS, "Main timer fired"); - CHK_MAGIC; + FIB_ITERATE_INIT(&fit, &p->rtable); - DBG( "Garbage: (%p)", rte ); rte_dump( rte ); - - 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 (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); - } - } - - DBG( "RIP: Broadcasting routing tables\n" ); + loop: + FIB_ITERATE_START(&p->rtable, &fit, node) { - struct rip_interface *rif; + struct rip_entry *en = (struct rip_entry *) node; + struct rip_rte *rt, **rp; + int changed = 0; - 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; + /* Checking received routes for timeout and for dead neighbors */ + for (rp = &en->routes; rt = *rp; /* rp = &rt->next */) + { + if (!rip_valid_rte(rt) || (rt->expires <= now)) + { + rip_remove_rte(p, rp); + changed = 1; + continue; + } - 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; - - rip_sendto( p, IPA_NONE, 0, rif ); + next = MIN(next, rt->expires); + rp = &rt->next; + } + + /* 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; + } + + /* Checking stale entries for garbage collection timeout */ + if (en->valid == RIP_ENTRY_STALE) + { + expires = en->changed + cf->max_garbage_time; + + if (expires <= now) + { + // TRACE(D_EVENTS, "entry is too old: %I/%d", en->n.prefix, en->n.pxlen); + en->valid = 0; + } + 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; } - P->tx_count++; - P->rnd_count--; } + FIB_ITERATE_END(node); - DBG( "RIP: tick tock done\n" ); + 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) + { + expires = n->last_seen + n->ifa->cf->timeout_time; + + if (expires <= now) + rip_remove_neighbor(p, n); + else + next = MIN(next, expires); + } + + tm_start(p->timer, MAX(next - now, 1)); } -/* - * rip_start - initialize instance of rip - */ -static int -rip_start(struct proto *p) +static inline void +rip_kick_timer(struct rip_proto *p) { - 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; -} - -static void -rip_dump(struct proto *p) -{ - int i; - node *w; - struct rip_interface *rif; - - 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 ); - } -} - -static void -rip_get_route_info(rte *rte, byte *buf, ea_list *attrs) -{ - eattr *metric = ea_find(attrs, EA_RIP_METRIC); - eattr *tag = ea_find(attrs, EA_RIP_TAG); - - buf += bsprintf(buf, " (%d/%d)", rte->pref, metric ? metric->u.data : 0); - if (tag && tag->u.data) - bsprintf(buf, " t%04x", tag->u.data); -} - -static void -kill_iface(struct rip_interface *i) -{ - DBG( "RIP: Interface %s disappeared\n", i->iface->name); - rfree(i->sock); - mb_free(i); + if (p->timer->expires > (now + 1)) + tm_start(p->timer, 1); /* Or 100 ms */ } /** - * 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_iface_timer - RIP interface timer hook + * @t: timer * - * Create an interface structure and start listening on the interface. + * 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 struct rip_interface * -new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt ) -{ - 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) - { - 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 (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; - } - } - - 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 { - - if (sk_open(rif->sock) < 0) - goto err; - - if (rif->multicast) - { - if (sk_setup_multicast(rif->sock) < 0) - goto err; - if (sk_join_group(rif->sock, rif->sock->daddr) < 0) - goto err; - } - else - { - if (sk_setup_broadcast(rif->sock) < 0) - goto err; - } - } - - 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; - - 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; - } - /* On dummy, we just return non-working socket, so that user gets error every time anyone requests table */ - return rif; -} - static void -rip_real_if_add(struct object_lock *lock) +rip_iface_timer(timer *t) { - 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); + struct rip_iface *ifa = t->data; + struct rip_proto *p = ifa->rip; + bird_clock_t period = ifa->cf->update_time; - 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); } -} - -static void -rip_if_notify(struct proto *p, unsigned c, struct iface *iface) -{ - DBG( "RIP: if notify\n" ); - if (iface->flags & IF_IGNORE) + if (ifa->cf->passive) 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 */ + TRACE(D_EVENTS, "Interface timer fired for %s", ifa->iface->name); - 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); + 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); } + + 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 inline void +rip_iface_kick_timer(struct rip_iface *ifa) +{ + if (ifa->timer->expires > (now + 1)) + tm_start(ifa->timer, 1); /* Or 100 ms */ +} + +static void +rip_trigger_update(struct rip_proto *p) +{ + if (p->triggered) + return; + + 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 */ + /* Prepare attributes with initial values */ + if ((*rt)->attrs->source != RTS_RIP) + *attrs = rip_prepare_attrs(pool, *attrs, 1, 0); + + return 0; +} + +static int +rip_reload_routes(struct proto *P) +{ + struct rip_proto *p = (struct rip_proto *) P; + + if (p->rt_reload) return 1; - if ((*rt)->attrs->source != RTS_RIP) { - struct ea_list *new = rip_gen_attrs(pool, 1, 0); - new->next = *attrs; - *attrs = new; - } - return 0; + 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 int -rip_rte_better(struct rte *new, struct rte *old) -{ - struct proto *p = new->attrs->src->proto; - - if (ipa_equal(old->attrs->from, new->attrs->from)) - return 1; - - if (old->u.rip.metric < new->u.rip.metric) - return 0; - - if (old->u.rip.metric > new->u.rip.metric) - return 1; - - 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; - - return 0; -} - -/* - * 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) -{ - struct proto *p = rte->attrs->src->proto; - CHK_MAGIC; - DBG( "rip_rte_insert: %p\n", rte ); - add_head( &P->garbage, &rte->u.rip.garbage ); -} - -/* - * 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 ); -} - static struct proto * rip_init(struct proto_config *cfg) { - struct proto *p = proto_new(cfg, sizeof(struct rip_proto)); + 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; + 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; + return P; } -void -rip_init_config(struct rip_proto_config *c) +static int +rip_start(struct proto *P) { - 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; + struct rip_proto *p = (void *) P; + struct rip_config *cf = (void *) (P->cf); + + 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); + + p->ecmp = cf->ecmp; + p->infinity = cf->infinity; + p->triggered = 0; + + p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; + p->log_rte_tbf = (struct tbf){ .rate = 4, .burst = 20 }; + + tm_start(p->timer, MIN(cf->min_timeout_time, cf->max_garbage_time)); + + return PS_UP; +} + +static int +rip_reconfigure(struct proto *P, struct proto_config *c) +{ + struct rip_proto *p = (void *) P; + struct rip_config *new = (void *) c; + // struct rip_config *old = (void *) (P->cf); + + if (new->infinity != p->infinity) + return 0; + + 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; +} + +static void +rip_get_route_info(rte *rte, byte *buf, ea_list *attrs) +{ + 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 968e7372..f245e612 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -1,188 +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/iface.h" +#include "nest/bfd.h" +#include "lib/lists.h" +#include "lib/resource.h" #include "lib/socket.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 */ +#include "lib/string.h" +#include "lib/timer.h" -#define RIP_V1 1 -#define RIP_V2 2 -#define RIP_NG 1 /* A new version numbering */ - -#ifndef IPV6 -#define RIP_PORT 520 /* RIP for IPv4 */ +#ifdef IPV6 +#define RIP_IS_V2 0 #else -#define RIP_PORT 521 /* RIPng */ +#define RIP_IS_V2 1 #endif -struct rip_connection { - node n; - - 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_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; + +#define RIP_PORT 520 /* RIP for IPv4 */ +#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_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 + + +struct rip_config +{ + struct proto_config c; + list patt_list; /* List of iface configs (struct rip_iface_config) */ + + 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; -}; -#else -struct rip_block { /* IPv6 version!, 20 bytes, too */ - ip_addr network; - u16 tag; - u8 pxlen; - u8 metric; -}; -#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_md5_tail { /* 20 bytes */ - u16 mustbeFFFF; - u16 mustbe0001; - char md5[16]; -}; - -struct rip_entry { - struct fib_node n; - - ip_addr whotoldme; - ip_addr nexthop; - int metric; - u16 tag; - - bird_clock_t updated, changed; - int flags; -}; - -struct rip_packet { - struct rip_packet_heading heading; - struct rip_block block[PACKET_MAX]; -}; - -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_patt { +struct rip_iface_config +{ 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 + 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; - int ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ + u32 update_time; /* Periodic update interval */ + u32 timeout_time; /* Route expiration timeout */ + u32 garbage_time; /* Unreachable entry GC timeout */ + list *passwords; /* Passwords for authentication */ }; -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 */ +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 */ - int infinity; /* User configurable data; must be comparable with memcmp */ - int port; - int period; - int garbage_time; - int timeout_time; + 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 */ - 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 + struct tbf log_pkt_tbf; /* TBF for packet messages */ + struct tbf log_rte_tbf; /* TBF for RTE messages */ }; -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; +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_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 fib_node n; + struct rip_rte *routes; /* List of incoming routes */ + + 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 changed; /* Last time when the outgoing route metric changed */ +}; + +struct rip_rte +{ + struct rip_rte *next; + + 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 */ +}; + + +#define RIP_AUTH_NONE 0 +#define RIP_AUTH_PLAIN 2 +#define RIP_AUTH_CRYPTO 3 + +#define RIP_IM_MULTICAST 1 +#define RIP_IM_BROADCAST 2 + +#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 */ + +#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) + +/* +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); + + #endif - int tx_count; /* Do one regular update once in a while */ - int rnd_count; /* Randomize sending time */ -}; - -#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 - - -void rip_init_config(struct rip_proto_config *c); - -/* 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 ); 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 f2f60100..1ffdff07 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) -#else - || !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr) - || (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr)) -#endif - ) + + switch (i->ifa_family) { - log(L_ERR "nl_parse_addr: Malformed message received"); +#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 + 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 "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]); @@ -1007,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]) @@ -1116,12 +1212,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/io.c b/sysdep/unix/io.c index 8a431481..2faf5d5d 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -43,12 +44,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 @@ -1024,7 +1025,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) @@ -1080,7 +1080,6 @@ sk_free(resource *r) if (s == stored_sock) stored_sock = sk_next(s); rem_node(&s->n); - sock_recalc_fdsets_p = 1; } } @@ -1278,7 +1277,6 @@ static void sk_insert(sock *s) { add_tail(&sock_list, &s->n); - sock_recalc_fdsets_p = 1; } static void @@ -1330,18 +1328,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 ? */ @@ -1418,9 +1404,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; @@ -1698,19 +1681,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; @@ -1779,7 +1755,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) { @@ -1798,6 +1774,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); @@ -2065,62 +2046,63 @@ 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, pout; 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); + timers: update_times(); tout = tm_first_shot(); if (tout <= now) { tm_shot(); - continue; + goto timers; } - 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) @@ -2145,18 +2127,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); + pout = poll(pfd, nfds, poll_tout); watchdog_start(); - if (hi < 0) + if (pout < 0) { if (errno == EINTR || errno == EAGAIN) continue; - die("select: %m"); + die("poll: %m"); } - if (hi) + if (pout) { /* guaranteed to be non-empty */ current_sock = SKIP_BACK(sock, n, HEAD(sock_list)); @@ -2164,23 +2146,29 @@ 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->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) do { 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; } while (e && s->rx_hook && steps); steps = MAX_STEPS; - if (FD_ISSET(s->fd, &wr)) + if (pfd[s->index].revents & POLLOUT) do { steps--; @@ -2207,13 +2195,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->fast_rx && (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, pfd[s->index].revents); if (s != current_sock) goto next2; } diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 49bf9519..f5dee877 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; @@ -417,46 +417,58 @@ again: 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 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); 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 } diff --git a/sysdep/unix/main_helper.c b/sysdep/unix/main_helper.c index 42a9e6cb..eb32849b 100644 --- a/sysdep/unix/main_helper.c +++ b/sysdep/unix/main_helper.c @@ -454,6 +454,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); @@ -470,6 +471,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);