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.
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);