From 22c3cf955dbbb65aa29e322efa70dabb749f0232 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Fri, 21 Feb 2020 02:35:50 +0100 Subject: [PATCH] RIP: Demand circuit support (RFC 2091) --- nest/route.h | 6 + nest/rt-fib.c | 34 +++++ proto/rip/config.Y | 6 +- proto/rip/packets.c | 294 +++++++++++++++++++++++++++++++++++++++----- proto/rip/rip.c | 70 ++++++++--- proto/rip/rip.h | 16 +++ 6 files changed, 375 insertions(+), 51 deletions(-) diff --git a/nest/route.h b/nest/route.h index b927db5f..5421ece5 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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 *); 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_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 { \ @@ -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_END(it) fit_put_end(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 diff --git a/nest/rt-fib.c b/nest/rt-fib.c index 76a86e6e..a7f70371 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -584,6 +584,40 @@ found: 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 /** diff --git a/proto/rip/config.Y b/proto/rip/config.Y index 5b5f94a0..6cea7dd0 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -36,7 +36,8 @@ CF_KEYWORDS(RIP, NG, 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) + RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, DEMAND, CIRCUIT, + RIP_METRIC, RIP_TAG) %type rip_variant rip_auth @@ -102,6 +103,7 @@ rip_iface_start: 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->rxmt_time = RIP_DEFAULT_RXMT_TIME; }; rip_iface_finish: @@ -149,9 +151,11 @@ rip_iface_item: | SPLIT HORIZON bool { RIP_IFACE->split_horizon = $3; } | POISON REVERSE bool { RIP_IFACE->poison_reverse = $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"); } | 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"); } + | 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"); } | 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"); } diff --git a/proto/rip/packets.c b/proto/rip/packets.c index 59ffd7c2..8de691af 100644 --- a/proto/rip/packets.c +++ b/proto/rip/packets.c @@ -18,12 +18,18 @@ #define RIP_CMD_REQUEST 1 /* want info */ #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_PASSWD_LENGTH 16 #define RIP_AF_IPV4 2 #define RIP_AF_AUTH 0xffff +#define RIP_UPDATE_VERSION 1 + /* RIP packet header */ struct rip_packet @@ -33,6 +39,14 @@ struct rip_packet u16 unused; }; +/* Triggered RIP update header (RFC 2091) */ +struct rip_update_hdr +{ + u8 version; + u8 flush; + u16 seqnum; +}; + /* RTE block for RIPv2 */ struct rip_block_v2 { @@ -89,6 +103,8 @@ struct rip_block 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 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) { 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 uint +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 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 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); if (!pass) @@ -271,16 +327,16 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p } 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; 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))) && + if ((*plen >= (hdr_len + sizeof(struct rip_block_auth))) && (auth->must_be_ffff == htons(0xffff))) 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); } +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 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; - pkt->command = RIP_CMD_REQUEST; - pkt->version = ifa->cf->version; - pkt->unused = 0; - pos += rip_pkt_hdrlen(ifa); + rip_fill_header(ifa, pkt, RIP_CMD_REQUEST); + + if (ifa->cf->demand_circuit) + { + 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 }; 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) return 0; - byte *pos = rip_tx_buffer(ifa); - byte *max = rip_tx_buffer(ifa) + ifa->tx_plen - + /* In demand circuit mode, we may wait for ACK */ + 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); ip_addr last_next_hop = IPA_NONE; btime now_ = current_time(); 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); + rip_fill_header(ifa, pkt, RIP_CMD_RESPONSE); + + if (ifa->cf->demand_circuit) + { + 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) { @@ -469,7 +546,7 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa) if (pos > max) { FIB_ITERATE_PUT(&ifa->tx_fit); - goto break_loop; + goto send_pkt; } struct rip_block rte = { @@ -522,13 +599,31 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa) next_entry: ; } FIB_ITERATE_END; - ifa->tx_active = 0; + + if (send) + { + FIB_ITERATE_PUT_END(&ifa->tx_fit); + goto send_pkt; + } /* 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); 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_changed = changed; 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); @@ -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); - byte *pos = (byte *) pkt + sizeof(struct rip_packet); + byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); 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) { @@ -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, .metric = rte.metric, .tag = rte.tag, - .expires = now_ + ifa->cf->timeout_time + .expires = expires }; 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 rip_rx_hook(sock *sk, uint len) { @@ -702,17 +898,43 @@ rip_rx_hook(sock *sk, uint len) DROP("truncated", len); 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))) 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 */ - 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; - if ((plen - sizeof(struct rip_packet)) % RIP_BLOCK_LENGTH) - DROP("invalid length", plen); + if ((pkt_len - hdr_len) % RIP_BLOCK_LENGTH) + DROP("invalid length", pkt_len); + + if (update_msg != ifa->cf->demand_circuit) + DROP("demand circuit mode mismatch", update_msg); n->last_seen = current_time(); rip_update_bfd(p, n); @@ -720,11 +942,17 @@ rip_rx_hook(sock *sk, uint len) switch (pkt->command) { 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; 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; default: diff --git a/proto/rip/rip.c b/proto/rip/rip.c index e811a3d8..80e8d082 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -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 @@ -506,14 +520,20 @@ rip_iface_start(struct rip_iface *ifa) TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name); - ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS; - ifa->next_triggered = current_time(); /* Available immediately */ - ifa->want_triggered = 1; /* All routes in triggered update */ - tm_start(ifa->timer, 100 MS); + if (! ifa->cf->demand_circuit) + { + ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS; + tm_set(ifa->timer, ifa->next_regular); + } + else + { + ifa->next_regular = TIME_INFINITY; + } + ifa->up = 1; - if (!ifa->cf->passive) - rip_send_request(ifa->rip, ifa); + rip_send_request(p, ifa); + rip_send_table(p, ifa, ifa->addr, 0); } static void @@ -526,10 +546,18 @@ rip_iface_stop(struct rip_iface *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) rip_remove_neighbor(p, n); tm_stop(ifa->timer); + tm_stop(ifa->rxmt_timer); 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); 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); lock->type = OBJLOCK_UDP; @@ -885,6 +914,11 @@ rip_timer(timer *t) /* Handling neighbor expiration */ 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) if (n->last_seen) { @@ -895,6 +929,7 @@ rip_timer(timer *t) else next = MIN(next, expires); } + } tm_start(p->timer, MAX(next - now_, 100 MS)); } @@ -902,7 +937,7 @@ rip_timer(timer *t) static inline void 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); } @@ -931,11 +966,8 @@ rip_iface_timer(timer *t) if (ifa->tx_active) { - if (now_ < (ifa->next_regular + period)) - { tm_start(ifa->timer, 100 MS); 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); + tm_start(ifa->timer, 100 MS); + return; } if (now_ >= ifa->next_regular) @@ -957,13 +989,17 @@ rip_iface_timer(timer *t) 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 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); } @@ -987,9 +1023,8 @@ rip_trigger_update(struct rip_proto *p) TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name); ifa->want_triggered = current_time(); rip_iface_kick_timer(ifa); + p->triggered = 1; } - - p->triggered = 1; } @@ -1179,7 +1214,8 @@ rip_show_interfaces(struct proto *P, char *iff) nbrs++; 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", ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer); } diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 2dc34862..bf597350 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -41,6 +41,7 @@ #define RIP_DEFAULT_UPDATE_TIME (30 S_) #define RIP_DEFAULT_TIMEOUT_TIME (180 S_) #define RIP_DEFAULT_GARBAGE_TIME (120 S_) +#define RIP_DEFAULT_RXMT_TIME (1 S_) struct rip_config @@ -73,6 +74,7 @@ struct rip_iface_config 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 demand_circuit; /* Use demand circuit extensions (RFC 2091) */ 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 */ @@ -81,6 +83,7 @@ struct rip_iface_config btime update_time; /* Periodic update interval */ btime timeout_time; /* Route expiration timeout */ btime garbage_time; /* Unreachable entry GC timeout */ + btime rxmt_time; /* Retransmit timeout for demand circuit */ 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 object_lock *lock; /* Interface lock */ timer *timer; /* Interface timer */ + timer *rxmt_timer; /* Retransmission timer */ sock *sk; /* UDP socket */ u8 up; /* Interface is active */ @@ -126,9 +130,18 @@ struct rip_iface /* Active update */ int tx_active; /* Update session is active */ + int tx_waiting; ip_addr tx_addr; /* Update session destination address */ 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_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 @@ -197,6 +210,7 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa) if (ifa->tx_active) { FIB_ITERATE_UNLINK(&ifa->tx_fit, &p->rtable); + FIB_ITERATE_UNLINK(&ifa->tx_done, &p->rtable); ifa->tx_active = 0; } } @@ -204,6 +218,7 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa) /* rip.c */ 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_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); void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n); void rip_show_interfaces(struct proto *P, char *iff); @@ -212,6 +227,7 @@ 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, btime changed); +void rip_rxmt_timeout(timer *t); int rip_open_socket(struct rip_iface *ifa);