diff options
author | Matthieu Boutier <boutier@pps.jussieu.fr> | 2012-01-23 23:46:32 +0100 |
---|---|---|
committer | Paul Jakma <paul@quagga.net> | 2012-03-25 17:06:53 +0100 |
commit | c35fafdf887aa32c5be6ad738d3a3b0140cea6e8 (patch) | |
tree | 4aa21a41dcd82247e467e5b955a6f7813bfd7ba7 | |
parent | 16e51b246be6b18641327685f44bd4f5f6649367 (diff) |
babeld: babelz merge.
Babelz is the last version of the stand-alone babel daemon. In
particular, it use multiple channels to diminuate
interferences. Please refer to this one for more details.
-rw-r--r-- | babeld/babel_interface.c | 72 | ||||
-rw-r--r-- | babeld/babel_interface.h | 19 | ||||
-rw-r--r-- | babeld/babel_main.c | 2 | ||||
-rw-r--r-- | babeld/babeld.c | 2 | ||||
-rw-r--r-- | babeld/message.c | 202 | ||||
-rw-r--r-- | babeld/neighbour.c | 5 | ||||
-rw-r--r-- | babeld/net.c | 10 | ||||
-rw-r--r-- | babeld/route.c | 574 | ||||
-rw-r--r-- | babeld/route.h | 50 | ||||
-rw-r--r-- | babeld/source.c | 59 | ||||
-rw-r--r-- | babeld/source.h | 7 | ||||
-rw-r--r-- | babeld/util.c | 68 | ||||
-rw-r--r-- | babeld/util.h | 4 | ||||
-rw-r--r-- | babeld/xroute.c | 27 | ||||
-rw-r--r-- | babeld/xroute.h | 8 |
15 files changed, 792 insertions, 317 deletions
diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c index e403cce0..1c8c8868 100644 --- a/babeld/babel_interface.c +++ b/babeld/babel_interface.c @@ -456,6 +456,30 @@ interface_idle(babel_interface_nfo *babel_ifp) babel_ifp->activity_time < babel_now.tv_sec - idle_time); } +int +update_hello_interval(struct interface *ifp) +{ + int rc = 0; + unsigned short interval; + struct babel_interface *babel_ifp = babel_get_if_nfo(ifp); + + if(interface_idle(babel_ifp)) + interval = idle_hello_interval; + else if(IF_CONF(ifp, hello_interval) > 0) + interval = IF_CONF(ifp, hello_interval); + else if((ifp->flags & BABEL_IF_WIRED)) + interval = wired_hello_interval; + else + interval = wireless_hello_interval; + + if(babel_ifp->hello_interval != interval) { + babel_ifp->hello_interval = interval; + rc = 1; + } + + return rc; +} + /* This should be no more than half the hello interval, so that hellos aren't sent late. The result is in milliseconds. */ unsigned @@ -560,10 +584,11 @@ interface_recalculate(struct interface *ifp) update_interface_metric(ifp); debugf(BABEL_DEBUG_COMMON, - "Upped network %s (%s, cost=%d%s).", + "Upped interface %s (%s, cost=%d, channel=%d%s).", ifp->name, (babel_ifp->flags & BABEL_IF_WIRED) ? "wired" : "wireless", babel_ifp->cost, + babel_ifp->channel, babel_ifp->ipv4 ? ", IPv4" : ""); if(rc > 0) @@ -777,19 +802,41 @@ DEFUN (show_babel_neighbour, } static void -show_babel_routes_sub (struct vty *vty, struct babel_route *route) +show_babel_routes_sub (struct babel_route *route, void *closure) { + struct vty *vty = (struct vty*) closure; const unsigned char *nexthop = memcmp(route->nexthop, route->neigh->address, 16) == 0 ? NULL : route->nexthop; + char channels[100]; + + if(route->channels[0] == 0) + channels[0] = '\0'; + else { + int k, j = 0; + snprintf(channels, 100, " chan ("); + j = strlen(channels); + for(k = 0; k < DIVERSITY_HOPS; k++) { + if(route->channels[k] == 0) + break; + if(k > 0) + channels[j++] = ','; + snprintf(channels + j, 100 - j, "%d", route->channels[k]); + j = strlen(channels); + } + snprintf(channels + j, 100 - j, ")"); + if(k == 0) + channels[0] = '\0'; + } vty_out(vty, - "%s metric %d refmetric %d id %s seqno %d age %d " + "%s metric %d refmetric %d id %s seqno %d%s age %d " "via %s neigh %s%s%s%s%s", format_prefix(route->src->prefix, route->src->plen), route_metric(route), route->refmetric, format_eui64(route->src->id), (int)route->seqno, + channels, (int)(babel_now.tv_sec - route->time), route->neigh->ifp->name, format_address(route->neigh->address), @@ -801,11 +848,12 @@ show_babel_routes_sub (struct vty *vty, struct babel_route *route) } static void -show_babel_xroutes_sub (struct vty *vty, struct xroute *xroute) +show_babel_xroutes_sub (struct xroute *xroute, void *closure) { + struct vty *vty = (struct vty *) closure; vty_out(vty, "%s metric %d (exported)%s", - format_prefix(xroutes->prefix, xroute->plen), - xroutes->metric, + format_prefix(xroute->prefix, xroute->plen), + xroute->metric, VTY_NEWLINE); } @@ -818,15 +866,8 @@ DEFUN (show_babel_database, "Database information\n" "No attributes\n") { - int i; - - for(i = 0; i < numroutes; i++) { - show_babel_routes_sub(vty, &routes[i]); - } - for(i = 0; i < numxroutes; i++) { - show_babel_xroutes_sub(vty, &xroutes[i]); - } - + for_all_routes(show_babel_routes_sub, vty); + for_all_xroutes(show_babel_xroutes_sub, vty); return CMD_SUCCESS; } @@ -950,6 +991,7 @@ babel_interface_allocate (void) babel_ifp->bucket = BUCKET_TOKENS_MAX; babel_ifp->hello_seqno = (random() & 0xFFFF); babel_ifp->hello_interval = BABELD_DEFAULT_HELLO_INTERVAL; + babel_ifp->channel = BABEL_IF_CHANNEL_INTERFERING; return babel_ifp; } diff --git a/babeld/babel_interface.h b/babeld/babel_interface.h index d9fb9a4a..5b551fbe 100644 --- a/babeld/babel_interface.h +++ b/babeld/babel_interface.h @@ -42,10 +42,15 @@ THE SOFTWARE. #include <zebra.h> #include "zclient.h" +#define CONFIG_DEFAULT 0 +#define CONFIG_NO 1 +#define CONFIG_YES 2 + /* babeld interface informations */ struct babel_interface { unsigned short flags; /* see below */ unsigned short cost; + int channel; struct timeval hello_timeout; struct timeval update_timeout; struct timeval flush_timeout; @@ -67,6 +72,7 @@ struct babel_interface { time_t bucket_time; unsigned int bucket; time_t activity_time; + time_t last_update_time; unsigned short hello_seqno; unsigned hello_interval; unsigned update_interval; @@ -85,12 +91,20 @@ static inline babel_interface_nfo* babel_get_if_nfo(struct interface *ifp) return ((babel_interface_nfo*) ifp->info); } +#define IF_CONF(_ifp, _field) babel_get_if_nfo(_ifp)->_field + /* babel_interface_nfo flags */ +#define BABEL_IF_IS_UP (1 << 0) #define BABEL_IF_WIRED (1 << 1) #define BABEL_IF_SPLIT_HORIZON (1 << 2) #define BABEL_IF_LQ (1 << 3) -#define BABEL_IF_IS_ENABLE (1 << 4) -#define BABEL_IF_IS_UP (1 << 5) +#define BABEL_IF_FARAWAY (1 << 4) +#define BABEL_IF_IS_ENABLE (1 << 7) + +/* Only INTERFERING can appear on the wire. */ +#define BABEL_IF_CHANNEL_UNKNOWN 0 +#define BABEL_IF_CHANNEL_INTERFERING 255 +#define BABEL_IF_CHANNEL_NONINTERFERING -2 static inline int if_up(struct interface *ifp) @@ -133,6 +147,7 @@ int babel_interface_address_delete (int, struct zclient *, zebra_size_t); /* others functions */ int interface_idle(babel_interface_nfo *); +int update_hello_interval(struct interface *ifp); unsigned jitter(babel_interface_nfo *, int); unsigned update_jitter(babel_interface_nfo *babel_ifp, int urgent); /* return "true" if "address" is one of our ipv6 addresses */ diff --git a/babeld/babel_main.c b/babeld/babel_main.c index 159ba656..4d6f60eb 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -482,7 +482,7 @@ babel_exit_properly(void) /* Uninstall and flush all routes. */ debugf(BABEL_DEBUG_COMMON, "Uninstall routes."); - babel_uninstall_all_routes(); + flush_all_routes(); babel_interface_close_all(); babel_zebra_close_connexion(); babel_save_state_file(); diff --git a/babeld/babeld.c b/babeld/babeld.c index 0cf8c8a1..d69662c0 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -269,7 +269,7 @@ babel_initial_noise(void) static void babel_clean_routing_process() { - babel_uninstall_all_routes(); + flush_all_routes(); babel_interface_close_all(); /* cancel threads */ diff --git a/babeld/message.c b/babeld/message.c index 92c416b9..8cd1db63 100644 --- a/babeld/message.c +++ b/babeld/message.c @@ -69,6 +69,8 @@ struct timeval unicast_flush_timeout = {0, 0}; static const unsigned char v4prefix[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 }; +/* Parse a network prefix, encoded in the somewhat baroque compressed + representation used by Babel. Return the number of bytes parsed. */ static int network_prefix(int ae, int plen, unsigned int omitted, const unsigned char *p, const unsigned char *dp, @@ -76,6 +78,7 @@ network_prefix(int ae, int plen, unsigned int omitted, { unsigned pb; unsigned char prefix[16]; + int ret = -1; if(plen >= 0) pb = (plen + 7) / 8; @@ -90,7 +93,9 @@ network_prefix(int ae, int plen, unsigned int omitted, memset(prefix, 0, 16); switch(ae) { - case 0: break; + case 0: + ret = 0; + break; case 1: if(omitted > 4 || pb > 4 || (pb > omitted && len < pb - omitted)) return -1; @@ -100,6 +105,7 @@ network_prefix(int ae, int plen, unsigned int omitted, memcpy(prefix, dp, 12 + omitted); } if(pb > omitted) memcpy(prefix + 12 + omitted, p, pb - omitted); + ret = pb - omitted; break; case 2: if(omitted > 16 || (pb > omitted && len < pb - omitted)) return -1; @@ -108,19 +114,68 @@ network_prefix(int ae, int plen, unsigned int omitted, memcpy(prefix, dp, omitted); } if(pb > omitted) memcpy(prefix + omitted, p, pb - omitted); + ret = pb - omitted; break; case 3: if(pb > 8 && len < pb - 8) return -1; prefix[0] = 0xfe; prefix[1] = 0x80; if(pb > 8) memcpy(prefix + 8, p, pb - 8); + ret = pb - 8; break; default: return -1; } mask_prefix(p_r, prefix, plen < 0 ? 128 : ae == 1 ? plen + 96 : plen); - return 1; + return ret; +} + +static void +parse_route_attributes(const unsigned char *a, int alen, + unsigned char *channels) +{ + int type, len, i = 0; + + while(i < alen) { + type = a[i]; + if(type == 0) { + i++; + continue; + } + + if(i + 1 > alen) { + fprintf(stderr, "Received truncated attributes.\n"); + return; + } + len = a[i + 1]; + if(i + len > alen) { + fprintf(stderr, "Received truncated attributes.\n"); + return; + } + + if(type == 1) { + /* Nothing. */ + } else if(type == 2) { + if(len > DIVERSITY_HOPS) { + fprintf(stderr, + "Received overlong channel information (%d > %d).\n", + len, DIVERSITY_HOPS); + len = DIVERSITY_HOPS; + } + if(memchr(a + i + 2, 0, len) != NULL) { + /* 0 is reserved. */ + fprintf(stderr, "Channel information contains 0!"); + return; + } + memset(channels, 0, DIVERSITY_HOPS); + memcpy(channels, a + i + 2, len); + } else { + fprintf(stderr, "Received unknown route attribute %d.\n", type); + } + + i += len + 2; + } } static int @@ -130,6 +185,13 @@ network_address(int ae, const unsigned char *a, unsigned int len, return network_prefix(ae, -1, 0, a, NULL, len, a_r); } +static int +channels_len(unsigned char *channels) +{ + unsigned char *p = memchr(channels, 0, DIVERSITY_HOPS); + return p ? (p - channels) : DIVERSITY_HOPS; +} + void parse_packet(const unsigned char *from, struct interface *ifp, const unsigned char *packet, int packetlen) @@ -284,8 +346,9 @@ parse_packet(const unsigned char *from, struct interface *ifp, } else if(type == MESSAGE_UPDATE) { unsigned char prefix[16], *nh; unsigned char plen; + unsigned char channels[DIVERSITY_HOPS]; unsigned short interval, seqno, metric; - int rc; + int rc, parsed_len; if(len < 10) { if(len < 2 || message[3] & 0x80) have_v4_prefix = have_v6_prefix = 0; @@ -307,6 +370,7 @@ parse_packet(const unsigned char *from, struct interface *ifp, have_v4_prefix = have_v6_prefix = 0; goto fail; } + parsed_len = 10 + rc; plen = message[4] + (message[2] == 1 ? 96 : 0); @@ -360,8 +424,27 @@ parse_packet(const unsigned char *from, struct interface *ifp, goto done; } + if((ifp->flags & BABEL_IF_FARAWAY)) { + channels[0] = 0; + } else { + /* This will be overwritten by parse_route_attributes below. */ + if(metric < 256) { + /* Assume non-interfering (wired) link. */ + channels[0] = 0; + } else { + /* Assume interfering. */ + channels[0] = BABEL_IF_CHANNEL_INTERFERING; + channels[1] = 0; + } + + if(parsed_len < len) + parse_route_attributes(message + 2 + parsed_len, + len - parsed_len, channels); + } + update_route(router_id, prefix, plen, seqno, metric, interval, - neigh, nh); + neigh, nh, + channels, channels_len(channels)); } else if(type == MESSAGE_REQUEST) { unsigned char prefix[16], plen; int rc; @@ -374,10 +457,17 @@ parse_packet(const unsigned char *from, struct interface *ifp, message[2] == 0 ? "any" : format_prefix(prefix, plen), format_address(from), ifp->name); if(message[2] == 0) { + struct babel_interface *babel_ifp =babel_get_if_nfo(neigh->ifp); /* If a neighbour is requesting a full route dump from us, we might as well send it an IHU. */ send_ihu(neigh, NULL); - send_update(neigh->ifp, 0, NULL, 0); + /* Since nodes send wildcard requests on boot, booting + a large number of nodes at the same time may cause an + update storm. Ignore a wildcard request that happens + shortly after we sent a full update. */ + if(babel_ifp->last_update_time < + babel_now.tv_sec - MAX(babel_ifp->hello_interval / 100, 1)) + send_update(neigh->ifp, 0, NULL, 0); } else { send_update(neigh->ifp, 0, prefix, plen); } @@ -665,10 +755,12 @@ send_hello_noupdate(struct interface *ifp, unsigned interval) void send_hello(struct interface *ifp) { + int changed; + changed = update_hello_interval(ifp); babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); send_hello_noupdate(ifp, (babel_ifp->hello_interval + 9) / 10); /* Send full IHU every 3 hellos, and marginal IHU each time */ - if(babel_ifp->hello_seqno % 3 == 0) + if(changed || babel_ifp->hello_seqno % 3 == 0) send_ihu(NULL, ifp); else send_marginal_ihu(ifp); @@ -724,12 +816,19 @@ static void really_send_update(struct interface *ifp, const unsigned char *id, const unsigned char *prefix, unsigned char plen, - unsigned short seqno, unsigned short metric) + unsigned short seqno, unsigned short metric, + unsigned char *channels, int channels_len) { babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); int add_metric, v4, real_plen, omit = 0; const unsigned char *real_prefix; unsigned short flags = 0; + int channels_size; + + if(diversity_kind != DIVERSITY_CHANNEL) + channels_len = -1; + + channels_size = channels_len >= 0 ? channels_len + 2 : 0; if(!if_up(ifp)) return; @@ -786,7 +885,8 @@ really_send_update(struct interface *ifp, babel_ifp->have_buffered_id = 1; } - start_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit); + start_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + + channels_size); accumulate_byte(ifp, v4 ? 1 : 2); accumulate_byte(ifp, flags); accumulate_byte(ifp, real_plen); @@ -795,7 +895,14 @@ really_send_update(struct interface *ifp, accumulate_short(ifp, seqno); accumulate_short(ifp, metric); accumulate_bytes(ifp, real_prefix + omit, (real_plen + 7) / 8 - omit); - end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit); + /* Note that an empty channels TLV is different from no such TLV. */ + if(channels_len >= 0) { + accumulate_byte(ifp, 2); + accumulate_byte(ifp, channels_len); + accumulate_bytes(ifp, channels, channels_len); + } + end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + + channels_size); if(flags & 0x80) { memcpy(babel_ifp->buffered_prefix, prefix, 16); @@ -884,9 +991,6 @@ flushupdates(struct interface *ifp) qsort(b, n, sizeof(struct buffered_update), compare_buffered_updates); for(i = 0; i < n; i++) { - unsigned short seqno; - unsigned short metric; - /* The same update may be scheduled multiple times before it is sent out. Since our buffer is now sorted, it is enough to compare with the previous update. */ @@ -903,22 +1007,51 @@ flushupdates(struct interface *ifp) if(xroute && (!route || xroute->metric <= kernel_metric)) { really_send_update(ifp, myid, xroute->prefix, xroute->plen, - myseqno, xroute->metric); + myseqno, xroute->metric, + NULL, 0); last_prefix = xroute->prefix; last_plen = xroute->plen; } else if(route) { + unsigned char channels[DIVERSITY_HOPS]; + int chlen; + struct interface *route_ifp = route->neigh->ifp; + struct babel_interface *babel_route_ifp = NULL; + unsigned short metric; + unsigned short seqno; + seqno = route->seqno; - metric = route_metric(route); + metric = + route_interferes(route, ifp) ? + route_metric(route) : + route_metric_noninterfering(route); + if(metric < INFINITY) satisfy_request(route->src->prefix, route->src->plen, seqno, route->src->id, ifp); if((babel_ifp->flags & BABEL_IF_SPLIT_HORIZON) && route->neigh->ifp == ifp) continue; + + babel_route_ifp = babel_get_if_nfo(route_ifp); + if(babel_route_ifp->channel ==BABEL_IF_CHANNEL_NONINTERFERING) { + memcpy(channels, route->channels, DIVERSITY_HOPS); + } else { + if(babel_route_ifp->channel == BABEL_IF_CHANNEL_UNKNOWN) + channels[0] = BABEL_IF_CHANNEL_INTERFERING; + else { + assert(babel_route_ifp->channel > 0 && + babel_route_ifp->channel <= 255); + channels[0] = babel_route_ifp->channel; + } + memcpy(channels + 1, route->channels, DIVERSITY_HOPS - 1); + } + + chlen = channels_len(channels); really_send_update(ifp, route->src->id, route->src->prefix, route->src->plen, - seqno, metric); + seqno, metric, + channels, chlen); update_source(route->src, seqno, metric); last_prefix = route->src->prefix; last_plen = route->src->plen; @@ -926,7 +1059,7 @@ flushupdates(struct interface *ifp) /* There's no route for this prefix. This can happen shortly after an xroute has been retracted, so send a retraction. */ really_send_update(ifp, myid, b[i].prefix, b[i].plen, - myseqno, INFINITY); + myseqno, INFINITY, NULL, -1); } } schedule_flush_now(ifp); @@ -961,12 +1094,17 @@ buffer_update(struct interface *ifp, if(babel_ifp->update_bufsize == 0) { int n; assert(babel_ifp->buffered_updates == NULL); - n = MAX(babel_ifp->bufsize / 16, 4); + /* Allocate enough space to hold a full update. Since the + number of installed routes will grow over time, make sure we + have enough space to send a full-ish frame. */ + n = installed_routes_estimate() + xroutes_estimate() + 4; + n = MAX(n, babel_ifp->bufsize / 16); again: babel_ifp->buffered_updates = malloc(n *sizeof(struct buffered_update)); if(babel_ifp->buffered_updates == NULL) { zlog_err("malloc(buffered_updates): %s", safe_strerror(errno)); if(n > 4) { + /* Try again with a tiny buffer. */ n = 4; goto again; } @@ -982,12 +1120,18 @@ buffer_update(struct interface *ifp, babel_ifp->num_buffered_updates++; } +static void +buffer_update_callback(struct babel_route *route, void *closure) +{ + buffer_update((struct interface*)closure, + route->src->prefix, route->src->plen); +} + void send_update(struct interface *ifp, int urgent, const unsigned char *prefix, unsigned char plen) { babel_interface_nfo *babel_ifp = NULL; - int i; if(ifp == NULL) { struct interface *ifp_aux; @@ -1020,15 +1164,13 @@ send_update(struct interface *ifp, int urgent, if(!interface_idle(babel_ifp)) { send_self_update(ifp); if(!parasitic) { - debugf(BABEL_DEBUG_COMMON,"Sending update to %s for any.", ifp->name); - for(i = 0; i < numroutes; i++) - if(routes[i].installed) - buffer_update(ifp, - routes[i].src->prefix, - routes[i].src->plen); + debugf(BABEL_DEBUG_COMMON,"Sending update to %s for any.", + ifp->name); + for_all_installed_routes(buffer_update_callback, ifp); } } set_timeout(&babel_ifp->update_timeout, babel_ifp->update_interval); + babel_ifp->last_update_time = babel_now.tv_sec; } schedule_update_flush(ifp, urgent); } @@ -1086,11 +1228,16 @@ update_myseqno() seqno_time = babel_now; } +static void +send_xroute_update_callback(struct xroute *xroute, void *closure) +{ + struct interface *ifp = (struct interface*)closure; + send_update(ifp, 0, xroute->prefix, xroute->plen); +} + void send_self_update(struct interface *ifp) { - int i; - if(ifp == NULL) { struct interface *ifp_aux; struct listnode *linklist_node = NULL; @@ -1104,8 +1251,7 @@ send_self_update(struct interface *ifp) if(!interface_idle(babel_get_if_nfo(ifp))) { debugf(BABEL_DEBUG_COMMON,"Sending self update to %s.", ifp->name); - for(i = 0; i < numxroutes; i++) - send_update(ifp, 0, xroutes[i].prefix, xroutes[i].plen); + for_all_xroutes(send_xroute_update_callback, ifp); } } diff --git a/babeld/neighbour.c b/babeld/neighbour.c index f360b891..5a327dfe 100644 --- a/babeld/neighbour.c +++ b/babeld/neighbour.c @@ -122,7 +122,8 @@ find_neighbour(const unsigned char *address, struct interface *ifp) return neigh; } -/* Recompute a neighbour's rxcost. Return true if anything changed. */ +/* Recompute a neighbour's rxcost. Return true if anything changed. + This does not call local_notify_neighbour, see update_neighbour_metric. */ int update_neighbour(struct neighbour *neigh, int hello, int hello_interval) { @@ -327,7 +328,7 @@ neighbour_cost(struct neighbour *neigh) return INFINITY; if(!(babel_get_if_nfo(neigh->ifp)->flags & BABEL_IF_LQ) - || (a <= 256 && b <= 256)) { + || (a < 256 && b < 256)) { return a; } else { /* a = 256/alpha, b = 256/beta, where alpha and beta are the expected diff --git a/babeld/net.c b/babeld/net.c index eaed51d2..5e0200b0 100644 --- a/babeld/net.c +++ b/babeld/net.c @@ -58,6 +58,7 @@ babel_socket(int port) int s, rc; int saved_errno; int one = 1, zero = 0; + const int ds = 0xc0; /* CS6 - Network Control */ s = socket(PF_INET6, SOCK_DGRAM, 0); if(s < 0) @@ -86,6 +87,15 @@ babel_socket(int port) if(rc < 0) goto fail; +#ifdef IPV6_TCLASS + rc = setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &ds, sizeof(ds)); +#else + rc = -1; + errno = ENOSYS; +#endif + if(rc < 0) + perror("Couldn't set traffic class"); + rc = fcntl(s, F_GETFL, 0); if(rc < 0) goto fail; diff --git a/babeld/route.c b/babeld/route.c index aadf80f2..a9ffc5d9 100644 --- a/babeld/route.c +++ b/babeld/route.c @@ -53,36 +53,161 @@ THE SOFTWARE. static void consider_route(struct babel_route *route); -struct babel_route *routes = NULL; -int numroutes = 0, maxroutes = 0; +struct babel_route **routes = NULL; +static int route_slots = 0, max_route_slots = 0; int kernel_metric = 0; int allow_duplicates = -1; +int diversity_kind = DIVERSITY_NONE; +int diversity_factor = 256; /* in units of 1/256 */ +int keep_unfeasible = 0; + +/* We maintain a list of "slots", ordered by prefix. Every slot + contains a linked list of the routes to this prefix, with the + installed route, if any, at the head of the list. */ + +static int +route_compare(const unsigned char *prefix, unsigned char plen, + struct babel_route *route) +{ + int i = memcmp(prefix, route->src->prefix, 16); + if(i != 0) + return i; + + if(plen < route->src->plen) + return -1; + else if(plen > route->src->plen) + return 1; + else + return 0; +} + +/* Performs binary search, returns -1 in case of failure. In the latter + case, new_return is the place where to insert the new element. */ + +static int +find_route_slot(const unsigned char *prefix, unsigned char plen, + int *new_return) +{ + int p, m, g, c; + + if(route_slots < 1) { + if(new_return) + *new_return = 0; + return -1; + } + + p = 0; g = route_slots - 1; + + do { + m = (p + g) / 2; + c = route_compare(prefix, plen, routes[m]); + if(c == 0) + return m; + else if(c < 0) + g = m - 1; + else + p = m + 1; + } while(p <= g); + + if(new_return) + *new_return = p; + + return -1; +} struct babel_route * find_route(const unsigned char *prefix, unsigned char plen, struct neighbour *neigh, const unsigned char *nexthop) { - int i; - for(i = 0; i < numroutes; i++) { - if(routes[i].neigh == neigh && - memcmp(routes[i].nexthop, nexthop, 16) == 0 && - source_match(routes[i].src, prefix, plen)) - return &routes[i]; + struct babel_route *route; + int i = find_route_slot(prefix, plen, NULL); + + if(i < 0) + return NULL; + + route = routes[i]; + + while(route) { + if(route->neigh == neigh && memcmp(route->nexthop, nexthop, 16) == 0) + return route; + route = route->next; } + return NULL; } struct babel_route * find_installed_route(const unsigned char *prefix, unsigned char plen) { - int i; - for(i = 0; i < numroutes; i++) { - if(routes[i].installed && source_match(routes[i].src, prefix, plen)) - return &routes[i]; - } + int i = find_route_slot(prefix, plen, NULL); + + if(i >= 0 && routes[i]->installed) + return routes[i]; + return NULL; } +/* Returns an overestimate of the number of installed routes. */ +int +installed_routes_estimate(void) +{ + return route_slots; +} + +static int +resize_route_table(int new_slots) +{ + struct babel_route **new_routes; + assert(new_slots >= route_slots); + + if(new_slots == 0) { + new_routes = NULL; + free(routes); + } else { + new_routes = realloc(routes, new_slots * sizeof(struct babel_route*)); + if(new_routes == NULL) + return -1; + } + + max_route_slots = new_slots; + routes = new_routes; + return 1; +} + +/* Insert a route into the table. If successful, retains the route. + On failure, caller must free the route. */ +static struct babel_route * +insert_route(struct babel_route *route) +{ + int i, n; + + assert(!route->installed); + + i = find_route_slot(route->src->prefix, route->src->plen, &n); + + if(i < 0) { + if(route_slots >= max_route_slots) + resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots); + if(route_slots >= max_route_slots) + return NULL; + route->next = NULL; + if(n < route_slots) + memmove(routes + n + 1, routes + n, + (route_slots - n) * sizeof(struct babel_route*)); + route_slots++; + routes[n] = route; + } else { + struct babel_route *r; + r = routes[i]; + while(r->next) + r = r->next; + r->next = route; + route->next = NULL; + } + + return route; +} + void flush_route(struct babel_route *route) { @@ -91,39 +216,67 @@ flush_route(struct babel_route *route) unsigned oldmetric; int lost = 0; - i = route - routes; - assert(i >= 0 && i < numroutes); - oldmetric = route_metric(route); + src = route->src; if(route->installed) { uninstall_route(route); lost = 1; } - src = route->src; + i = find_route_slot(route->src->prefix, route->src->plen, NULL); + assert(i >= 0 && i < route_slots); - if(i != numroutes - 1) - memcpy(routes + i, routes + numroutes - 1, sizeof(struct babel_route)); - numroutes--; - VALGRIND_MAKE_MEM_UNDEFINED(routes + numroutes, sizeof(struct babel_route)); + if(route == routes[i]) { + routes[i] = route->next; + route->next = NULL; + free(route); - if(numroutes == 0) { - free(routes); - routes = NULL; - maxroutes = 0; - } else if(maxroutes > 8 && numroutes < maxroutes / 4) { - struct babel_route *new_routes; - int n = maxroutes / 2; - new_routes = realloc(routes, n * sizeof(struct babel_route)); - if(new_routes != NULL) { - routes = new_routes; - maxroutes = n; + if(routes[i] == NULL) { + if(i < route_slots - 1) + memmove(routes + i, routes + i + 1, + (route_slots - i - 1) * sizeof(struct babel_route*)); + routes[route_slots - 1] = NULL; + route_slots--; } + + if(route_slots == 0) + resize_route_table(0); + else if(max_route_slots > 8 && route_slots < max_route_slots / 4) + resize_route_table(max_route_slots / 2); + } else { + struct babel_route *r = routes[i]; + while(r->next != route) + r = r->next; + r->next = route->next; + route->next = NULL; + free(route); } if(lost) route_lost(src, oldmetric); + + release_source(src); +} + +void +flush_all_routes() +{ + int i; + + /* Start from the end, to avoid shifting the table. */ + i = route_slots - 1; + while(i >= 0) { + while(i < route_slots) { + /* Uninstall first, to avoid calling route_lost. */ + if(routes[i]->installed) + uninstall_route(routes[0]); + flush_route(routes[i]); + } + i--; + } + + check_sources_released(); } void @@ -132,12 +285,19 @@ flush_neighbour_routes(struct neighbour *neigh) int i; i = 0; - while(i < numroutes) { - if(routes[i].neigh == neigh) { - flush_route(&routes[i]); - continue; + while(i < route_slots) { + struct babel_route *r; + r = routes[i]; + while(r) { + if(r->neigh == neigh) { + flush_route(r); + goto again; + } + r = r->next; } i++; + again: + ; } } @@ -147,13 +307,46 @@ flush_interface_routes(struct interface *ifp, int v4only) int i; i = 0; - while(i < numroutes) { - if(routes[i].neigh->ifp == ifp && - (!v4only || v4mapped(routes[i].nexthop))) { - flush_route(&routes[i]); - continue; + while(i < route_slots) { + struct babel_route *r; + r = routes[i]; + while(r) { + if(r->neigh->ifp == ifp && + (!v4only || v4mapped(r->nexthop))) { + flush_route(r); + goto again; + } + r = r->next; } i++; + again: + ; + } +} + +/* Iterate a function over all routes. */ +void +for_all_routes(void (*f)(struct babel_route*, void*), void *closure) +{ + int i; + + for(i = 0; i < route_slots; i++) { + struct babel_route *r = routes[i]; + while(r) { + (*f)(r, closure); + r = r->next; + } + } +} + +void +for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure) +{ + int i; + + for(i = 0; i < route_slots; i++) { + if(routes[i]->installed) + (*f)(routes[i], closure); } } @@ -163,10 +356,28 @@ metric_to_kernel(int metric) return metric < INFINITY ? kernel_metric : KERNEL_INFINITY; } +/* This is used to maintain the invariant that the installed route is at + the head of the list. */ +static void +move_installed_route(struct babel_route *route, int i) +{ + assert(i >= 0 && i < route_slots); + assert(route->installed); + + if(route != routes[i]) { + struct babel_route *r = routes[i]; + while(r->next != route) + r = r->next; + r->next = route->next; + route->next = routes[i]; + routes[i] = route; + } +} + void install_route(struct babel_route *route) { - int rc; + int i, rc; if(route->installed) return; @@ -175,6 +386,15 @@ install_route(struct babel_route *route) zlog_err("WARNING: installing unfeasible route " "(this shouldn't happen)."); + i = find_route_slot(route->src->prefix, route->src->plen, NULL); + assert(i >= 0 && i < route_slots); + + if(routes[i] != route && routes[i]->installed) { + fprintf(stderr, "WARNING: attempting to install duplicate route " + "(this shouldn't happen)."); + return; + } + rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen, route->nexthop, route->neigh->ifp->ifindex, @@ -186,6 +406,8 @@ install_route(struct babel_route *route) return; } route->installed = 1; + move_installed_route(route, i); + } void @@ -239,15 +461,16 @@ switch_routes(struct babel_route *old, struct babel_route *new) old->installed = 0; new->installed = 1; + move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen, + NULL)); } static void -change_route_metric(struct babel_route *route, unsigned newmetric) +change_route_metric(struct babel_route *route, + unsigned refmetric, unsigned cost, unsigned add) { int old, new; - - if(route_metric(route) == newmetric) - return; + int newmetric = MIN(refmetric + cost + add, INFINITY); old = metric_to_kernel(route_metric(route)); new = metric_to_kernel(newmetric); @@ -265,14 +488,15 @@ change_route_metric(struct babel_route *route, unsigned newmetric) } } - route->metric = newmetric; + route->refmetric = refmetric; + route->cost = cost; + route->add_metric = add; } static void retract_route(struct babel_route *route) { - route->refmetric = INFINITY; - change_route_metric(route, INFINITY); + change_route_metric(route, INFINITY, INFINITY, 0); } int @@ -293,6 +517,51 @@ route_expired(struct babel_route *route) return route->time < babel_now.tv_sec - route->hold_time; } +static int +channels_interfere(int ch1, int ch2) +{ + if(ch1 == BABEL_IF_CHANNEL_NONINTERFERING + || ch2 == BABEL_IF_CHANNEL_NONINTERFERING) + return 0; + if(ch1 == BABEL_IF_CHANNEL_INTERFERING + || ch2 == BABEL_IF_CHANNEL_INTERFERING) + return 1; + return ch1 == ch2; +} + +int +route_interferes(struct babel_route *route, struct interface *ifp) +{ + struct babel_interface *babel_ifp = NULL; + switch(diversity_kind) { + case DIVERSITY_NONE: + return 1; + case DIVERSITY_INTERFACE_1: + return route->neigh->ifp == ifp; + case DIVERSITY_CHANNEL_1: + case DIVERSITY_CHANNEL: + if(route->neigh->ifp == ifp) + return 1; + babel_ifp = babel_get_if_nfo(ifp); + if(channels_interfere(babel_ifp->channel, + babel_get_if_nfo(route->neigh->ifp)->channel)) + return 1; + if(diversity_kind == DIVERSITY_CHANNEL) { + int i; + for(i = 0; i < DIVERSITY_HOPS; i++) { + if(route->channels[i] == 0) + break; + if(channels_interfere(babel_ifp->channel, route->channels[i])) + return 1; + } + } + return 0; + default: + fprintf(stderr, "Unknown kind of diversity.\n"); + return 1; + } +} + int update_feasible(struct source *src, unsigned short seqno, unsigned short refmetric) @@ -317,21 +586,22 @@ struct babel_route * find_best_route(const unsigned char *prefix, unsigned char plen, int feasible, struct neighbour *exclude) { - struct babel_route *route = NULL; - int i; + struct babel_route *route = NULL, *r = NULL; + int i = find_route_slot(prefix, plen, NULL); + + if(i < 0) + return NULL; + + route = routes[i]; - for(i = 0; i < numroutes; i++) { - if(!source_match(routes[i].src, prefix, plen)) - continue; - if(route_expired(&routes[i])) - continue; - if(feasible && !route_feasible(&routes[i])) - continue; - if(exclude && routes[i].neigh == exclude) - continue; - if(route && route_metric(route) <= route_metric(&routes[i])) - continue; - route = &routes[i]; + r = route->next; + while(r) { + if(!route_expired(r) && + (!feasible || route_feasible(r)) && + (!exclude || r->neigh != exclude) && + (route_metric(r) < route_metric(route))) + route = r; + r = r->next; } return route; } @@ -354,31 +624,29 @@ update_route_metric(struct babel_route *route) route->src->prefix, route->src->plen, neigh->address, neigh->ifp->ifindex); - int newmetric = MIN(route->refmetric + - add_metric + - neighbour_cost(route->neigh), - INFINITY); - - if(newmetric != oldmetric) { - change_route_metric(route, newmetric); + change_route_metric(route, route->refmetric, + neighbour_cost(route->neigh), add_metric); + if(route_metric(route) != oldmetric) route_changed(route, route->src, oldmetric); - } } } /* Called whenever a neighbour's cost changes, to update the metric of - all routes through that neighbour. */ + all routes through that neighbour. Calls local_notify_neighbour. */ void update_neighbour_metric(struct neighbour *neigh, int changed) { + if(changed) { int i; - i = 0; - while(i < numroutes) { - if(routes[i].neigh == neigh) - update_route_metric(&routes[i]); - i++; + for(i = 0; i < route_slots; i++) { + struct babel_route *r = routes[i]; + while(r) { + if(r->neigh == neigh) + update_route_metric(r); + r = r->next; + } } } } @@ -388,11 +656,13 @@ update_interface_metric(struct interface *ifp) { int i; - i = 0; - while(i < numroutes) { - if(routes[i].neigh->ifp == ifp) - update_route_metric(&routes[i]); - i++; + for(i = 0; i < route_slots; i++) { + struct babel_route *r = routes[i]; + while(r) { + if(r->neigh->ifp == ifp) + update_route_metric(r); + r = r->next; + } } } @@ -402,7 +672,8 @@ update_route(const unsigned char *router_id, const unsigned char *prefix, unsigned char plen, unsigned short seqno, unsigned short refmetric, unsigned short interval, - struct neighbour *neigh, const unsigned char *nexthop) + struct neighbour *neigh, const unsigned char *nexthop, + const unsigned char *channels, int channels_len) { struct babel_route *route; struct source *src; @@ -424,12 +695,18 @@ update_route(const unsigned char *router_id, if(add_metric >= INFINITY) return NULL; - src = find_source(router_id, prefix, plen, 1, seqno); + route = find_route(prefix, plen, neigh, nexthop); + + if(route && memcmp(route->src->id, router_id, 8) == 0) + /* Avoid scanning the source table. */ + src = route->src; + else + src = find_source(router_id, prefix, plen, 1, seqno); + if(src == NULL) return NULL; feasible = update_feasible(src, seqno, refmetric); - route = find_route(prefix, plen, neigh, nexthop); metric = MIN((int)refmetric + neighbour_cost(neigh) + add_metric, INFINITY); if(route) { @@ -457,12 +734,12 @@ update_route(const unsigned char *router_id, } } - route->src = src; - if(feasible && refmetric < INFINITY) + route->src = retain_source(src); + if((feasible || keep_unfeasible) && refmetric < INFINITY) route->time = babel_now.tv_sec; route->seqno = seqno; - route->refmetric = refmetric; - change_route_metric(route, metric); + change_route_metric(route, + refmetric, neighbour_cost(neigh), add_metric); route->hold_time = hold_time; route_changed(route, oldsrc, oldmetric); @@ -472,36 +749,46 @@ update_route(const unsigned char *router_id, if(!feasible) send_unfeasible_request(neigh, route->installed && route_old(route), seqno, metric, src); + release_source(oldsrc); } else { + struct babel_route *new_route; + if(refmetric >= INFINITY) /* Somebody's retracting a route we never saw. */ return NULL; if(!feasible) { send_unfeasible_request(neigh, 0, seqno, metric, src); - return NULL; - } - if(numroutes >= maxroutes) { - struct babel_route *new_routes; - int n = maxroutes < 1 ? 8 : 2 * maxroutes; - new_routes = routes == NULL ? - malloc(n * sizeof(struct babel_route)) : - realloc(routes, n * sizeof(struct babel_route)); - if(new_routes == NULL) + if(!keep_unfeasible) return NULL; - maxroutes = n; - routes = new_routes; } - route = &routes[numroutes]; - route->src = src; + + route = malloc(sizeof(struct babel_route)); + if(route == NULL) { + perror("malloc(route)"); + return NULL; + } + + route->src = retain_source(src); route->refmetric = refmetric; + route->cost = neighbour_cost(neigh); + route->add_metric = add_metric; route->seqno = seqno; - route->metric = metric; route->neigh = neigh; memcpy(route->nexthop, nexthop, 16); route->time = babel_now.tv_sec; route->hold_time = hold_time; route->installed = 0; - numroutes++; + memset(&route->channels, 0, sizeof(route->channels)); + if(channels_len > 0) + memcpy(&route->channels, channels, + MIN(channels_len, DIVERSITY_HOPS)); + route->next = NULL; + new_route = insert_route(route); + if(new_route == NULL) { + fprintf(stderr, "Couldn't insert route.\n"); + free(route); + return NULL; + } consider_route(route); } return route; @@ -558,13 +845,6 @@ consider_route(struct babel_route *route) if(route_metric(installed) >= INFINITY) goto install; - if(route_metric(installed) >= route_metric(route) + 192) - goto install; - - /* Avoid switching sources */ - if(installed->src != route->src) - return; - if(route_metric(installed) >= route_metric(route) + 64) goto install; @@ -584,15 +864,18 @@ retract_neighbour_routes(struct neighbour *neigh) { int i; - i = 0; - while(i < numroutes) { - if(routes[i].neigh == neigh) { - if(routes[i].refmetric != INFINITY) { - unsigned short oldmetric = route_metric(&routes[i]); - retract_route(&routes[i]); + for(i = 0; i < route_slots; i++) { + struct babel_route *r = routes[i]; + while(r) { + if(r->neigh == neigh) { + if(r->refmetric != INFINITY) { + unsigned short oldmetric = route_metric(r); + retract_route(r); if(oldmetric != INFINITY) - route_changed(&routes[i], routes[i].src, oldmetric); + route_changed(r, r->src, oldmetric); + } } + r = r->next; } i++; } @@ -699,51 +982,38 @@ route_lost(struct source *src, unsigned oldmetric) } } +/* This is called periodically to flush old routes. It will also send + requests for routes that are about to expire. */ void expire_routes(void) { + struct babel_route *r; int i; debugf(BABEL_DEBUG_COMMON,"Expiring old routes."); i = 0; - while(i < numroutes) { - struct babel_route *route = &routes[i]; - - if(route->time > babel_now.tv_sec || /* clock stepped */ - route_old(route)) { - flush_route(route); - continue; - } + while(i < route_slots) { + r = routes[i]; + while(r) { + /* Protect against clock being stepped. */ + if(r->time > babel_now.tv_sec || route_old(r)) { + flush_route(r); + goto again; + } - update_route_metric(route); + update_route_metric(r); - if(route->installed && route->refmetric < INFINITY) { - if(route_old(route)) - send_unicast_request(route->neigh, - route->src->prefix, route->src->plen); + if(r->installed && r->refmetric < INFINITY) { + if(route_old(r)) + /* Route about to expire, send a request. */ + send_unicast_request(r->neigh, + r->src->prefix, r->src->plen); + } + r = r->next; } i++; + again: + ; } } - -void -babel_uninstall_all_routes(void) -{ - while(numroutes > 0) { - uninstall_route(&routes[0]); - /* We need to flush the route so network_up won't reinstall it */ - flush_route(&routes[0]); - } -} - -struct babel_route * -babel_route_get_by_source(struct source *src) -{ - int i; - for(i = 0; i < numroutes; i++) { - if(routes[i].src == src) - return &routes[i]; - } - return NULL; -} diff --git a/babeld/route.h b/babeld/route.h index c08332a6..b6d2d294 100644 --- a/babeld/route.h +++ b/babeld/route.h @@ -43,41 +43,69 @@ THE SOFTWARE. #include "babel_interface.h" #include "source.h" +#define DIVERSITY_NONE 0 +#define DIVERSITY_INTERFACE_1 1 +#define DIVERSITY_CHANNEL_1 2 +#define DIVERSITY_CHANNEL 3 + +#define DIVERSITY_HOPS 8 + struct babel_route { struct source *src; - unsigned short metric; unsigned short refmetric; + unsigned short cost; + unsigned short add_metric; unsigned short seqno; struct neighbour *neigh; unsigned char nexthop[16]; time_t time; unsigned short hold_time; /* in seconds */ short installed; + unsigned char channels[DIVERSITY_HOPS]; + struct babel_route *next; }; -static inline unsigned short +extern struct babel_route **routes; +extern int kernel_metric, allow_duplicates; +extern int diversity_kind, diversity_factor; +extern int keep_unfeasible; + +static inline int route_metric(const struct babel_route *route) { - return route->metric; + int m = (int)route->refmetric + route->cost + route->add_metric; + return MIN(m, INFINITY); } -extern struct babel_route *routes; -extern int numroutes, maxroutes; -extern int kernel_metric, allow_duplicates; +static inline int +route_metric_noninterfering(const struct babel_route *route) +{ + int m = + (int)route->refmetric + + (diversity_factor * route->cost + 128) / 256 + + route->add_metric; + m = MAX(m, route->refmetric + 1); + return MIN(m, INFINITY); +} struct babel_route *find_route(const unsigned char *prefix, unsigned char plen, struct neighbour *neigh, const unsigned char *nexthop); struct babel_route *find_installed_route(const unsigned char *prefix, unsigned char plen); +int installed_routes_estimate(void); void flush_route(struct babel_route *route); +void flush_all_routes(void); void flush_neighbour_routes(struct neighbour *neigh); void flush_interface_routes(struct interface *ifp, int v4only); +void for_all_routes(void (*f)(struct babel_route*, void*), void *closure); +void for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure); void install_route(struct babel_route *route); void uninstall_route(struct babel_route *route); void switch_route(struct babel_route *old, struct babel_route *new); int route_feasible(struct babel_route *route); int route_old(struct babel_route *route); int route_expired(struct babel_route *route); +int route_interferes(struct babel_route *route, struct interface *ifp); int update_feasible(struct source *src, unsigned short seqno, unsigned short refmetric); struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen, @@ -87,11 +115,12 @@ struct babel_route *install_best_route(const unsigned char prefix[16], void update_neighbour_metric(struct neighbour *neigh, int change); void update_interface_metric(struct interface *ifp); void update_route_metric(struct babel_route *route); -struct babel_route *update_route(const unsigned char *a, - const unsigned char *p, unsigned char plen, +struct babel_route *update_route(const unsigned char *id, + const unsigned char *prefix, unsigned char plen, unsigned short seqno, unsigned short refmetric, unsigned short interval, struct neighbour *neigh, - const unsigned char *nexthop); + const unsigned char *nexthop, + const unsigned char *channels, int channels_len); void retract_neighbour_routes(struct neighbour *neigh); void send_unfeasible_request(struct neighbour *neigh, int force, unsigned short seqno, unsigned short metric, @@ -103,7 +132,4 @@ void route_changed(struct babel_route *route, void route_lost(struct source *src, unsigned oldmetric); void expire_routes(void); -void babel_uninstall_all_routes(void); -struct babel_route *babel_route_get_by_source(struct source *src); - #endif diff --git a/babeld/source.c b/babeld/source.c index cc4ed445..772112d4 100644 --- a/babeld/source.c +++ b/babeld/source.c @@ -63,8 +63,10 @@ find_source(const unsigned char *id, const unsigned char *p, unsigned char plen, continue; if(memcmp(src->id, id, 8) != 0) continue; - if(source_match(src, p, plen)) - return src; + if(src->plen != plen) + continue; + if(memcmp(src->prefix, p, 16) == 0) + return src; } if(!create) @@ -82,18 +84,32 @@ find_source(const unsigned char *id, const unsigned char *p, unsigned char plen, src->seqno = seqno; src->metric = INFINITY; src->time = babel_now.tv_sec; + src->route_count = 0; src->next = srcs; srcs = src; return src; } +struct source * +retain_source(struct source *src) +{ + assert(src->route_count < 0xffff); + src->route_count++; + return src; +} + +void +release_source(struct source *src) +{ + assert(src->route_count > 0); + src->route_count--; +} + int flush_source(struct source *src) { - /* This is absolutely horrible -- it makes expire_sources quadratic. - But it's not called very often. */ - - if (babel_route_get_by_source(src) != NULL) + if(src->route_count > 0) + /* The source is in use by a route. */ return 0; if(srcs == src) { @@ -109,19 +125,6 @@ flush_source(struct source *src) return 1; } -int -source_match(struct source *src, - const unsigned char *p, unsigned char plen) -{ - if(src->plen != plen) - return 0; - if(src->prefix[15] != p[15]) - return 0; - if(memcmp(src->prefix, p, 16) != 0) - return 0; - return 1; -} - void update_source(struct source *src, unsigned short seqno, unsigned short metric) @@ -129,6 +132,10 @@ update_source(struct source *src, if(metric >= INFINITY) return; + /* If a source is expired, pretend that it doesn't exist and update + it unconditionally. This makes ensures that old data will + eventually be overridden, and prevents us from getting stuck if + a router loses its sequence number. */ if(src->time < babel_now.tv_sec - SOURCE_GC_TIME || seqno_compare(src->seqno, seqno) < 0 || (src->seqno == seqno && src->metric > metric)) { @@ -157,3 +164,17 @@ expire_sources() src = src->next; } } + +void +check_sources_released(void) +{ + struct source *src; + + for(src = srcs; src; src = src->next) { + if(src->route_count != 0) + fprintf(stderr, "Warning: source %s %s has refcount %d.\n", + format_eui64(src->id), + format_prefix(src->prefix, src->plen), + (int)src->route_count); + } +} diff --git a/babeld/source.h b/babeld/source.h index 38d3c004..62a7e1ee 100644 --- a/babeld/source.h +++ b/babeld/source.h @@ -48,19 +48,20 @@ struct source { unsigned char plen; unsigned short seqno; unsigned short metric; + unsigned short route_count; time_t time; }; -int source_match(struct source *src, - const unsigned char *p, unsigned char plen); struct source *find_source(const unsigned char *id, const unsigned char *p, unsigned char plen, int create, unsigned short seqno); +struct source *retain_source(struct source *src); +void release_source(struct source *src); int flush_source(struct source *src); void update_source(struct source *src, unsigned short seqno, unsigned short metric); void expire_sources(void); - +void check_sources_released(void); #endif diff --git a/babeld/util.c b/babeld/util.c index 514e0ff3..011f3824 100644 --- a/babeld/util.c +++ b/babeld/util.c @@ -200,17 +200,6 @@ parse_msec(const char *string) return -1; } -void -do_debugf(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - fflush(stderr); - va_end(args); -} - int in_prefix(const unsigned char *restrict address, const unsigned char *restrict prefix, unsigned char plen) @@ -328,63 +317,6 @@ parse_address(const char *address, unsigned char *addr_r, int *af_r) } int -parse_net(const char *net, unsigned char *prefix_r, unsigned char *plen_r, - int *af_r) -{ - char buf[INET6_ADDRSTRLEN]; - char *slash, *end; - unsigned char prefix[16]; - long plen; - int af; - struct in_addr ina; - struct in6_addr ina6; - int rc; - - if(strcmp(net, "default") == 0) { - memset(prefix, 0, 16); - plen = 0; - } else { - slash = strchr(net, '/'); - if(slash == NULL) { - rc = parse_address(net, prefix, &af); - if(rc < 0) - return rc; - plen = 128; - } else { - if(slash - net >= INET6_ADDRSTRLEN) - return -1; - memcpy(buf, net, slash - net); - buf[slash - net] = '\0'; - rc = inet_pton(AF_INET, buf, &ina); - if(rc > 0) { - memcpy(prefix, v4prefix, 12); - memcpy(prefix + 12, &ina, 4); - plen = strtol(slash + 1, &end, 0); - if(*end != '\0' || plen < 0 || plen > 32) - return -1; - plen += 96; - af = AF_INET; - } else { - rc = inet_pton(AF_INET6, buf, &ina6); - if(rc > 0) { - memcpy(prefix, &ina6, 16); - plen = strtol(slash + 1, &end, 0); - if(*end != '\0' || plen < 0 || plen > 128) - return -1; - af = AF_INET6; - } else { - return -1; - } - } - } - } - mask_prefix(prefix_r, prefix, plen); - *plen_r = plen; - if(af_r) *af_r = af; - return 0; -} - -int parse_eui64(const char *eui, unsigned char *eui_r) { int n; diff --git a/babeld/util.h b/babeld/util.h index 376b967f..5d9d2f5d 100644 --- a/babeld/util.h +++ b/babeld/util.h @@ -100,8 +100,6 @@ int timeval_compare(const struct timeval *s1, const struct timeval *s2) void timeval_min(struct timeval *d, const struct timeval *s); void timeval_min_sec(struct timeval *d, time_t secs); int parse_msec(const char *string) ATTRIBUTE ((pure)); -void do_debugf(const char *format, ...) - ATTRIBUTE ((format (printf, 1, 2))) COLD; int in_prefix(const unsigned char *restrict address, const unsigned char *restrict prefix, unsigned char plen) ATTRIBUTE ((pure)); @@ -113,8 +111,6 @@ const char *format_prefix(const unsigned char *address, unsigned char prefix); const char *format_eui64(const unsigned char *eui); const char *format_bool(const int b); int parse_address(const char *address, unsigned char *addr_r, int *af_r); -int parse_net(const char *ifp, unsigned char *prefix_r, unsigned char *plen_r, - int *af_r); int parse_eui64(const char *eui, unsigned char *eui_r); int wait_for_fd(int direction, int fd, int msecs); int martian_prefix(const unsigned char *prefix, int plen) ATTRIBUTE ((pure)); diff --git a/babeld/xroute.c b/babeld/xroute.c index 8330b492..80651671 100644 --- a/babeld/xroute.c +++ b/babeld/xroute.c @@ -50,9 +50,12 @@ THE SOFTWARE. #include "util.h" #include "babel_interface.h" -struct xroute *xroutes; -int numxroutes = 0; -int maxxroutes = 0; +static int xroute_add_new_route(unsigned char prefix[16], unsigned char plen, + unsigned short metric, unsigned int ifindex, + int proto, int send_updates); + +static struct xroute *xroutes; +static int numxroutes = 0, maxxroutes = 0; /* Add redistributed route to Babel table. */ int @@ -189,8 +192,24 @@ add_xroute(unsigned char prefix[16], unsigned char plen, return 1; } -/* add an xroute, verifying some conditions; return 0 if there is no changes */ +/* Returns an overestimate of the number of xroutes. */ int +xroutes_estimate() +{ + return numxroutes; +} + +void +for_all_xroutes(void (*f)(struct xroute*, void*), void *closure) +{ + int i; + + for(i = 0; i < numxroutes; i++) + (*f)(&xroutes[i], closure); +} + +/* add an xroute, verifying some conditions; return 0 if there is no changes */ +static int xroute_add_new_route(unsigned char prefix[16], unsigned char plen, unsigned short metric, unsigned int ifindex, int proto, int send_updates) diff --git a/babeld/xroute.h b/babeld/xroute.h index c9a4f85f..4d4ab99d 100644 --- a/babeld/xroute.h +++ b/babeld/xroute.h @@ -45,14 +45,8 @@ struct xroute { int proto; }; -extern struct xroute *xroutes; -extern int numxroutes; - struct xroute *find_xroute(const unsigned char *prefix, unsigned char plen); void flush_xroute(struct xroute *xroute); -int xroute_add_new_route(unsigned char prefix[16], unsigned char plen, - unsigned short metric, unsigned int ifindex, - int proto, int send_updates); int babel_ipv4_route_add (struct zapi_ipv4 *api, struct prefix_ipv4 *prefix, unsigned int ifindex, struct in_addr *nexthop); int babel_ipv4_route_delete (struct zapi_ipv4 *api, struct prefix_ipv4 *prefix, @@ -61,3 +55,5 @@ int babel_ipv6_route_add (struct zapi_ipv6 *api, struct prefix_ipv6 *prefix, unsigned int ifindex, struct in6_addr *nexthop); int babel_ipv6_route_delete (struct zapi_ipv6 *api, struct prefix_ipv6 *prefix, unsigned int ifindex); +int xroutes_estimate(void); +void for_all_xroutes(void (*f)(struct xroute*, void*), void *closure); |