0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-17 08:38:42 +00:00

Babel: Fix handling of seqno requests

Old behavior has several deficiencies compared to standard behavior
(no triggered updates for replies, no retransmissions, ...).
This commit is contained in:
Ondrej Zajicek (work) 2017-11-08 14:35:52 +01:00
parent 672fb78e12
commit dbf1ed263c
2 changed files with 189 additions and 144 deletions

View File

@ -48,19 +48,20 @@
static inline int ge_mod64k(uint a, uint b)
{ return (u16)(a - b) < 0x8000; }
static void babel_dump_entry(struct babel_entry *e);
static void babel_dump_route(struct babel_route *r);
static void babel_expire_requests(struct babel_proto *p, struct babel_entry *e);
static void babel_select_route(struct babel_proto *p, struct babel_entry *e);
static void babel_send_route_request(struct babel_proto *p, struct babel_entry *e, struct babel_neighbor *n);
static void babel_send_wildcard_request(struct babel_iface *ifa);
static int babel_cache_seqno_request(struct babel_proto *p, net_addr *n, u64 router_id, u16 seqno);
static void babel_trigger_iface_update(struct babel_iface *ifa);
static void babel_trigger_update(struct babel_proto *p);
static void babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e);
static void babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e, struct babel_seqno_request *sr);
static void babel_update_cost(struct babel_neighbor *n);
static inline void babel_kick_timer(struct babel_proto *p);
static inline void babel_iface_kick_timer(struct babel_iface *ifa);
static inline void babel_lock_neighbor(struct babel_neighbor *nbr)
{ if (nbr) nbr->uc++; }
static inline void babel_unlock_neighbor(struct babel_neighbor *nbr)
{ if (nbr && !--nbr->uc) mb_free(nbr); }
/*
* Functions to maintain data structures
@ -72,6 +73,7 @@ babel_init_entry(void *E)
struct babel_entry *e = E;
e->updated = current_time();
init_list(&e->requests);
init_list(&e->sources);
init_list(&e->routes);
}
@ -260,9 +262,10 @@ loop:
}
babel_expire_sources(p, e);
babel_expire_requests(p, e);
/* Remove empty entries */
if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes))
if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes) && EMPTY_LIST(e->requests))
{
FIB_ITERATE_PUT(&fit);
fib_delete(rtable, e);
@ -279,6 +282,110 @@ babel_expire_routes(struct babel_proto *p)
babel_expire_routes_(p, &p->ip6_rtable);
}
static inline int seqno_request_valid(struct babel_seqno_request *sr)
{ return !sr->nbr || sr->nbr->ifa; }
/*
* Add seqno request to the table of pending requests (RFC 6216 3.2.6) and send
* it to network. Do nothing if it is already in the table.
*/
static void
babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
u64 router_id, u16 seqno, u8 hop_count,
struct babel_neighbor *nbr)
{
struct babel_seqno_request *sr;
WALK_LIST(sr, e->requests)
if (sr->router_id == router_id)
{
/* Found matching or newer */
if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr))
return;
/* Found older */
babel_unlock_neighbor(sr->nbr);
rem_node(NODE sr);
goto found;
}
/* No entries found */
sr = sl_alloc(p->seqno_slab);
found:
sr->router_id = router_id;
sr->seqno = seqno;
sr->hop_count = hop_count;
sr->count = 0;
sr->expires = current_time() + BABEL_SEQNO_REQUEST_EXPIRY;
babel_lock_neighbor(sr->nbr = nbr);
add_tail(&e->requests, NODE sr);
babel_send_seqno_request(p, e, sr);
}
static void
babel_remove_seqno_request(struct babel_proto *p, struct babel_seqno_request *sr)
{
babel_unlock_neighbor(sr->nbr);
rem_node(NODE sr);
sl_free(p->seqno_slab, sr);
}
static int
babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e,
u64 router_id, u16 seqno)
{
struct babel_seqno_request *sr;
WALK_LIST(sr, e->requests)
if ((sr->router_id == router_id) && ge_mod64k(seqno, sr->seqno))
{
/* Found the request, remove it */
babel_remove_seqno_request(p, sr);
return 1;
}
return 0;
}
static void
babel_expire_requests(struct babel_proto *p, struct babel_entry *e)
{
struct babel_seqno_request *sr, *srx;
btime now_ = current_time();
WALK_LIST_DELSAFE(sr, srx, e->requests)
{
/* Remove seqno requests sent to dead neighbors */
if (!seqno_request_valid(sr))
{
babel_remove_seqno_request(p, sr);
continue;
}
/* Handle expired requests - resend or remove */
if (sr->expires && sr->expires <= now_)
{
if (sr->count < BABEL_SEQNO_REQUEST_RETRY)
{
sr->count++;
sr->expires += (BABEL_SEQNO_REQUEST_EXPIRY << sr->count);
babel_send_seqno_request(p, e, sr);
}
else
{
TRACE(D_EVENTS, "Seqno request for %N router-id %lR expired",
e->n.addr, sr->router_id);
babel_remove_seqno_request(p, sr);
continue;
}
}
}
}
static struct babel_neighbor *
babel_find_neighbor(struct babel_iface *ifa, ip_addr addr)
{
@ -309,6 +416,7 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
nbr->txcost = BABEL_INFINITY;
nbr->cost = BABEL_INFINITY;
init_list(&nbr->routes);
babel_lock_neighbor(nbr);
add_tail(&ifa->neigh_list, NODE nbr);
return nbr;
@ -333,8 +441,9 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
babel_select_route(p, e);
}
nbr->ifa = NULL;
rem_node(NODE nbr);
mb_free(nbr);
babel_unlock_neighbor(nbr);
}
static void
@ -588,14 +697,14 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e)
(as unreachable), then send a seqno request.
babel_build_rte() will set the unreachable flag if the metric is BABEL_INFINITY.*/
if (e->selected_in)
if (best)
{
TRACE(D_EVENTS, "Lost feasible route for prefix %N", e->n.addr);
e->selected_in->metric = e->selected_in->advert_metric = BABEL_INFINITY;
best->metric = best->advert_metric = BABEL_INFINITY;
e->updated = current_time();
babel_send_seqno_request(p, e);
babel_add_seqno_request(p, e, best->router_id, best->seqno + 1, 0, NULL);
babel_announce_rte(p, e);
/* Section 3.6 of the RFC forbids an infeasible from being selected. This
@ -695,7 +804,6 @@ babel_send_hello(struct babel_iface *ifa)
static void
babel_send_route_request(struct babel_proto *p, struct babel_entry *e, struct babel_neighbor *n)
{
struct babel_iface *ifa = n->ifa;
union babel_msg msg = {};
TRACE(D_PACKETS, "Sending route request for %N to %I",
@ -704,7 +812,7 @@ babel_send_route_request(struct babel_proto *p, struct babel_entry *e, struct ba
msg.type = BABEL_TLV_ROUTE_REQUEST;
net_copy(&msg.route_request.net, e->n.addr);
babel_send_unicast(&msg, ifa, n->addr);
babel_send_unicast(&msg, n->ifa, n->addr);
}
static void
@ -723,48 +831,32 @@ babel_send_wildcard_request(struct babel_iface *ifa)
}
static void
babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e)
babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e, struct babel_seqno_request *sr)
{
struct babel_route *r = e->selected_in;
struct babel_iface *ifa = NULL;
struct babel_source *s = NULL;
union babel_msg msg = {};
s = babel_find_source(e, r->router_id);
if (!s || !babel_cache_seqno_request(p, e->n.addr, r->router_id, s->seqno + 1))
return;
TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
e->n.addr, r->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST;
msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = r->router_id;
msg.seqno_request.hop_count = sr->hop_count ?: BABEL_INITIAL_HOP_COUNT;
msg.seqno_request.seqno = sr->seqno;
msg.seqno_request.router_id = sr->router_id;
net_copy(&msg.seqno_request.net, e->n.addr);
if (sr->nbr)
{
TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d to %I on %s",
e->n.addr, sr->router_id, sr->seqno, sr->nbr->addr, sr->nbr->ifa->ifname);
babel_send_unicast(&msg, sr->nbr->ifa, sr->nbr->addr);
}
else
{
TRACE(D_PACKETS, "Sending broadcast seqno request for %N router-id %lR seqno %d",
e->n.addr, sr->router_id, sr->seqno);
struct babel_iface *ifa;
WALK_LIST(ifa, p->interfaces)
babel_enqueue(&msg, ifa);
}
static void
babel_unicast_seqno_request(struct babel_proto *p, struct babel_entry *e, struct babel_source *s, struct babel_neighbor *nbr)
{
union babel_msg msg = {};
if (!s || !babel_cache_seqno_request(p, e->n.addr, s->router_id, s->seqno + 1))
return;
TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
e->n.addr, s->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST;
msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = s->router_id;
net_copy(&msg.seqno_request.net, e->n.addr);
babel_send_unicast(&msg, nbr->ifa, nbr->addr);
}
}
/**
@ -966,81 +1058,6 @@ babel_update_hello_history(struct babel_neighbor *n, u16 seqno, uint interval)
n->last_hello_int = interval;
}
static void
babel_expire_seqno_requests(struct babel_proto *p)
{
btime now_ = current_time();
struct babel_seqno_request *n, *nx;
WALK_LIST_DELSAFE(n, nx, p->seqno_cache)
{
if ((n->updated + BABEL_SEQNO_REQUEST_EXPIRY) <= now_)
{
rem_node(NODE n);
sl_free(p->seqno_slab, n);
}
}
}
/*
* Checks the seqno request cache for a matching request and returns failure if
* found. Otherwise, a new entry is stored in the cache.
*/
static int
babel_cache_seqno_request(struct babel_proto *p, net_addr *n,
u64 router_id, u16 seqno)
{
struct babel_seqno_request *r;
WALK_LIST(r, p->seqno_cache)
{
if (net_equal(&r->net, n) && (r->router_id == router_id) && (r->seqno == seqno))
return 0;
}
/* no entries found */
r = sl_alloc(p->seqno_slab);
net_copy(&r->net, n);
r->router_id = router_id;
r->seqno = seqno;
r->updated = current_time();
add_tail(&p->seqno_cache, NODE r);
return 1;
}
static void
babel_forward_seqno_request(struct babel_proto *p, struct babel_entry *e,
struct babel_msg_seqno_request *in,
ip_addr sender)
{
struct babel_route *r;
TRACE(D_PACKETS, "Forwarding seqno request for %N router-id %lR seqno %d",
e->n.addr, in->router_id, in->seqno);
WALK_LIST(r, e->routes)
{
if ((r->router_id == in->router_id) &&
!OUR_ROUTE(r) &&
!ipa_equal(r->neigh->addr, sender))
{
if (!babel_cache_seqno_request(p, e->n.addr, in->router_id, in->seqno))
return;
union babel_msg msg = {};
msg.type = BABEL_TLV_SEQNO_REQUEST;
msg.seqno_request.hop_count = in->hop_count-1;
msg.seqno_request.seqno = in->seqno;
msg.seqno_request.router_id = in->router_id;
net_copy(&msg.seqno_request.net, e->n.addr);
babel_send_unicast(&msg, r->neigh->ifa, r->neigh->addr);
return;
}
}
}
/*
* TLV handlers
@ -1227,7 +1244,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
/* RFC section 3.8.2.2 - Dealing with unfeasible updates */
if (!feasible && (metric != BABEL_INFINITY) &&
(!best || (r == best) || (metric < best->metric)))
babel_unicast_seqno_request(p, e, s, nbr);
babel_add_seqno_request(p, e, s->router_id, s->seqno + 1, 0, nbr);
if (!r)
{
@ -1264,6 +1281,13 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
r->expires = current_time() + r->expiry_interval;
if (r->expiry_interval > BABEL_ROUTE_REFRESH_INTERVAL)
r->refresh_time = current_time() + r->expiry_interval - BABEL_ROUTE_REFRESH_INTERVAL;
/* If received update satisfies seqno request, we send triggered updates */
if (babel_satisfy_seqno_request(p, e, msg->router_id, msg->seqno))
{
babel_trigger_update(p);
e->updated = current_time();
}
}
babel_select_route(p, e);
@ -1301,7 +1325,6 @@ babel_handle_route_request(union babel_msg *m, struct babel_iface *ifa)
}
}
void
babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
{
@ -1335,11 +1358,31 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
p->update_seqno_inc = 1;
babel_trigger_update(p);
}
else
else if (msg->hop_count > 1)
{
/* Not ours; forward if TTL allows it */
if (msg->hop_count > 1)
babel_forward_seqno_request(p, e, msg, msg->sender);
/* Find best admissible route */
struct babel_route *r, *best1 = NULL, *best2 = NULL;
WALK_LIST(r, e->routes)
if ((r->router_id == msg->router_id) && r->neigh && !ipa_equal(r->neigh->addr, msg->sender))
{
/* Find best feasible route */
if (babel_is_feasible(babel_find_source(e, r->router_id), r->seqno, r->advert_metric) &&
(!best1 || r->metric < best1->metric))
best1 = r;
/* Find best not necessary feasible route */
if (!best2 || r->metric < best2->metric)
best2 = r;
}
/* If no route is found, do nothing */
r = best1 ?: best2;
if (!r)
return;
babel_add_seqno_request(p, e, msg->router_id, msg->seqno, msg->hop_count-1, r->neigh);
}
}
@ -1396,7 +1439,7 @@ babel_iface_timer(timer *t)
{
TRACE(D_EVENTS, "Sending triggered updates on %s", ifa->ifname);
babel_send_update(ifa, ifa->want_triggered);
ifa->next_triggered = now_ + MIN(5 S, update_period / 2);
ifa->next_triggered = now_ + MIN(1 S, update_period / 2);
ifa->want_triggered = 0;
p->triggered = 0;
}
@ -1422,7 +1465,7 @@ babel_iface_start(struct babel_iface *ifa)
ifa->next_hello = current_time() + (random() % ifa->cf->hello_interval);
ifa->next_regular = current_time() + (random() % ifa->cf->update_interval);
ifa->next_triggered = current_time() + MIN(5 S, ifa->cf->update_interval / 2);
ifa->next_triggered = current_time() + MIN(1 S, ifa->cf->update_interval / 2);
ifa->want_triggered = 0; /* We send an immediate update (below) */
tm2_start(ifa->timer, 100 MS);
ifa->up = 1;
@ -1988,7 +2031,6 @@ babel_timer(timer *t)
struct babel_proto *p = t->data;
babel_expire_routes(p);
babel_expire_seqno_requests(p);
babel_expire_neighbors(p);
}
@ -2177,7 +2219,6 @@ babel_start(struct proto *P)
p->source_slab = sl_new(P->pool, sizeof(struct babel_source));
p->msg_slab = sl_new(P->pool, sizeof(struct babel_msg_node));
p->seqno_slab = sl_new(P->pool, sizeof(struct babel_seqno_request));
init_list(&p->seqno_cache);
p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };

View File

@ -42,7 +42,8 @@
#define BABEL_ROUTE_EXPIRY_FACTOR(X) ((btime)(X)*7/2) /* 3.5 */
#define BABEL_ROUTE_REFRESH_INTERVAL (2 S_) /* Time before route expiry to send route request */
#define BABEL_HOLD_TIME (10 S_) /* Expiry time for our own routes */
#define BABEL_SEQNO_REQUEST_EXPIRY (60 S_)
#define BABEL_SEQNO_REQUEST_RETRY 4
#define BABEL_SEQNO_REQUEST_EXPIRY (2 S_)
#define BABEL_GARBAGE_INTERVAL (300 S_)
#define BABEL_RXCOST_WIRED 96
#define BABEL_RXCOST_WIRELESS 256
@ -147,9 +148,7 @@ struct babel_proto {
slab *route_slab;
slab *source_slab;
slab *msg_slab;
slab *seqno_slab;
list seqno_cache; /* Seqno requests in the cache (struct babel_seqno_request) */
struct tbf log_pkt_tbf; /* TBF for packet messages */
};
@ -190,6 +189,7 @@ struct babel_neighbor {
struct babel_iface *ifa;
ip_addr addr;
uint uc; /* Reference counter for seqno requests */
u16 rxcost; /* Sent in last IHU */
u16 txcost; /* Received in last IHU */
u16 cost; /* Computed neighbor cost */
@ -231,12 +231,23 @@ struct babel_route {
btime expiry_interval;
};
struct babel_seqno_request {
node n;
u64 router_id;
u16 seqno;
u8 hop_count;
u8 count;
btime expires;
struct babel_neighbor *nbr;
};
struct babel_entry {
struct babel_route *selected_in;
struct babel_route *selected_out;
btime updated;
list requests;
list sources; /* Source entries for this prefix (struct babel_source). */
list routes; /* Routes for this prefix (struct babel_route) */
@ -244,13 +255,6 @@ struct babel_entry {
};
/* Stores forwarded seqno requests for duplicate suppression. */
struct babel_seqno_request {
node n;
net_addr net;
u64 router_id;
u16 seqno;
btime updated;
};
/*