From 185a0a51f8aed635eecac0cfbd837dd262a8add0 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 13 Feb 2018 16:39:36 +0100 Subject: [PATCH] Babel: Add source-specific routing support This patch adds support for source-specific routing to the Babel protocol. It changes the protocol to support both NET_IP6 and NET_IP6_SADR channels for IPv6 addresses. If only a NET_IP6 channel is configured, source-specific updates are ignored. Otherwise, non-source-specific routes are simply treated as source-specific routes with SADR prefix 0. Thanks to Toke Hoiland-Jorgensen for the original patch. Minor changes by Ondrej Santiago Zajicek. --- proto/babel/babel.c | 58 +++++++++++---- proto/babel/babel.h | 25 ++++++- proto/babel/packets.c | 169 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 224 insertions(+), 28 deletions(-) diff --git a/proto/babel/babel.c b/proto/babel/babel.c index aa7e8b68..a60ffe78 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -1935,8 +1935,10 @@ babel_show_neighbors(struct proto *P, char *iff) } static void -babel_show_entries_(struct babel_proto *p UNUSED, struct fib *rtable) +babel_show_entries_(struct babel_proto *p, struct fib *rtable) { + int width = babel_sadr_enabled(p) ? -54 : -24; + FIB_WALK(rtable, struct babel_entry, e) { struct babel_route *r = NULL; @@ -1950,13 +1952,13 @@ babel_show_entries_(struct babel_proto *p UNUSED, struct fib *rtable) srcs++; if (e->valid) - cli_msg(-1025, "%-24N %-23lR %6u %5u %7u %7u", + cli_msg(-1025, "%-*N %-23lR %6u %5u %7u %7u", width, e->n.addr, e->router_id, e->metric, e->seqno, rts, srcs); else if (r = e->selected) - cli_msg(-1025, "%-24N %-23lR %6u %5u %7u %7u", + cli_msg(-1025, "%-*N %-23lR %6u %5u %7u %7u", width, e->n.addr, r->router_id, r->metric, r->seqno, rts, srcs); else - cli_msg(-1025, "%-24N %-23s %6s %5s %7u %7u", + cli_msg(-1025, "%-*N %-23s %6s %5s %7u %7u", width, e->n.addr, "", "-", "-", rts, srcs); } FIB_WALK_END; @@ -1966,6 +1968,7 @@ void babel_show_entries(struct proto *P) { struct babel_proto *p = (void *) P; + int width = babel_sadr_enabled(p) ? -54 : -24; if (p->p.proto_state != PS_UP) { @@ -1975,7 +1978,7 @@ babel_show_entries(struct proto *P) } cli_msg(-1025, "%s:", p->p.name); - cli_msg(-1025, "%-24s %-23s %6s %5s %7s %7s", + cli_msg(-1025, "%-*s %-23s %6s %5s %7s %7s", width, "Prefix", "Router ID", "Metric", "Seqno", "Routes", "Sources"); babel_show_entries_(p, &p->ip4_rtable); @@ -1985,8 +1988,10 @@ babel_show_entries(struct proto *P) } static void -babel_show_routes_(struct babel_proto *p UNUSED, struct fib *rtable) +babel_show_routes_(struct babel_proto *p, struct fib *rtable) { + int width = babel_sadr_enabled(p) ? -54 : -24; + FIB_WALK(rtable, struct babel_entry, e) { struct babel_route *r; @@ -1994,7 +1999,7 @@ babel_show_routes_(struct babel_proto *p UNUSED, struct fib *rtable) { char c = (r == e->selected) ? '*' : (r->feasible ? '+' : ' '); btime time = r->expires ? r->expires - current_time() : 0; - cli_msg(-1025, "%-24N %-25I %-10s %5u %c %5u %7t", + cli_msg(-1025, "%-*N %-25I %-10s %5u %c %5u %7t", width, e->n.addr, r->next_hop, r->neigh->ifa->ifname, r->metric, c, r->seqno, MAX(time, 0)); } @@ -2006,6 +2011,7 @@ void babel_show_routes(struct proto *P) { struct babel_proto *p = (void *) P; + int width = babel_sadr_enabled(p) ? -54 : -24; if (p->p.proto_state != PS_UP) { @@ -2015,7 +2021,7 @@ babel_show_routes(struct proto *P) } cli_msg(-1025, "%s:", p->p.name); - cli_msg(-1025, "%-24s %-25s %-9s %6s F %5s %7s", + cli_msg(-1025, "%-*s %-25s %-9s %6s F %5s %7s", width, "Prefix", "Nexthop", "Interface", "Metric", "Seqno", "Expires"); babel_show_routes_(p, &p->ip4_rtable); @@ -2182,14 +2188,32 @@ babel_rte_same(struct rte *new, struct rte *old) } +static void +babel_postconfig(struct proto_config *CF) +{ + struct babel_config *cf = (void *) CF; + struct channel_config *ip4, *ip6, *ip6_sadr; + + ip4 = proto_cf_find_channel(CF, NET_IP4); + ip6 = proto_cf_find_channel(CF, NET_IP6); + ip6_sadr = proto_cf_find_channel(CF, NET_IP6_SADR); + + if (ip6 && ip6_sadr) + cf_error("Both ipv6 and ipv6-sadr channels"); + + cf->ip4_channel = ip4; + cf->ip6_channel = ip6 ?: ip6_sadr; +} + static struct proto * babel_init(struct proto_config *CF) { struct proto *P = proto_new(CF); struct babel_proto *p = (void *) P; + struct babel_config *cf = (void *) CF; - proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)); - proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); + proto_configure_channel(P, &p->ip4_channel, cf->ip4_channel); + proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel); P->if_notify = babel_if_notify; P->rt_notify = babel_rt_notify; @@ -2207,10 +2231,11 @@ babel_start(struct proto *P) { struct babel_proto *p = (void *) P; struct babel_config *cf = (void *) P->cf; + u8 ip6_type = cf->ip6_channel ? cf->ip6_channel->net_type : NET_IP6; fib_init(&p->ip4_rtable, P->pool, NET_IP4, sizeof(struct babel_entry), OFFSETOF(struct babel_entry, n), 0, babel_init_entry); - fib_init(&p->ip6_rtable, P->pool, NET_IP6, sizeof(struct babel_entry), + fib_init(&p->ip6_rtable, P->pool, ip6_type, sizeof(struct babel_entry), OFFSETOF(struct babel_entry, n), 0, babel_init_entry); init_list(&p->interfaces); @@ -2258,11 +2283,15 @@ babel_reconfigure(struct proto *P, struct proto_config *CF) { struct babel_proto *p = (void *) P; struct babel_config *new = (void *) CF; + u8 ip6_type = new->ip6_channel ? new->ip6_channel->net_type : NET_IP6; TRACE(D_EVENTS, "Reconfiguring"); - if (!proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) || - !proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6))) + if (p->ip6_rtable.addr_type != ip6_type) + return 0; + + if (!proto_configure_channel(P, &p->ip4_channel, new->ip4_channel) || + !proto_configure_channel(P, &p->ip6_channel, new->ip6_channel)) return 0; p->p.cf = CF; @@ -2280,9 +2309,10 @@ struct protocol proto_babel = { .template = "babel%d", .attr_class = EAP_BABEL, .preference = DEF_PREF_BABEL, - .channel_mask = NB_IP, + .channel_mask = NB_IP | NB_IP6_SADR, .proto_size = sizeof(struct babel_proto), .config_size = sizeof(struct babel_config), + .postconfig = babel_postconfig, .init = babel_init, .dump = babel_dump, .start = babel_start, diff --git a/proto/babel/babel.h b/proto/babel/babel.h index 1128d261..b194ce30 100644 --- a/proto/babel/babel.h +++ b/proto/babel/babel.h @@ -85,7 +85,10 @@ enum babel_tlv_type { enum babel_subtlv_type { BABEL_SUBTLV_PAD1 = 0, - BABEL_SUBTLV_PADN = 1 + BABEL_SUBTLV_PADN = 1, + + /* Mandatory subtlvs */ + BABEL_SUBTLV_SOURCE_PREFIX = 128, }; enum babel_iface_type { @@ -109,6 +112,9 @@ struct babel_config { struct proto_config c; list iface_list; /* List of iface configs (struct babel_iface_config) */ uint hold_time; /* Time to hold stale entries and unreachable routes */ + + struct channel_config *ip4_channel; + struct channel_config *ip6_channel; }; struct babel_iface_config { @@ -303,7 +309,10 @@ struct babel_msg_update { u16 seqno; u16 metric; u64 router_id; - net_addr net; + union { + net_addr net; + net_addr_ip6_sadr net_sadr; + }; ip_addr next_hop; ip_addr sender; }; @@ -311,7 +320,10 @@ struct babel_msg_update { struct babel_msg_route_request { u8 type; u8 full; - net_addr net; + union { + net_addr net; + net_addr_ip6_sadr net_sadr; + }; }; struct babel_msg_seqno_request { @@ -319,7 +331,10 @@ struct babel_msg_seqno_request { u8 hop_count; u16 seqno; u64 router_id; - net_addr net; + union { + net_addr net; + net_addr_ip6_sadr net_sadr; + }; ip_addr sender; }; @@ -339,6 +354,8 @@ struct babel_msg_node { union babel_msg msg; }; +static inline int babel_sadr_enabled(struct babel_proto *p) +{ return p->ip6_rtable.addr_type == NET_IP6_SADR; } /* babel.c */ void babel_handle_ack_req(union babel_msg *msg, struct babel_iface *ifa); diff --git a/proto/babel/packets.c b/proto/babel/packets.c index dd86222a..59678678 100644 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@ -105,6 +105,13 @@ struct babel_tlv_seqno_request { u8 addr[0]; } PACKED; +struct babel_subtlv_source_prefix { + u8 type; + u8 length; + u8 plen; + u8 addr[0]; +} PACKED; + /* Hello flags */ #define BABEL_HF_UNICAST 0x8000 @@ -127,6 +134,7 @@ struct babel_parse_state { u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */ u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */ u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */ + u8 sadr_enabled; }; enum parse_result { @@ -237,6 +245,7 @@ static int babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *msg, stru static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); +static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); @@ -244,6 +253,7 @@ static uint babel_write_ihu(struct babel_tlv *hdr, union babel_msg *msg, struct static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); +static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len); struct babel_tlv_data { u8 min_length; @@ -640,6 +650,9 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, ip6_addr prefix6 = get_ip6(buf); net_fill_ip6(&msg->net, prefix6, tlv->plen); + if (state->sadr_enabled) + net_make_ip6_sadr(&msg->net); + if (tlv->flags & BABEL_UF_DEF_PREFIX) { put_ip6(state->def_ip6_prefix, prefix6); @@ -770,12 +783,21 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, put_u16(&tlv->seqno, msg->seqno); put_u16(&tlv->metric, msg->metric); + if (msg->net.type == NET_IP6_SADR) + { + int l = babel_write_source_prefix(hdr, &msg->net, max_len - (len0 + len)); + if (l < 0) + return 0; + + len += l; + } + return len0 + len; } static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, - struct babel_parse_state *state UNUSED) + struct babel_parse_state *state) { struct babel_tlv_route_request *tlv = (void *) hdr; struct babel_msg_route_request *msg = &m->route_request; @@ -812,6 +834,10 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, read_ip6_px(&msg->net, tlv->addr, tlv->plen); state->current_tlv_endpos += BYTES(tlv->plen); + + if (state->sadr_enabled) + net_make_ip6_sadr(&msg->net); + return PARSE_SUCCESS; case BABEL_AE_IP6_LL: @@ -856,6 +882,15 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m, put_ip6_px(tlv->addr, &msg->net); } + if (msg->net.type == NET_IP6_SADR) + { + int l = babel_write_source_prefix(hdr, &msg->net, max_len - len); + if (l < 0) + return 0; + + len += l; + } + return len; } @@ -900,6 +935,10 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m, read_ip6_px(&msg->net, tlv->addr, tlv->plen); state->current_tlv_endpos += BYTES(tlv->plen); + + if (state->sadr_enabled) + net_make_ip6_sadr(&msg->net); + return PARSE_SUCCESS; case BABEL_AE_IP6_LL: @@ -943,38 +982,147 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m, tlv->hop_count = msg->hop_count; put_u64(&tlv->router_id, msg->router_id); + if (msg->net.type == NET_IP6_SADR) + { + int l = babel_write_source_prefix(hdr, &msg->net, max_len - len); + if (l < 0) + return 0; + + len += l; + } + return len; } +static int +babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, + struct babel_parse_state *state UNUSED) +{ + struct babel_subtlv_source_prefix *tlv = (void *) hdr; + net_addr_ip6_sadr *net; + + /* + * We would like to skip the sub-TLV if SADR is not enabled, but we do not + * know AF of the enclosing TLV yet. We will do that later. + */ + + /* Check internal consistency */ + if ((tlv->length < 1) || + (tlv->plen > IP6_MAX_PREFIX_LENGTH) || + (tlv->length < (1 + BYTES(tlv->plen)))) + return PARSE_ERROR; + + /* Plen MUST NOT be 0 */ + if (tlv->plen == 0) + return PARSE_ERROR; + + switch(msg->type) + { + case BABEL_TLV_UPDATE: + /* Wildcard updates with source prefix MUST be silently ignored */ + if (msg->update.wildcard) + return PARSE_IGNORE; + + net = (void *) &msg->update.net; + break; + + case BABEL_TLV_ROUTE_REQUEST: + /* Wildcard requests with source addresses MUST be silently ignored */ + if (msg->route_request.full) + return PARSE_IGNORE; + + net = (void *) &msg->route_request.net; + break; + + case BABEL_TLV_SEQNO_REQUEST: + net = (void *) &msg->seqno_request.net; + break; + + default: + return PARSE_ERROR; + } + + /* If SADR is active, the net has appropriate type */ + if (net->type != NET_IP6_SADR) + return PARSE_IGNORE; + + /* Duplicate Source Prefix sub-TLV; SHOULD ignore whole TLV */ + if (net->src_pxlen > 0) + return PARSE_IGNORE; + + net_addr_ip6 src; + read_ip6_px((void *) &src, tlv->addr, tlv->plen); + net->src_prefix = src.prefix; + net->src_pxlen = src.pxlen; + + return PARSE_SUCCESS; +} + +static int +babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len) +{ + struct babel_subtlv_source_prefix *tlv = (void *) NEXT_TLV(hdr); + net_addr_ip6_sadr *net = (void *) n; + + /* Do not use this sub-TLV for default prefix */ + if (net->src_pxlen == 0) + return 0; + + uint len = sizeof(*tlv) + BYTES(net->src_pxlen); + + if (len > max_len) + return -1; + + TLV_HDR(tlv, BABEL_SUBTLV_SOURCE_PREFIX, len); + hdr->length += len; + + net_addr_ip6 src = NET_ADDR_IP6(net->src_prefix, net->src_pxlen); + tlv->plen = src.pxlen; + put_ip6_px(tlv->addr, (void *) &src); + + return len; +} + + static inline int babel_read_subtlvs(struct babel_tlv *hdr, - union babel_msg *msg UNUSED, + union babel_msg *msg, struct babel_parse_state *state) { struct babel_tlv *tlv; + byte *pos, *end = (byte *) hdr + TLV_LENGTH(hdr); + int res; for (tlv = (void *) hdr + state->current_tlv_endpos; - (void *) tlv < (void *) hdr + TLV_LENGTH(hdr); + (byte *) tlv < end; tlv = NEXT_TLV(tlv)) { + /* Ugly special case */ + if (tlv->type == BABEL_TLV_PAD1) + continue; + + /* The end of the common TLV header */ + pos = (byte *)tlv + sizeof(struct babel_tlv); + if ((pos > end) || (pos + tlv->length > end)) + return PARSE_ERROR; + /* * The subtlv type space is non-contiguous (due to the mandatory bit), so * use a switch for dispatch instead of the mapping array we use for TLVs */ switch (tlv->type) { - case BABEL_SUBTLV_PAD1: - case BABEL_SUBTLV_PADN: - /* FIXME: Framing errors in PADN are silently ignored, see babel_process_packet() */ + case BABEL_SUBTLV_SOURCE_PREFIX: + res = babel_read_source_prefix(tlv, msg, state); + if (res != PARSE_SUCCESS) + return res; break; + case BABEL_SUBTLV_PADN: default: /* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */ - if (tlv->type > 128) - { - DBG("Babel: Mandatory subtlv %d found; skipping TLV\n", tlv->type); + if (tlv->type >= 128) return PARSE_IGNORE; - } break; } } @@ -1197,6 +1345,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len, .ifa = ifa, .saddr = saddr, .next_hop_ip6 = saddr, + .sadr_enabled = babel_sadr_enabled(p), }; if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))