This 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, preferring the NET_IP6_SADR channel. 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.
Signed-off-by: Toke Høiland-Jørgensen <[email protected]> --- This version also works with plain ipv6 channels. proto/babel/babel.c | 34 +++++++--- proto/babel/babel.h | 20 ++++-- proto/babel/packets.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 206 insertions(+), 21 deletions(-) diff --git a/proto/babel/babel.c b/proto/babel/babel.c index aa7e8b68..59e350e3 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -1950,13 +1950,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, "%-50N %-23lR %6u %5u %7u %7u", 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, "%-50N %-23lR %6u %5u %7u %7u", 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, "%-50N %-23s %6s %5s %7u %7u", e->n.addr, "<none>", "-", "-", rts, srcs); } FIB_WALK_END; @@ -1975,7 +1975,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, "%-50s %-23s %6s %5s %7s %7s", "Prefix", "Router ID", "Metric", "Seqno", "Routes", "Sources"); babel_show_entries_(p, &p->ip4_rtable); @@ -1994,7 +1994,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, "%-50N %-25I %-10s %5u %c %5u %7t", e->n.addr, r->next_hop, r->neigh->ifa->ifname, r->metric, c, r->seqno, MAX(time, 0)); } @@ -2015,7 +2015,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, "%-50s %-25s %-9s %6s F %5s %7s", "Prefix", "Nexthop", "Interface", "Metric", "Seqno", "Expires"); babel_show_routes_(p, &p->ip4_rtable); @@ -2187,9 +2187,14 @@ babel_init(struct proto_config *CF) { struct proto *P = proto_new(CF); struct babel_proto *p = (void *) P; + struct channel_config *ip6_chan = proto_cf_find_channel(CF, NET_IP6_SADR); + + /* Only want one of IP6 or IP6 SADR */ + if (!ip6_chan) + ip6_chan = proto_cf_find_channel(CF, NET_IP6); 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->ip6_channel, ip6_chan); P->if_notify = babel_if_notify; P->rt_notify = babel_rt_notify; @@ -2207,10 +2212,11 @@ babel_start(struct proto *P) { struct babel_proto *p = (void *) P; struct babel_config *cf = (void *) P->cf; + u8 ip6_type = p->ip6_channel ? p->ip6_channel->net_type : NET_IP6_SADR; 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 +2264,19 @@ babel_reconfigure(struct proto *P, struct proto_config *CF) { struct babel_proto *p = (void *) P; struct babel_config *new = (void *) CF; + struct channel_config *ip6_chan; TRACE(D_EVENTS, "Reconfiguring"); + /* Only want one of IP6 or IP6 SADR */ + if (!(ip6_chan = proto_cf_find_channel(CF, NET_IP6_SADR))) + ip6_chan = proto_cf_find_channel(CF, NET_IP6); + + if (ip6_chan && p->ip6_channel && ip6_chan->net_type != p->ip6_channel->net_type) + return 0; + 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))) + !proto_configure_channel(P, &p->ip6_channel, ip6_chan)) return 0; p->p.cf = CF; @@ -2280,7 +2294,7 @@ 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), .init = babel_init, diff --git a/proto/babel/babel.h b/proto/babel/babel.h index 1128d261..0902241a 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 { @@ -303,7 +306,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 +317,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 +328,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; }; diff --git a/proto/babel/packets.c b/proto/babel/packets.c index dd86222a..321f7f8c 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,8 @@ 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 +254,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; @@ -639,6 +650,8 @@ 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) + msg->net.type = NET_IP6_SADR; if (tlv->flags & BABEL_UF_DEF_PREFIX) { @@ -739,11 +752,13 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, } else { + u8 buf[16], omit; + int l; + tlv->ae = BABEL_AE_IP6; tlv->plen = net6_pxlen(&msg->net); /* Address compression - omit initial matching bytes */ - u8 buf[16], omit; put_ip6(buf, net6_prefix(&msg->net)); omit = bytes_equal(buf, state->def_ip6_prefix, MIN(tlv->plen, state->def_ip6_pxlen) / 8); @@ -764,6 +779,12 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, put_ip6(state->def_ip6_prefix, net6_prefix(&msg->net)); state->def_ip6_pxlen = tlv->plen; } + + l = babel_write_source_prefix(hdr, &msg->net, max_len - len - len0); + if (l < 0) + return 0; + + len += l; } put_time16(&tlv->interval, msg->interval); @@ -811,6 +832,9 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, return PARSE_ERROR; read_ip6_px(&msg->net, tlv->addr, tlv->plen); + if (state->sadr_enabled) + msg->net.type = NET_IP6_SADR; + state->current_tlv_endpos += BYTES(tlv->plen); return PARSE_SUCCESS; @@ -851,9 +875,17 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m, } else { + int l; + tlv->ae = BABEL_AE_IP6; tlv->plen = net6_pxlen(&msg->net); put_ip6_px(tlv->addr, &msg->net); + + l = babel_write_source_prefix(hdr, &msg->net, max_len - len); + if (l < 0) + return 0; + + len += l; } return len; @@ -899,6 +931,9 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m, return PARSE_ERROR; read_ip6_px(&msg->net, tlv->addr, tlv->plen); + if (state->sadr_enabled) + msg->net.type = NET_IP6_SADR; + state->current_tlv_endpos += BYTES(tlv->plen); return PARSE_SUCCESS; @@ -920,6 +955,7 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m, struct babel_msg_seqno_request *msg = &m->seqno_request; uint len = sizeof(struct babel_tlv_seqno_request) + NET_SIZE(&msg->net); + int l; if (len > max_len) return 0; @@ -943,34 +979,156 @@ 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); + l = babel_write_source_prefix(hdr, &msg->net, max_len - len); + if (l < 0) + return 0; + + return len + l; +} + +/* Add a subtlv to the tlv pointed to by header, with the specified subtlv type + and len. The length of the surrounding TLV is expanded. + + Caller must ensure the buffer has enough space. */ +static struct babel_tlv * +babel_add_subtlv(struct babel_tlv *hdr, + u8 type, + u8 len) +{ + struct babel_tlv *subtlv = (struct babel_tlv *) ((byte *) hdr + TLV_LENGTH(hdr)); + hdr->length += len; + TLV_HDR(subtlv, type, len); + return subtlv; +} + +static int +babel_write_source_prefix(struct babel_tlv *hdr, + net_addr *net, + uint max_len) +{ + struct babel_subtlv_source_prefix *tlv; + struct net_addr_ip6_sadr *adr = (void *) net; + uint len; + + if (!net_is_sadr_set(net)) + return 0; + + len = sizeof(*tlv) + BYTES(adr->src_pxlen); + + if (len > max_len) + return -1; + + tlv = (void *) babel_add_subtlv(hdr, BABEL_SUBTLV_SOURCE_PREFIX, len); + net_addr_ip6 sadr = NET_ADDR_IP6(adr->src_prefix, adr->src_pxlen); + tlv->plen = adr->src_pxlen; + put_ip6_px(tlv->addr, (void *) &sadr); + return len; } +static int +babel_read_source_prefix(struct babel_tlv *hdr, + union babel_msg *msg, + struct babel_parse_state *state) +{ + struct babel_subtlv_source_prefix *tlv = (void *) hdr; + struct net_addr_ip6_sadr *adr; + struct net_addr_ip6_sadr sadr; + + /* We are not configured to process source-specific routes */ + if (!state->sadr_enabled) { + DBG("Not configured for SADR, ignoring source-specific route\n"); + return PARSE_IGNORE; + } + + if (tlv->length < BYTES(tlv->plen) + 1 || + tlv->plen > IP6_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + /* plen MUST NOT be 0 */ + if (tlv->plen == 0) + return PARSE_IGNORE; + + switch(msg->type) { + case BABEL_TLV_UPDATE: + /* wildcard updates with source prefix MUST be silently ignored */ + if (msg->update.wildcard) + return PARSE_IGNORE; + + adr = (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; + + adr = (void *) &msg->route_request.net; + break; + + case BABEL_TLV_SEQNO_REQUEST: + adr = (void *) &msg->seqno_request.net; + break; + + default: + return PARSE_ERROR; + } + + if (!net_is_sadr((net_addr *) adr)) + return PARSE_ERROR; + + /* Duplicate source prefix subtlv; SHOULD ignore TLV */ + if (adr->src_pxlen > 0) + return PARSE_IGNORE; + + /* read_ip6_px() reads a prefix into a net_addr_ip6_sadr struct, which in this + case is the sadr */ + read_ip6_px((void *) &sadr, tlv->addr, tlv->plen); + adr->src_prefix = sadr.dst_prefix; + adr->src_pxlen = sadr.dst_pxlen; + + return PARSE_SUCCESS; +} + 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)) { + if (tlv->type == BABEL_TLV_PAD1) + continue; + + pos = (byte *)tlv + sizeof(struct babel_tlv); + if ((pos > end) || (pos + tlv->length > end)) + { + DBG("Babel: subtlv framing error.\n"); + 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) + if (tlv->type >= 128) { DBG("Babel: Mandatory subtlv %d found; skipping TLV\n", tlv->type); return PARSE_IGNORE; @@ -1197,6 +1355,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len, .ifa = ifa, .saddr = saddr, .next_hop_ip6 = saddr, + .sadr_enabled = (p->ip6_rtable.addr_type == NET_IP6_SADR), }; if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) -- 2.16.1
