RIP: Demand circuit support (RFC 2091)

This commit is contained in:
Ondrej Zajicek (work) 2020-02-21 02:35:50 +01:00
parent 3343088a71
commit 22c3cf955d
6 changed files with 375 additions and 51 deletions

View file

@ -84,6 +84,8 @@ void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't
struct fib_node *fit_get(struct fib *, struct fib_iterator *); struct fib_node *fit_get(struct fib *, struct fib_iterator *);
void fit_put(struct fib_iterator *, struct fib_node *); 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); void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
void fit_put_end(struct fib_iterator *i);
void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src);
#define FIB_WALK(fib, type, z) do { \ #define FIB_WALK(fib, type, z) do { \
@ -118,8 +120,12 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin
#define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_) #define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_)
#define FIB_ITERATE_PUT_END(it) fit_put_end(it)
#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it) #define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
#define FIB_ITERATE_COPY(dst, src, fib) fit_copy(fib, dst, src)
/* /*
* Master Routing Tables. Generally speaking, each of them contains a FIB * Master Routing Tables. Generally speaking, each of them contains a FIB

View file

@ -584,6 +584,40 @@ found:
fit_put(i, n); fit_put(i, n);
} }
void
fit_put_end(struct fib_iterator *i)
{
i->prev = i->next = NULL;
i->node = NULL;
i->hash = ~0 - 1;
}
void
fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src)
{
struct fib_iterator *nxt = src->next;
fit_get(f, dst);
if (!src->prev)
{
/* We are at the end */
fit_put_end(dst);
return;
}
src->next = dst;
dst->prev = src;
dst->next = nxt;
if (nxt)
nxt->prev = dst;
dst->node = src->node;
dst->hash = src->hash;
}
#ifdef DEBUGGING #ifdef DEBUGGING
/** /**

View file

@ -36,7 +36,8 @@ CF_KEYWORDS(RIP, NG, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT,
GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE, GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE,
VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD, VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD,
AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY, AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY,
RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG) RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, DEMAND, CIRCUIT,
RIP_METRIC, RIP_TAG)
%type <i> rip_variant rip_auth %type <i> rip_variant rip_auth
@ -102,6 +103,7 @@ rip_iface_start:
RIP_IFACE->update_time = RIP_DEFAULT_UPDATE_TIME; RIP_IFACE->update_time = RIP_DEFAULT_UPDATE_TIME;
RIP_IFACE->timeout_time = RIP_DEFAULT_TIMEOUT_TIME; RIP_IFACE->timeout_time = RIP_DEFAULT_TIMEOUT_TIME;
RIP_IFACE->garbage_time = RIP_DEFAULT_GARBAGE_TIME; RIP_IFACE->garbage_time = RIP_DEFAULT_GARBAGE_TIME;
RIP_IFACE->rxmt_time = RIP_DEFAULT_RXMT_TIME;
}; };
rip_iface_finish: rip_iface_finish:
@ -149,9 +151,11 @@ rip_iface_item:
| SPLIT HORIZON bool { RIP_IFACE->split_horizon = $3; } | SPLIT HORIZON bool { RIP_IFACE->split_horizon = $3; }
| POISON REVERSE bool { RIP_IFACE->poison_reverse = $3; } | POISON REVERSE bool { RIP_IFACE->poison_reverse = $3; }
| CHECK ZERO bool { RIP_IFACE->check_zero = $3; } | CHECK ZERO bool { RIP_IFACE->check_zero = $3; }
| DEMAND CIRCUIT bool { RIP_IFACE->demand_circuit = $3; }
| UPDATE TIME expr { RIP_IFACE->update_time = $3 S_; if ($3<=0) cf_error("Update time must be positive"); } | UPDATE TIME expr { RIP_IFACE->update_time = $3 S_; if ($3<=0) cf_error("Update time must be positive"); }
| TIMEOUT TIME expr { RIP_IFACE->timeout_time = $3 S_; if ($3<=0) cf_error("Timeout time must be positive"); } | TIMEOUT TIME expr { RIP_IFACE->timeout_time = $3 S_; if ($3<=0) cf_error("Timeout time must be positive"); }
| GARBAGE TIME expr { RIP_IFACE->garbage_time = $3 S_; if ($3<=0) cf_error("Garbage time must be positive"); } | GARBAGE TIME expr { RIP_IFACE->garbage_time = $3 S_; if ($3<=0) cf_error("Garbage time must be positive"); }
| RETRANSMIT TIME expr_us { RIP_IFACE->rxmt_time = $3; if ($3<=0) cf_error("Retransmit 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"); } | 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"); } | 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 LENGTH expr { RIP_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); }

View file

@ -18,12 +18,18 @@
#define RIP_CMD_REQUEST 1 /* want info */ #define RIP_CMD_REQUEST 1 /* want info */
#define RIP_CMD_RESPONSE 2 /* responding to request */ #define RIP_CMD_RESPONSE 2 /* responding to request */
#define RIP_CMD_UPDATE_REQUEST 9
#define RIP_CMD_UPDATE_RESPONSE 10
#define RIP_CMD_UPDATE_ACK 11
#define RIP_BLOCK_LENGTH 20 #define RIP_BLOCK_LENGTH 20
#define RIP_PASSWD_LENGTH 16 #define RIP_PASSWD_LENGTH 16
#define RIP_AF_IPV4 2 #define RIP_AF_IPV4 2
#define RIP_AF_AUTH 0xffff #define RIP_AF_AUTH 0xffff
#define RIP_UPDATE_VERSION 1
/* RIP packet header */ /* RIP packet header */
struct rip_packet struct rip_packet
@ -33,6 +39,14 @@ struct rip_packet
u16 unused; u16 unused;
}; };
/* Triggered RIP update header (RFC 2091) */
struct rip_update_hdr
{
u8 version;
u8 flush;
u16 seqnum;
};
/* RTE block for RIPv2 */ /* RTE block for RIPv2 */
struct rip_block_v2 struct rip_block_v2
{ {
@ -89,6 +103,8 @@ struct rip_block
ip_addr next_hop; ip_addr next_hop;
}; };
static int
rip_send_ack(struct rip_proto *p, struct rip_iface *ifa, uint flush, uint seqnum);
#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) #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 DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0)
@ -107,8 +123,27 @@ struct rip_block
static inline void * rip_tx_buffer(struct rip_iface *ifa) static inline void * rip_tx_buffer(struct rip_iface *ifa)
{ return ifa->sk->tbuf; } { return ifa->sk->tbuf; }
static inline uint rip_pkt_hdrlen(struct rip_iface *ifa) static inline uint
{ return sizeof(struct rip_packet) + (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); } rip_pkt_hdrlen(struct rip_iface *ifa)
{
return sizeof(struct rip_packet) +
(ifa->cf->demand_circuit ? sizeof(struct rip_update_hdr) : 0) +
(ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0);
}
static inline struct rip_update_hdr *
rip_get_update_hdr(struct rip_packet *pkt)
{
return (void *) ((byte *) pkt + sizeof(struct rip_packet));
}
static inline struct rip_block_auth *
rip_get_auth_block(struct rip_iface *ifa, struct rip_packet *pkt)
{
return (void *) ((byte *) pkt + sizeof(struct rip_packet) +
(ifa->cf->demand_circuit ? sizeof(struct rip_update_hdr) : 0));
}
static inline void static inline void
rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte) rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte)
@ -199,10 +234,31 @@ rip_update_csn(struct rip_proto *p UNUSED, struct rip_iface *ifa)
} }
} }
static inline void
rip_fill_header(struct rip_iface *ifa, struct rip_packet *pkt, uint cmd)
{
*pkt = (struct rip_packet) {
.command = cmd,
.version = ifa->cf->version
};
}
static inline void
rip_fill_update_hdr(struct rip_packet *pkt, uint flush, uint seqnum)
{
struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
*hdr = (struct rip_update_hdr) {
.version = RIP_UPDATE_VERSION,
.flush = flush,
.seqnum = htons(seqnum)
};
}
static void static void
rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen) 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 rip_block_auth *auth = rip_get_auth_block(ifa, pkt);
struct password_item *pass = password_find(ifa->cf->passwords, 0); struct password_item *pass = password_find(ifa->cf->passwords, 0);
if (!pass) if (!pass)
@ -271,16 +327,16 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p
} }
static int static int
rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, struct rip_neighbor *n) rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, uint hdr_len, struct rip_neighbor *n)
{ {
struct rip_block_auth *auth = (void *) (pkt + 1); struct rip_block_auth *auth = (void *) ((byte *) pkt + hdr_len);
struct password_item *pass = NULL; struct password_item *pass = NULL;
const char *err_dsc = NULL; const char *err_dsc = NULL;
uint err_val = 0; uint err_val = 0;
uint auth_type = 0; uint auth_type = 0;
/* Check for authentication entry */ /* Check for authentication entry */
if ((*plen >= (sizeof(struct rip_packet) + sizeof(struct rip_block_auth))) && if ((*plen >= (hdr_len + sizeof(struct rip_block_auth))) &&
(auth->must_be_ffff == htons(0xffff))) (auth->must_be_ffff == htons(0xffff)))
auth_type = ntohs(auth->auth_type); auth_type = ntohs(auth->auth_type);
@ -376,17 +432,31 @@ rip_send_to(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt,
return sk_send_to(ifa->sk, plen, dst, 0); return sk_send_to(ifa->sk, plen, dst, 0);
} }
static inline void
rip_kick_rxmt_timer(struct rip_iface *ifa)
{
if (! tm_active(ifa->rxmt_timer))
tm_start(ifa->rxmt_timer, ifa->cf->rxmt_time);
}
void void
rip_send_request(struct rip_proto *p, struct rip_iface *ifa) rip_send_request(struct rip_proto *p, struct rip_iface *ifa)
{ {
byte *pos = rip_tx_buffer(ifa); struct rip_packet *pkt = rip_tx_buffer(ifa);
byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa);
struct rip_packet *pkt = (void *) pos; rip_fill_header(ifa, pkt, RIP_CMD_REQUEST);
pkt->command = RIP_CMD_REQUEST;
pkt->version = ifa->cf->version; if (ifa->cf->demand_circuit)
pkt->unused = 0; {
pos += rip_pkt_hdrlen(ifa); pkt->command = RIP_CMD_UPDATE_REQUEST;
rip_fill_update_hdr(pkt, 0, 0);
/* Must be acknowledged by update response */
ifa->req_pending = 1;
rip_kick_rxmt_timer(ifa);
}
struct rip_block b = { .no_af = 1, .metric = p->infinity }; struct rip_block b = { .no_af = 1, .metric = p->infinity };
rip_put_block(p, pos, &b); rip_put_block(p, pos, &b);
@ -437,18 +507,25 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
if (! ifa->tx_active) if (! ifa->tx_active)
return 0; return 0;
byte *pos = rip_tx_buffer(ifa); /* In demand circuit mode, we may wait for ACK */
byte *max = rip_tx_buffer(ifa) + ifa->tx_plen - if (ifa->tx_pending)
return 0;
struct rip_packet *pkt = rip_tx_buffer(ifa);
byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa);
byte *max = (byte *) pkt + ifa->tx_plen -
(rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH); (rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH);
ip_addr last_next_hop = IPA_NONE; ip_addr last_next_hop = IPA_NONE;
btime now_ = current_time(); btime now_ = current_time();
int send = 0; int send = 0;
struct rip_packet *pkt = (void *) pos; rip_fill_header(ifa, pkt, RIP_CMD_RESPONSE);
pkt->command = RIP_CMD_RESPONSE;
pkt->version = ifa->cf->version; if (ifa->cf->demand_circuit)
pkt->unused = 0; {
pos += rip_pkt_hdrlen(ifa); pkt->command = RIP_CMD_UPDATE_RESPONSE;
rip_fill_update_hdr(pkt, ifa->tx_flush, ifa->tx_seqnum);
}
FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, struct rip_entry, en) FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, struct rip_entry, en)
{ {
@ -469,7 +546,7 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
if (pos > max) if (pos > max)
{ {
FIB_ITERATE_PUT(&ifa->tx_fit); FIB_ITERATE_PUT(&ifa->tx_fit);
goto break_loop; goto send_pkt;
} }
struct rip_block rte = { struct rip_block rte = {
@ -522,13 +599,31 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa)
next_entry: ; next_entry: ;
} }
FIB_ITERATE_END; FIB_ITERATE_END;
ifa->tx_active = 0;
if (send)
{
FIB_ITERATE_PUT_END(&ifa->tx_fit);
goto send_pkt;
}
/* Do not send empty packet */ /* Do not send empty packet */
if (!send)
return 0;
break_loop: ifa->tx_active = 0;
/* Unlink second iterator */
FIB_ITERATE_UNLINK(&ifa->tx_done, &p->rtable);
return 0;
send_pkt:
/* Waiting for ack or timeout */
if (ifa->cf->demand_circuit)
{
ifa->tx_pending = 1;
rip_kick_rxmt_timer(ifa);
}
TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name); TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name);
return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr); return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr);
} }
@ -558,6 +653,10 @@ rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, btime c
ifa->tx_addr = addr; ifa->tx_addr = addr;
ifa->tx_changed = changed; ifa->tx_changed = changed;
FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable); FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable);
FIB_ITERATE_INIT(&ifa->tx_done, &p->rtable);
if (ifa->cf->demand_circuit)
ifa->tx_flush = ! changed;
rip_update_csn(p, ifa); rip_update_csn(p, ifa);
@ -597,9 +696,27 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name); TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name);
byte *pos = (byte *) pkt + sizeof(struct rip_packet); byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa);
byte *end = (byte *) pkt + plen; byte *end = (byte *) pkt + plen;
btime now_ = current_time();
btime expires = current_time() + ifa->cf->timeout_time;
if (pkt->command == RIP_CMD_UPDATE_RESPONSE)
{
struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
rip_send_ack(p, ifa, hdr->flush, ntohs(hdr->seqnum));
expires = TIME_INFINITY;
/* Handle flush bit */
if (hdr->flush)
{
/* Flush old routes */
rip_flush_table(p, from);
/* Acknowledge pending request */
ifa->req_pending = 0;
}
}
for (; pos < end; pos += RIP_BLOCK_LENGTH) for (; pos < end; pos += RIP_BLOCK_LENGTH)
{ {
@ -647,7 +764,7 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
.next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr, .next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr,
.metric = rte.metric, .metric = rte.metric,
.tag = rte.tag, .tag = rte.tag,
.expires = now_ + ifa->cf->timeout_time .expires = expires
}; };
rip_update_rte(p, &rte.net, &new); rip_update_rte(p, &rte.net, &new);
@ -663,6 +780,85 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
} }
} }
static int
rip_send_ack(struct rip_proto *p, struct rip_iface *ifa, uint flush, uint seqnum)
{
struct rip_packet *pkt = rip_tx_buffer(ifa);
rip_fill_header(ifa, pkt, RIP_CMD_UPDATE_ACK);
rip_fill_update_hdr(pkt, flush, seqnum);
TRACE(D_PACKETS, "Sending acknowledge via %s", ifa->iface->name);
return rip_send_to(p, ifa, pkt, rip_pkt_hdrlen(ifa), ifa->tx_addr);
}
static void
rip_receive_ack(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen UNUSED, struct rip_neighbor *from)
{
TRACE(D_PACKETS, "Acknowledge received from %I on %s", from->nbr->addr, ifa->iface->name);
struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
uint seqnum = ntohs(hdr->seqnum);
if (! ifa->tx_active || ! ifa->tx_pending)
{
LOG_PKT("Bad acknowledge packet from %I via %s - no pending response",
from->nbr->addr, ifa->iface->name);
return;
}
if (seqnum != ifa->tx_seqnum)
{
LOG_PKT("Bad acknowledge packet from %I via %s - "
"mismatched sequence number (rcv %u, old %u)",
from->nbr->addr, ifa->iface->name, seqnum, (uint) ifa->tx_seqnum);
return;
}
/* Move acked position */
FIB_ITERATE_COPY(&ifa->tx_done, &ifa->tx_fit, &p->rtable);
/* Packet is no longer pending */
ifa->tx_pending = 0;
ifa->tx_seqnum++;
/* Next one does not have flush bit */
ifa->tx_flush = 0;
rip_send_response(p, ifa);
}
/**
* rip_rxmt_timeout - RIP retransmission timer hook
* @t: timer
*
* In Demand Circuit mode, update packets must be acknowledged to ensure
* reliability. If they are not acknowledged, we need to retransmit them.
*/
void
rip_rxmt_timeout(timer *t)
{
struct rip_iface *ifa = t->data;
struct rip_proto *p = ifa->rip;
if (ifa->req_pending)
rip_send_request(p, ifa);
if (ifa->tx_pending)
{
/* Revert to acked position */
FIB_ITERATE_COPY(&ifa->tx_fit, &ifa->tx_done, &p->rtable);
/* Packet is no longer pending */
ifa->tx_pending = 0;
ifa->tx_seqnum++;
rip_send_response(p, ifa);
}
}
static int static int
rip_rx_hook(sock *sk, uint len) rip_rx_hook(sock *sk, uint len)
{ {
@ -702,17 +898,43 @@ rip_rx_hook(sock *sk, uint len)
DROP("truncated", len); DROP("truncated", len);
struct rip_packet *pkt = (struct rip_packet *) sk->rbuf; struct rip_packet *pkt = (struct rip_packet *) sk->rbuf;
uint plen = len; uint pkt_len = len;
uint hdr_len = sizeof(struct rip_packet);
uint update_msg = 0;
if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version))) if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version)))
DROP("wrong version", pkt->version); DROP("wrong version", pkt->version);
/* Update packets (RFC 2091) have additional header even before auth data */
if ((pkt->command == RIP_CMD_UPDATE_REQUEST) ||
(pkt->command == RIP_CMD_UPDATE_RESPONSE) ||
(pkt->command == RIP_CMD_UPDATE_ACK))
{
hdr_len += sizeof(struct rip_update_hdr);
if (len < hdr_len)
DROP("too short", len);
struct rip_update_hdr *hdr = rip_get_update_hdr(pkt);
if (hdr->version != RIP_UPDATE_VERSION)
DROP("wrong update header version", hdr->version);
if (hdr->flush > 1)
DROP("wrong flush value", hdr->flush);
update_msg = 1;
}
/* rip_check_authentication() has its own error logging */ /* rip_check_authentication() has its own error logging */
if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &plen, n)) if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &pkt_len, hdr_len, n))
return 1; return 1;
if ((plen - sizeof(struct rip_packet)) % RIP_BLOCK_LENGTH) if ((pkt_len - hdr_len) % RIP_BLOCK_LENGTH)
DROP("invalid length", plen); DROP("invalid length", pkt_len);
if (update_msg != ifa->cf->demand_circuit)
DROP("demand circuit mode mismatch", update_msg);
n->last_seen = current_time(); n->last_seen = current_time();
rip_update_bfd(p, n); rip_update_bfd(p, n);
@ -720,11 +942,17 @@ rip_rx_hook(sock *sk, uint len)
switch (pkt->command) switch (pkt->command)
{ {
case RIP_CMD_REQUEST: case RIP_CMD_REQUEST:
rip_receive_request(p, ifa, pkt, plen, n); case RIP_CMD_UPDATE_REQUEST:
rip_receive_request(p, ifa, pkt, pkt_len, n);
break; break;
case RIP_CMD_RESPONSE: case RIP_CMD_RESPONSE:
rip_receive_response(p, ifa, pkt, plen, n); case RIP_CMD_UPDATE_RESPONSE:
rip_receive_response(p, ifa, pkt, pkt_len, n);
break;
case RIP_CMD_UPDATE_ACK:
rip_receive_ack(p, ifa, pkt, pkt_len, n);
break; break;
default: default:

View file

@ -368,6 +368,20 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
} }
} }
void
rip_flush_table(struct rip_proto *p, struct rip_neighbor *n)
{
btime expires = current_time() + n->ifa->cf->timeout_time;
FIB_WALK(&p->rtable, struct rip_entry, en)
{
for (struct rip_rte *e = en->routes; e; e = e->next)
if ((e->from == n) && (e->expires == TIME_INFINITY))
e->expires = expires;
}
FIB_WALK_END;
}
/* /*
* RIP neighbors * RIP neighbors
@ -506,14 +520,20 @@ rip_iface_start(struct rip_iface *ifa)
TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name); TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name);
ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS; if (! ifa->cf->demand_circuit)
ifa->next_triggered = current_time(); /* Available immediately */ {
ifa->want_triggered = 1; /* All routes in triggered update */ ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS;
tm_start(ifa->timer, 100 MS); tm_set(ifa->timer, ifa->next_regular);
}
else
{
ifa->next_regular = TIME_INFINITY;
}
ifa->up = 1; ifa->up = 1;
if (!ifa->cf->passive) rip_send_request(p, ifa);
rip_send_request(ifa->rip, ifa); rip_send_table(p, ifa, ifa->addr, 0);
} }
static void static void
@ -526,10 +546,18 @@ rip_iface_stop(struct rip_iface *ifa)
rip_reset_tx_session(p, ifa); rip_reset_tx_session(p, ifa);
ifa->next_regular = 0;
ifa->next_triggered = 0;
ifa->want_triggered = 0;
ifa->tx_pending = 0;
ifa->req_pending = 0;
WALK_LIST_FIRST(n, ifa->neigh_list) WALK_LIST_FIRST(n, ifa->neigh_list)
rip_remove_neighbor(p, n); rip_remove_neighbor(p, n);
tm_stop(ifa->timer); tm_stop(ifa->timer);
tm_stop(ifa->rxmt_timer);
ifa->up = 0; ifa->up = 0;
} }
@ -643,6 +671,7 @@ rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config
add_tail(&p->iface_list, NODE ifa); add_tail(&p->iface_list, NODE ifa);
ifa->timer = tm_new_init(p->p.pool, rip_iface_timer, ifa, 0, 0); ifa->timer = tm_new_init(p->p.pool, rip_iface_timer, ifa, 0, 0);
ifa->rxmt_timer = tm_new_init(p->p.pool, rip_rxmt_timeout, ifa, 0, 0);
struct object_lock *lock = olock_new(p->p.pool); struct object_lock *lock = olock_new(p->p.pool);
lock->type = OBJLOCK_UDP; lock->type = OBJLOCK_UDP;
@ -885,6 +914,11 @@ rip_timer(timer *t)
/* Handling neighbor expiration */ /* Handling neighbor expiration */
WALK_LIST(ifa, p->iface_list) WALK_LIST(ifa, p->iface_list)
{
/* No expiration for demand circuit ifaces */
if (ifa->cf->demand_circuit)
continue;
WALK_LIST_DELSAFE(n, nn, ifa->neigh_list) WALK_LIST_DELSAFE(n, nn, ifa->neigh_list)
if (n->last_seen) if (n->last_seen)
{ {
@ -895,6 +929,7 @@ rip_timer(timer *t)
else else
next = MIN(next, expires); next = MIN(next, expires);
} }
}
tm_start(p->timer, MAX(next - now_, 100 MS)); tm_start(p->timer, MAX(next - now_, 100 MS));
} }
@ -902,7 +937,7 @@ rip_timer(timer *t)
static inline void static inline void
rip_kick_timer(struct rip_proto *p) rip_kick_timer(struct rip_proto *p)
{ {
if (p->timer->expires > (current_time() + 100 MS)) if ((p->timer->expires > (current_time() + 100 MS)))
tm_start(p->timer, 100 MS); tm_start(p->timer, 100 MS);
} }
@ -931,11 +966,8 @@ rip_iface_timer(timer *t)
if (ifa->tx_active) if (ifa->tx_active)
{ {
if (now_ < (ifa->next_regular + period)) tm_start(ifa->timer, 100 MS);
{ tm_start(ifa->timer, 100 MS); return; } 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) if (now_ >= ifa->next_regular)
@ -957,13 +989,17 @@ rip_iface_timer(timer *t)
p->triggered = 0; p->triggered = 0;
} }
tm_start(ifa->timer, ifa->want_triggered ? (1 S) : (ifa->next_regular - now_)); if (ifa->want_triggered && (ifa->next_triggered < ifa->next_regular))
tm_set(ifa->timer, ifa->next_triggered);
else if (ifa->next_regular != TIME_INFINITY)
tm_set(ifa->timer, ifa->next_regular);
} }
static inline void static inline void
rip_iface_kick_timer(struct rip_iface *ifa) rip_iface_kick_timer(struct rip_iface *ifa)
{ {
if (ifa->timer->expires > (current_time() + 100 MS)) if ((! tm_active(ifa->timer)) || (ifa->timer->expires > (current_time() + 100 MS)))
tm_start(ifa->timer, 100 MS); tm_start(ifa->timer, 100 MS);
} }
@ -987,9 +1023,8 @@ rip_trigger_update(struct rip_proto *p)
TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name); TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name);
ifa->want_triggered = current_time(); ifa->want_triggered = current_time();
rip_iface_kick_timer(ifa); rip_iface_kick_timer(ifa);
p->triggered = 1;
} }
p->triggered = 1;
} }
@ -1179,7 +1214,8 @@ rip_show_interfaces(struct proto *P, char *iff)
nbrs++; nbrs++;
btime now_ = current_time(); btime now_ = current_time();
btime timer = (ifa->next_regular > now_) ? (ifa->next_regular - now_) : 0; btime timer = ((ifa->next_regular < TIME_INFINITY) && (ifa->next_regular > now_)) ?
(ifa->next_regular - now_) : 0;
cli_msg(-1021, "%-10s %-6s %6u %6u %7t", cli_msg(-1021, "%-10s %-6s %6u %6u %7t",
ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer); ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer);
} }

View file

@ -41,6 +41,7 @@
#define RIP_DEFAULT_UPDATE_TIME (30 S_) #define RIP_DEFAULT_UPDATE_TIME (30 S_)
#define RIP_DEFAULT_TIMEOUT_TIME (180 S_) #define RIP_DEFAULT_TIMEOUT_TIME (180 S_)
#define RIP_DEFAULT_GARBAGE_TIME (120 S_) #define RIP_DEFAULT_GARBAGE_TIME (120 S_)
#define RIP_DEFAULT_RXMT_TIME (1 S_)
struct rip_config struct rip_config
@ -73,6 +74,7 @@ struct rip_iface_config
u8 auth_type; /* Authentication type (RIP_AUTH_*) */ u8 auth_type; /* Authentication type (RIP_AUTH_*) */
u8 ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ 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 check_link; /* Whether iface link change is used */
u8 demand_circuit; /* Use demand circuit extensions (RFC 2091) */
u8 bfd; /* Use BFD on iface */ u8 bfd; /* Use BFD on iface */
u16 rx_buffer; /* RX buffer size, 0 for MTU */ u16 rx_buffer; /* RX buffer size, 0 for MTU */
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */ u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
@ -81,6 +83,7 @@ struct rip_iface_config
btime update_time; /* Periodic update interval */ btime update_time; /* Periodic update interval */
btime timeout_time; /* Route expiration timeout */ btime timeout_time; /* Route expiration timeout */
btime garbage_time; /* Unreachable entry GC timeout */ btime garbage_time; /* Unreachable entry GC timeout */
btime rxmt_time; /* Retransmit timeout for demand circuit */
list *passwords; /* Passwords for authentication */ list *passwords; /* Passwords for authentication */
}; };
@ -110,6 +113,7 @@ struct rip_iface
struct rip_iface_config *cf; /* Related config, must be updated in reconfigure */ struct rip_iface_config *cf; /* Related config, must be updated in reconfigure */
struct object_lock *lock; /* Interface lock */ struct object_lock *lock; /* Interface lock */
timer *timer; /* Interface timer */ timer *timer; /* Interface timer */
timer *rxmt_timer; /* Retransmission timer */
sock *sk; /* UDP socket */ sock *sk; /* UDP socket */
u8 up; /* Interface is active */ u8 up; /* Interface is active */
@ -126,9 +130,18 @@ struct rip_iface
/* Active update */ /* Active update */
int tx_active; /* Update session is active */ int tx_active; /* Update session is active */
int tx_waiting;
ip_addr tx_addr; /* Update session destination address */ ip_addr tx_addr; /* Update session destination address */
btime tx_changed; /* Minimal changed time for triggered update */ btime tx_changed; /* Minimal changed time for triggered update */
struct fib_iterator tx_fit; /* FIB iterator in RIP routing table (p.rtable) */ struct fib_iterator tx_fit; /* FIB iterator in RIP routing table (p.rtable) */
struct fib_iterator tx_done; /* FIB iterator for acked routes (p.rtable) */
/* Update message */
u8 tx_pending;
u8 tx_flush;
u16 tx_seqnum;
u8 req_pending;
}; };
struct rip_neighbor struct rip_neighbor
@ -197,6 +210,7 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
if (ifa->tx_active) if (ifa->tx_active)
{ {
FIB_ITERATE_UNLINK(&ifa->tx_fit, &p->rtable); FIB_ITERATE_UNLINK(&ifa->tx_fit, &p->rtable);
FIB_ITERATE_UNLINK(&ifa->tx_done, &p->rtable);
ifa->tx_active = 0; ifa->tx_active = 0;
} }
} }
@ -204,6 +218,7 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
/* rip.c */ /* rip.c */
void rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new); void rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new);
void rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from); void rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from);
void rip_flush_table(struct rip_proto *p, struct rip_neighbor *n);
struct rip_neighbor * rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa); 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_update_bfd(struct rip_proto *p, struct rip_neighbor *n);
void rip_show_interfaces(struct proto *P, char *iff); void rip_show_interfaces(struct proto *P, char *iff);
@ -212,6 +227,7 @@ void rip_show_neighbors(struct proto *P, char *iff);
/* packets.c */ /* packets.c */
void rip_send_request(struct rip_proto *p, struct rip_iface *ifa); 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, btime changed); void rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, btime changed);
void rip_rxmt_timeout(timer *t);
int rip_open_socket(struct rip_iface *ifa); int rip_open_socket(struct rip_iface *ifa);