Commit 8465dccb authored by Ondřej Zajíček's avatar Ondřej Zajíček

Major RIP redesign

The new RIP implementation fixes plenty of old bugs and also adds support
for many new features: ECMP support, link state support, BFD support,
configurable split horizon and more. Most options are now per-interface.
parent c7b99a93
......@@ -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))
......
This diff is collapsed.
......@@ -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
......
......@@ -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;
......
......@@ -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
......
......@@ -51,3 +51,18 @@ password_find_by_id(list *l, int id)
return NULL;
}
struct password_item *
password_find_by_value(list *l, char *pass, uint size)
{
struct password_item *pi;
if (!l)
return NULL;
WALK_LIST(pi, *l)
if (password_verify(pi, pass, size) && (pi->accfrom <= now_real) && (now_real < pi->accto))
return pi;
return NULL;
}
......@@ -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)
{
......
......@@ -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
......
......@@ -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
/**
......
......@@ -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;
......
......@@ -223,7 +223,7 @@ ospf_rx_hook(sock *sk, int len)
return 1;
DBG("OSPF: RX hook called (iface %s, src %I, dst %I)\n",
sk->ifname, sk->faddr, sk->laddr);
sk->iface->name, sk->faddr, sk->laddr);
/* Initially, the packet is associated with the 'master' iface */
struct ospf_iface *ifa = sk->data;
......
S rip.c
S auth.c
S packets.c
source=rip.c auth.c
source=rip.c packets.c
root-rel=../../
dir-name=proto/rip
......
/*
* Rest in pieces - RIP protocol
*
* Copyright (c) 1999 Pavel Machek <pavel@ucw.cz>
* Copyright (c) 2004 Ondrej Filip <feela@network.cz>
*
* Bug fixes by Eric Leblond <eleblond@init-sys.com>, April 2003
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "lib/socket.h"
#include "lib/resource.h"
#include "lib/lists.h"
#include "lib/timer.h"
#include "lib/md5.h"
#include "lib/string.h"
#include "rip.h"
#define P ((struct rip_proto *) p)
#define P_CF ((struct rip_proto_config *)p->cf)
#define PACKETLEN(num) (num * sizeof(struct rip_block) + sizeof(struct rip_packet_heading))
/*
* rip_incoming_authentication - check authentication of incomming packet and return 1 if there's problem.
*/
int
rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num, ip_addr whotoldme )
{
DBG( "Incoming authentication: " );
switch (ntohs(block->authtype)) { /* Authentication type */
case AT_PLAINTEXT:
{
struct password_item *passwd = password_find(P_CF->passwords, 1);
DBG( "Plaintext passwd" );
if (!passwd) {
log( L_AUTH "No passwords set and password authentication came" );
return 1;
}
if (strncmp( (char *) (&block->packetlen), passwd->password, 16)) {
log( L_AUTH "Passwd authentication failed!" );
DBG( "Expected %s, got %.16s\n", passwd->password, &block->packetlen );
return 1;
}
}
break;
case AT_MD5:
DBG( "md5 password" );
{
struct password_item *pass = NULL, *ptmp;
struct rip_md5_tail *tail;
struct MD5Context ctxt;
char md5sum_packet[16];
char md5sum_computed[16];
struct neighbor *neigh = neigh_find(p, &whotoldme, 0);
list *l = P_CF->passwords;
if (ntohs(block->packetlen) != PACKETLEN(num) - sizeof(struct rip_md5_tail) ) {
log( L_ERR "Packet length in MD5 does not match computed value" );
return 1;
}
tail = (struct rip_md5_tail *) ((char *) packet + (ntohs(block->packetlen) ));
if ((tail->mustbeFFFF != 0xffff) || (tail->mustbe0001 != 0x0100)) {
log( L_ERR "MD5 tail signature is not there" );
return 1;
}
WALK_LIST(ptmp, *l)
{
if (block->keyid != ptmp->id) continue;
if ((ptmp->genfrom > now_real) || (ptmp->gento < now_real)) continue;
pass = ptmp;
break;
}
if(!pass) return 1;
if (!neigh) {
log( L_AUTH "Non-neighbour MD5 checksummed packet?" );
} else {
if (neigh->aux > block->seq) {
log( L_AUTH "MD5 protected packet with lower numbers" );
return 1;
}
neigh->aux = block->seq;
}
memcpy(md5sum_packet, tail->md5, 16);
strncpy(tail->md5, pass->password, 16);
MD5Init(&ctxt);
MD5Update(&ctxt, (char *) packet, ntohs(block->packetlen) + sizeof(struct rip_block_auth) );
MD5Final(md5sum_computed, &ctxt);
if (memcmp(md5sum_packet, md5sum_computed, 16))
return 1;
}
}
return 0;
}
/*
* rip_outgoing_authentication - append authentication information to the packet.
* %num: number of rip_blocks already in packets. This function returns size of packet to send.
*/
int
rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num )
{
struct password_item *passwd = password_find(P_CF->passwords, 1);
if (!P_CF->authtype)
return PACKETLEN(num);
DBG( "Outgoing authentication: " );
if (!passwd) {
log( L_ERR "No suitable password found for authentication" );
return PACKETLEN(num);
}
block->authtype = htons(P_CF->authtype);
block->mustbeFFFF = 0xffff;
switch (P_CF->authtype) {
case AT_PLAINTEXT:
strncpy( (char *) (&block->packetlen), passwd->password, 16);
return PACKETLEN(num);
case AT_MD5:
{
struct rip_md5_tail *tail;
struct MD5Context ctxt;
static u32 sequence = 0;
if (num > PACKET_MD5_MAX)
bug( "We can not add MD5 authentication to this long packet" );
/* need to preset the sequence number to a sane value */
if (!sequence)
sequence = (u32) time(NULL);
block->keyid = passwd->id;
block->authlen = sizeof(struct rip_block_auth);
block->seq = sequence++;
block->zero0 = 0;
block->zero1 = 0;
block->packetlen = htons(PACKETLEN(num));
tail = (struct rip_md5_tail *) ((char *) packet + PACKETLEN(num) );
tail->mustbeFFFF = 0xffff;
tail->mustbe0001 = 0x0100;
strncpy(tail->md5, passwd->password, 16);
MD5Init(&ctxt);
MD5Update(&ctxt, (char *) packet, PACKETLEN(num) + sizeof(struct rip_md5_tail));
MD5Final(tail->md5, &ctxt);
return PACKETLEN(num) + block->authlen;
}
default:
bug( "Unknown authtype in outgoing authentication?" );
}
}
/*
* BIRD -- RIP Configuration
*
* (c) 1998--1999 Pavel Machek <pavel@ucw.cz>
* (c) 2004--2013 Ondrej Filip <feela@network.cz>
* (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org>
* (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 <i> rip_mode rip_auth
%type <i> rip_auth
CF_GRAMMAR
CF_ADDTO(proto, rip_cfg '}' { RIP_CFG->passwords = get_passwords(); } )
CF_ADDTO(proto, rip_proto)
rip_cfg_start: proto_start RIP {
this_proto = proto_config_new(&proto_rip, $1);
rip_init_config(RIP_CFG);
}
;
rip_proto_start: proto_start RIP
{
this_proto = proto_config_new(&proto_rip, $1);
init_list(&RIP_CFG->patt_list);
rip_cfg:
rip_cfg_start proto_name '{'
| rip_cfg proto_item ';'
| rip_cfg INFINITY expr ';' { RIP_CFG->infinity = $3; }
| rip_cfg PORT expr ';' { RIP_CFG->port = $3; }
| rip_cfg PERIOD expr ';' { RIP_CFG->period = $3; }
| rip_cfg GARBAGE TIME expr ';' { RIP_CFG->garbage_time = $4; }
| rip_cfg TIMEOUT TIME expr ';' { RIP_CFG->timeout_time = $4; }
| rip_cfg AUTHENTICATION rip_auth ';' {RIP_CFG->authtype = $3; }
| rip_cfg password_list ';'
| rip_cfg HONOR ALWAYS ';' { RIP_CFG->honor = HO_ALWAYS; }
| rip_cfg HONOR NEIGHBOR ';' { RIP_CFG->honor = HO_NEIGHBOR; }
| rip_cfg HONOR NEVER ';' { RIP_CFG->honor = HO_NEVER; }
| rip_cfg INTERFACE rip_iface ';'
;
RIP_CFG->rip2 = RIP_IS_V2;
RIP_CFG->infinity = RIP_DEFAULT_INFINITY;
rip_auth:
PLAINTEXT { $$=AT_PLAINTEXT; }
| MD5 { $$=AT_MD5; }
| NONE { $$=AT_NONE; }
;
RIP_CFG->min_timeout_time = 60;
RIP_CFG->max_garbage_time = 60;
};
rip_proto_item:
proto_item
| ECMP bool { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; }
| ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); }
| INFINITY expr { RIP_CFG->infinity = $2; }
| INTERFACE rip_iface
;
rip_mode:
BROADCAST { $$=IM_BROADCAST; }
| MULTICAST { $$=0; }
| QUIET { $$=IM_QUIET; }
| NOLISTEN { $$=IM_NOLISTEN; }
| VERSION1 { $$=IM_VERSION1 | IM_BROADCAST; }
rip_proto_opts:
/* empty */
| rip_proto_opts rip_proto_item ';'
;
rip_proto:
rip_proto_start proto_name '{' rip_proto_opts '}';
rip_iface_start:
{
this_ipatt = cfg_allocz(sizeof(struct rip_iface_config));
add_tail(&RIP_CFG->patt_list, NODE this_ipatt);
init_list(&this_ipatt->ipn_list);
reset_passwords();
RIP_IFACE->metric = 1;
RIP_IFACE->port = rip_cfg_is_v2() ? RIP_PORT : RIP_NG_PORT;
RIP_IFACE->version = rip_cfg_is_v2() ? RIP_V2 : RIP_V1;
RIP_IFACE->split_horizon = 1;
RIP_IFACE->poison_reverse = 1;
RIP_IFACE->check_zero = 1;
RIP_IFACE->ttl_security = rip_cfg_is_v2() ? 0 : 1;
RIP_IFACE->rx_buffer = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0;
RIP_IFACE->tx_length = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0;
RIP_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
RIP_IFACE->tx_priority = sk_priority_control;
RIP_IFACE->update_time = RIP_DEFAULT_UPDATE_TIME;
RIP_IFACE->timeout_time = RIP_DEFAULT_TIMEOUT_TIME;
RIP_IFACE->garbage_time = RIP_DEFAULT_GARBAGE_TIME;
};
rip_iface_finish:
{
RIP_IFACE->passwords = get_passwords();
if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords)
log(L_WARN "Authentication and password options should be used together");
/* Default mode is broadcast for RIPv1, multicast for RIPv2 and RIPng */
if (!RIP_IFACE->mode)
RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ?
RIP_IM_BROADCAST : RIP_IM_MULTICAST;
RIP_CFG->min_timeout_time = MIN_(RIP_CFG->min_timeout_time, RIP_IFACE->timeout_time);
RIP_CFG->max_garbage_time = MAX_(RIP_CFG->max_garbage_time, RIP_IFACE->garbage_time);
};
rip_iface_item:
| METRIC expr { RIP_IPATT->metric = $2; }
| MODE rip_mode { RIP_IPATT->mode |= $2; }
| TX tos { RIP_IPATT->tx_tos = $2; }
| TX PRIORITY expr { RIP_IPATT->tx_priority = $3; }
| TTL SECURITY bool { RIP_IPATT->ttl_security = $3; }
| TTL SECURITY TX ONLY { RIP_IPATT->ttl_security = 2; }
METRIC expr { RIP_IFACE->metric = $2; if (($2<1) || ($2>255)) cf_error("Metric must be in range 1-255"); }
| MODE MULTICAST { RIP_IFACE->mode = RIP_IM_MULTICAST; }
| MODE BROADCAST { RIP_IFACE->mode = RIP_IM_BROADCAST; if (rip_cfg_is_ng()) cf_error("Broadcast not supported in RIPng"); }
| PASSIVE bool { RIP_IFACE->passive = $2; }
| ADDRESS ipa { RIP_IFACE->address = $2; }
| PORT expr { RIP_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); }
| VERSION expr { RIP_IFACE->version = $2;
if (rip_cfg_is_ng()) cf_error("Version not supported in RIPng");
if (($2 != RIP_V1) && ($2 != RIP_V2)) cf_error("Unsupported version");
}
| VERSION ONLY bool { RIP_IFACE->version_only = $3; }
| SPLIT HORIZON bool { RIP_IFACE->split_horizon = $3; }
| POISON REVERSE bool { RIP_IFACE->poison_reverse = $3; }
| CHECK ZERO bool { RIP_IFACE->check_zero = $3; }
| UPDATE TIME expr { RIP_IFACE->update_time = $3; if ($3<=0) cf_error("Update time must be positive"); }
| TIMEOUT TIME expr { RIP_IFACE->timeout_time = $3; if ($3<=0) cf_error("Timeout time must be positive"); }
| GARBAGE TIME expr { RIP_IFACE->garbage_time = $3; if ($3<=0) cf_error("Garbage time must be positive"); }
| ECMP WEIGHT expr { RIP_IFACE->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); }
| RX BUFFER expr { RIP_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); }
| TX LENGTH expr { RIP_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); }
| TX tos { RIP_IFACE->tx_tos = $2; }
| TX PRIORITY expr { RIP_IFACE->tx_priority = $3; }
| TTL SECURITY bool { RIP_IFACE->ttl_security = $3; }
| TTL SECURITY TX ONLY { RIP_IFACE->ttl_security = 2; }
| CHECK LINK bool { RIP_IFACE->check_link = $3; }
| BFD bool { RIP_IFACE->bfd = $2; cf_check_bfd($2); }
| AUTHENTICATION rip_auth { RIP_IFACE->auth_type = $2; if ($2) rip_check_auth(); }
| password_list { rip_check_auth(); }
;
rip_auth:
NONE { $$ = RIP_AUTH_NONE; }
| PLAINTEXT { $$ = RIP_AUTH_PLAIN; }
| CRYPTOGRAPHIC { $$ = RIP_AUTH_CRYPTO; }
| MD5 { $$ = RIP_AUTH_CRYPTO; }
;
rip_iface_opts:
rip_iface_opts:
/* empty */
| rip_iface_opts rip_iface_item ';'
;
......@@ -98,25 +160,22 @@ rip_iface_opt_list:
| '{' rip_iface_opts '}'
;
rip_iface_init:
/* EMPTY */ {
this_ipatt = cfg_allocz(sizeof(struct rip_patt));
add_tail(&RIP_CFG->iface_list, NODE this_ipatt);
init_list(&this_ipatt->ipn_list);
RIP_IPATT->metric = 1;
RIP_IPATT->tx_tos = IP_PREC_INTERNET_CONTROL;
RIP_IPATT->tx_priority = sk_priority_control;
RIP_IPATT->ttl_security = RIP_DEFAULT_TTL_SECURITY;
}
;
rip_iface:
rip_iface_start iface_patt_list_nopx rip_iface_opt_list rip_iface_finish;
rip_iface: /* TODO: switch to iface_patt_list_nopx */
rip_iface_init iface_patt_list rip_iface_opt_list
;
CF_ADDTO(dynamic_attr, RIP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_METRIC); })
CF_ADDTO(dynamic_attr, RIP_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_TAG); })
CF_CLI_HELP(SHOW RIP, ..., [[Show information about RIP protocol]]);
CF_CLI(SHOW RIP INTERFACES, optsym opttext, [<name>] [\"<interface>\"], [[Show information about RIP interfaces]])
{ rip_show_interfaces(proto_get_named($4, &proto_rip), $5); };
CF_CLI(SHOW RIP NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about RIP neighbors]])
{ rip_show_neighbors(proto_get_named($4, &proto_rip), $5); };
CF_CODE
CF_END
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment