[PATCH] babel: Add support for dual-stack v4/v6 operation
This adds support for dual-stack v4/v6 operation to the Babel protocol. Routing messages will be exchanged over IPv6, but IPv4 routes can be carried in the messages being exchanged. This matches how the reference Babel implementation (babeld) works. The nexthop address for v4 can be configured per interface, and will default to the first available IPv4 address on the given interface. For symmetry, a configuration option to configure the IPv6 nexthop address is also added. Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> --- This patch is against v2.0.0-pre1. I have done basic testing that Bird can now exchange both v4 and v6 routes with babeld, but have not performed extensive verification of corner cases (i.e, I haven't run it on a real network yet). nest/proto.c | 3 - nest/protocol.h | 3 + proto/babel/babel.c | 151 ++++++++++++++++++++++++++++++++---------- proto/babel/babel.h | 10 ++- proto/babel/config.Y | 6 +- proto/babel/packets.c | 179 +++++++++++++++++++++++++++++++++++++++++++------- 6 files changed, 286 insertions(+), 66 deletions(-) diff --git a/nest/proto.c b/nest/proto.c index 3d764df0..4d46e433 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -53,9 +53,6 @@ static void channel_reset_limit(struct channel_limit *l); static inline int proto_is_done(struct proto *p) { return (p->proto_state == PS_DOWN) && (p->active_channels == 0); } -static inline int channel_is_active(struct channel *c) -{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); } - static void proto_log_state_change(struct proto *p) { diff --git a/nest/protocol.h b/nest/protocol.h index f0958678..efeae24b 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -572,6 +572,9 @@ void channel_set_state(struct channel *c, uint state); static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); } static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); } static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); } +static inline int channel_is_active(struct channel *c) +{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); } + void channel_request_feeding(struct channel *c); void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto); diff --git a/proto/babel/babel.c b/proto/babel/babel.c index 1b1d9f62..6cf66fb9 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -78,13 +78,15 @@ babel_init_entry(void *E) static inline struct babel_entry * babel_find_entry(struct babel_proto *p, const net_addr *n) { - return fib_find(&p->rtable, n); + struct fib *rtable = (n->type == NET_IP4) ? &p->ip4_rtable : &p->ip6_rtable; + return fib_find(rtable, n); } static struct babel_entry * babel_get_entry(struct babel_proto *p, const net_addr *n) { - struct babel_entry *e = fib_get(&p->rtable, n); + struct fib *rtable = (n->type == NET_IP4) ? &p->ip4_rtable : &p->ip6_rtable; + struct babel_entry *e = fib_get(rtable, n); e->proto = p; return e; } @@ -224,15 +226,15 @@ babel_refresh_route(struct babel_route *r) } static void -babel_expire_routes(struct babel_proto *p) +babel_expire_rtable(struct fib *rtable) { struct babel_route *r, *rx; struct fib_iterator fit; - FIB_ITERATE_INIT(&fit, &p->rtable); + FIB_ITERATE_INIT(&fit, rtable); loop: - FIB_ITERATE_START(&p->rtable, &fit, struct babel_entry, e) + FIB_ITERATE_START(rtable, &fit, struct babel_entry, e) { int changed = 0; @@ -253,7 +255,7 @@ loop: /* * We have to restart the iteration because there may be a cascade of * synchronous events babel_select_route() -> nest table change -> - * babel_rt_notify() -> p->rtable change, invalidating hidden variables. + * babel_rt_notify() -> rtable change, invalidating hidden variables. */ FIB_ITERATE_PUT(&fit); @@ -267,13 +269,20 @@ loop: if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes)) { FIB_ITERATE_PUT(&fit); - fib_delete(&p->rtable, e); + fib_delete(rtable, e); goto loop; } } FIB_ITERATE_END; } +static void +babel_expire_routes(struct babel_proto *p) +{ + babel_expire_rtable(&p->ip4_rtable); + babel_expire_rtable(&p->ip6_rtable); +} + static struct babel_neighbor * babel_find_neighbor(struct babel_iface *ifa, ip_addr addr) { @@ -468,6 +477,12 @@ static void babel_announce_rte(struct babel_proto *p, struct babel_entry *e) { struct babel_route *r = e->selected_in; + struct channel *c; + + if (e->n.addr->type == NET_IP4) + c = p->ip4_channel; + else + c = p->ip6_channel; if (r) { @@ -490,12 +505,12 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e) rte->u.babel.router_id = r->router_id; rte->pflags = 0; - rte_update(&p->p, e->n.addr, rte); + rte_update2(c, e->n.addr, rte, p->p.main_source); } else { /* Retraction */ - rte_update(&p->p, e->n.addr, NULL); + rte_update2(c, e->n.addr, NULL, p->p.main_source); } } @@ -740,11 +755,11 @@ babel_unicast_seqno_request(struct babel_route *r) * transmitted entry is updated. */ static void -babel_send_update(struct babel_iface *ifa, bird_clock_t changed) +babel_send_update_rtable(struct babel_iface *ifa, bird_clock_t changed, struct fib *rtable) { struct babel_proto *p = ifa->proto; - FIB_WALK(&p->rtable, struct babel_entry, e) + FIB_WALK(rtable, struct babel_entry, e) { struct babel_route *r = e->selected_out; @@ -774,6 +789,9 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed) msg.update.router_id = r->router_id; net_copy(&msg.update.net, e->n.addr); + msg.update.next_hop = ((e->n.addr->type == NET_IP4) ? + ifa->cf->next_hop_v4 : ifa->cf->next_hop_v6); + babel_enqueue(&msg, ifa); /* Update feasibility distance for redistributed routes */ @@ -794,6 +812,15 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed) } static void +babel_send_update(struct babel_iface *ifa, bird_clock_t changed) +{ + struct babel_proto *p = ifa->proto; + + babel_send_update_rtable(ifa, changed, &p->ip4_rtable); + babel_send_update_rtable(ifa, changed, &p->ip6_rtable); +} + +static void babel_trigger_iface_update(struct babel_iface *ifa) { struct babel_proto *p = ifa->proto; @@ -1477,12 +1504,20 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con struct ifa *addr; WALK_LIST(addr, new->addrs) + { if (ipa_is_link_local(addr->ip)) ifa->addr = addr->ip; + if (ipa_zero(ifa->cf->next_hop_v4) && ipa_is_ip4(addr->ip)) + ifa->cf->next_hop_v4 = addr->ip; + } + if (ipa_zero(ifa->addr)) log(L_WARN "%s: Cannot find link-local addr on %s", p->p.name, new->name); + if (ipa_zero(ifa->cf->next_hop_v4) && channel_is_active(p->ip4_channel)) + log(L_WARN "%s: Cannot find IPv4 next hop addr on %s", p->p.name, new->name); + init_list(&ifa->neigh_list); ifa->hello_seqno = 1; @@ -1570,6 +1605,26 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b (new->tx_priority != old->tx_priority)) return 0; + + if (ipa_zero(new->next_hop_v4)) + { + struct ifa *addr; + WALK_LIST(addr, ifa->iface->addrs) + { + if (ipa_is_ip4(addr->ip)) + { + new->next_hop_v4 = addr->ip; + break; + } + } + } + + /* If no v4 address is found for next_hop we give up on reconfiguring and hope + that we can find one when the interface is brought down and back up + again. */ + if (ipa_zero(new->next_hop_v4) && channel_is_active(p->ip4_channel)) + return 0; + TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name); ifa->cf = new; @@ -1680,9 +1735,11 @@ babel_dump_iface(struct babel_iface *ifa) { struct babel_neighbor *n; - debug("Babel: Interface %s addr %I rxcost %d type %d hello seqno %d intervals %d %d\n", + debug("Babel: Interface %s addr %I rxcost %d type %d hello seqno %d intervals %d %d", ifa->ifname, ifa->addr, ifa->cf->rxcost, ifa->cf->type, ifa->hello_seqno, ifa->cf->hello_interval, ifa->cf->update_interval); + debug(" next hop v4 %I next hop v6 %I\n", ifa->cf->next_hop_v4, + ipa_zero(ifa->cf->next_hop_v6) ? ifa->addr : ifa->cf->next_hop_v6); WALK_LIST(n, ifa->neigh_list) { debug(" "); babel_dump_neighbor(n); } @@ -1699,7 +1756,12 @@ babel_dump(struct proto *P) WALK_LIST(ifa, p->interfaces) babel_dump_iface(ifa); - FIB_WALK(&p->rtable, struct babel_entry, e) + FIB_WALK(&p->ip4_rtable, struct babel_entry, e) + { + babel_dump_entry(e); + } + FIB_WALK_END; + FIB_WALK(&p->ip6_rtable, struct babel_entry, e) { babel_dump_entry(e); } @@ -1749,8 +1811,9 @@ babel_show_interfaces(struct proto *P, char *iff) } cli_msg(-1023, "%s:", p->p.name); - cli_msg(-1023, "%-10s %-6s %7s %6s %6s", - "Interface", "State", "RX cost", "Nbrs", "Timer"); + cli_msg(-1023, "%-10s %-6s %7s %6s %6s %-15s %s", + "Interface", "State", "RX cost", "Nbrs", "Timer", + "Next hop (v4)", "Next hop (v6)"); WALK_LIST(ifa, p->interfaces) { @@ -1762,8 +1825,11 @@ babel_show_interfaces(struct proto *P, char *iff) nbrs++; int timer = MIN(ifa->next_regular, ifa->next_hello) - now; - cli_msg(-1023, "%-10s %-6s %7u %6u %6u", - ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->rxcost, nbrs, MAX(timer, 0)); + cli_msg(-1023, "%-10s %-6s %7u %6u %6u %-15I %I", + ifa->iface->name, (ifa->up ? "Up" : "Down"), + ifa->cf->rxcost, nbrs, MAX(timer, 0), + ifa->cf->next_hop_v4, + ipa_zero(ifa->cf->next_hop_v6) ? ifa->addr : ifa->cf->next_hop_v6); } cli_msg(0, ""); @@ -1808,27 +1874,14 @@ babel_show_neighbors(struct proto *P, char *iff) cli_msg(0, ""); } -void -babel_show_entries(struct proto *P) +void babel_show_rtable(struct babel_proto *p, struct fib *rtable) { - struct babel_proto *p = (void *) P; struct babel_source *s = NULL; struct babel_route *r = NULL; char ridbuf[ROUTER_ID_64_LENGTH+1]; - if (p->p.proto_state != PS_UP) - { - cli_msg(-1025, "%s: is not up", p->p.name); - cli_msg(0, ""); - return; - } - - cli_msg(-1025, "%s:", p->p.name); - cli_msg(-1025, "%-29s %-23s %6s %5s %7s %7s", - "Prefix", "Router ID", "Metric", "Seqno", "Expires", "Sources"); - - FIB_WALK(&p->rtable, struct babel_entry, e) + FIB_WALK(rtable, struct babel_entry, e) { r = e->selected_in ? e->selected_in : e->selected_out; @@ -1853,6 +1906,26 @@ babel_show_entries(struct proto *P) } } FIB_WALK_END; +} + +void +babel_show_entries(struct proto *P) +{ + struct babel_proto *p = (void *) P; + + if (p->p.proto_state != PS_UP) + { + cli_msg(-1025, "%s: is not up", p->p.name); + cli_msg(0, ""); + return; + } + + cli_msg(-1025, "%s:", p->p.name); + cli_msg(-1025, "%-29s %-23s %6s %5s %7s %7s", + "Prefix", "Router ID", "Metric", "Seqno", "Expires", "Sources"); + + babel_show_rtable(p, &p->ip4_rtable); + babel_show_rtable(p, &p->ip6_rtable); cli_msg(0, ""); } @@ -2028,8 +2101,10 @@ static struct proto * babel_init(struct proto_config *CF) { struct proto *P = proto_new(CF); + struct babel_proto *p = (void *) P; - P->main_channel = proto_add_channel(P, proto_cf_main_channel(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)); P->if_notify = babel_if_notify; P->rt_notify = babel_rt_notify; @@ -2048,8 +2123,11 @@ babel_start(struct proto *P) struct babel_proto *p = (void *) P; struct babel_config *cf = (void *) P->cf; - fib_init(&p->rtable, P->pool, NET_IP6, sizeof(struct babel_entry), + 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), + OFFSETOF(struct babel_entry, n), 0, babel_init_entry); + init_list(&p->interfaces); p->timer = tm_new_set(P->pool, babel_timer, p, 0, 1); tm_start(p->timer, 2); @@ -2099,7 +2177,8 @@ babel_reconfigure(struct proto *P, struct proto_config *CF) TRACE(D_EVENTS, "Reconfiguring"); - if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) + 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)))) return 0; p->p.cf = CF; @@ -2117,7 +2196,7 @@ struct protocol proto_babel = { .template = "babel%d", .attr_class = EAP_BABEL, .preference = DEF_PREF_BABEL, - .channel_mask = NB_IP6, + .channel_mask = NB_IP, .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 792c9d60..236c1a3b 100644 --- a/proto/babel/babel.h +++ b/proto/babel/babel.h @@ -116,12 +116,20 @@ struct babel_iface_config { u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */ int tx_tos; int tx_priority; + + ip_addr next_hop_v4; + ip_addr next_hop_v6; }; struct babel_proto { struct proto p; timer *timer; - struct fib rtable; + struct fib ip4_rtable; + struct fib ip6_rtable; + + struct channel *ip4_channel; + struct channel *ip6_channel; + list interfaces; /* Interfaces we really know about (struct babel_iface) */ u64 router_id; u16 update_seqno; /* To be increased on request */ diff --git a/proto/babel/config.Y b/proto/babel/config.Y index cf8983fa..f22ae61c 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -21,7 +21,8 @@ CF_DEFINES CF_DECLS CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED, -WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC) +WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC, NEXT, HOP, +IPV4, IPV6) CF_GRAMMAR @@ -30,7 +31,6 @@ CF_ADDTO(proto, babel_proto) babel_proto_start: proto_start BABEL { this_proto = proto_config_new(&proto_babel, $1); - this_proto->net_type = NET_IP6; init_list(&BABEL_CFG->iface_list); }; @@ -98,6 +98,8 @@ babel_iface_item: | TX tos { BABEL_IFACE->tx_tos = $2; } | TX PRIORITY expr { BABEL_IFACE->tx_priority = $3; } | CHECK LINK bool { BABEL_IFACE->check_link = $3; } + | NEXT HOP IPV4 ipa { BABEL_IFACE->next_hop_v4 = $4; if (!ipa_is_ip4($4)) cf_error("Must be an IPv4 address"); } + | NEXT HOP IPV6 ipa { BABEL_IFACE->next_hop_v6 = $4; if (!ipa_is_ip6($4)) cf_error("Must be an IPv6 address"); } ; babel_iface_opts: diff --git a/proto/babel/packets.c b/proto/babel/packets.c index 70cfc196..0f70898e 100644 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@ -112,7 +112,8 @@ struct babel_parse_state { struct babel_proto *proto; struct babel_iface *ifa; ip_addr saddr; - ip_addr next_hop; + ip_addr next_hop_v4; + ip_addr next_hop_v6; u64 router_id; /* Router ID used in subsequent updates */ u8 def_ip6_prefix[16]; /* Implicit IPv6 prefix in network order */ u8 def_ip4_prefix[4]; /* Implicit IPv4 prefix in network order */ @@ -130,7 +131,8 @@ enum parse_result { struct babel_write_state { u64 router_id; u8 router_id_seen; -// ip_addr next_hop; + u8 next_hop_v4_seen; + u8 next_hop_v6_seen; }; @@ -163,6 +165,21 @@ put_time16(void *p, u16 v) } static inline void +read_ip4_px(net_addr *n, const void *p, uint plen) +{ + ip4_addr addr = {0}; + memcpy(&addr, p, BYTES(plen)); + net_fill_ip4(n, ip4_ntoh(addr), plen); +} + +static inline void +put_ip4_px(void *p, net_addr *n) +{ + ip4_addr addr = ip4_hton(net4_prefix(n)); + memcpy(p, &addr, NET_SIZE(n)); +} + +static inline void read_ip6_px(net_addr *n, const void *p, uint plen) { ip6_addr addr = IPA_NONE; @@ -432,21 +449,24 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED, return PARSE_ERROR; case BABEL_AE_IP4: - /* TODO */ + if (TLV_OPT_LENGTH(tlv) < sizeof(ip4_addr)) + return PARSE_ERROR; + + state->next_hop_v4 = ipa_from_ip4(get_ip4(&tlv->addr)); return PARSE_IGNORE; case BABEL_AE_IP6: if (TLV_OPT_LENGTH(tlv) < sizeof(ip6_addr)) return PARSE_ERROR; - state->next_hop = ipa_from_ip6(get_ip6(&tlv->addr)); + state->next_hop_v6 = ipa_from_ip6(get_ip6(&tlv->addr)); return PARSE_IGNORE; case BABEL_AE_IP6_LL: if (TLV_OPT_LENGTH(tlv) < 8) return PARSE_ERROR; - state->next_hop = ipa_from_ip6(get_ip6_ll(&tlv->addr)); + state->next_hop_v6 = ipa_from_ip6(get_ip6_ll(&tlv->addr)); return PARSE_IGNORE; default: @@ -456,6 +476,47 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED, return PARSE_IGNORE; } +/* This is called directly from babel_write_update() and returns -1 if a next + hop should be written but there is not enough space. */ +static int +babel_write_next_hop(struct babel_tlv *hdr, ip_addr addr, + struct babel_write_state *state, uint max_len) +{ + struct babel_tlv_next_hop *tlv = (void *) hdr; + + + if (ipa_is_ip4(addr) && !state->next_hop_v4_seen) { + uint len = sizeof(struct babel_tlv_next_hop) + sizeof(ip4_addr); + if (len > max_len) + return -1; + + TLV_HDR(tlv, BABEL_TLV_NEXT_HOP, len); + + tlv->ae = BABEL_AE_IP4; + put_ip4(&tlv->addr, ipa_to_ip4(addr)); + state->next_hop_v4_seen = 1; + + return len; + } + else if (ipa_is_ip6(addr) && ip6_nonzero(addr) && !state->next_hop_v6_seen) + { + uint len = sizeof(struct babel_tlv_next_hop) + sizeof(ip6_addr); + if (len > max_len) + return -1; + + TLV_HDR(tlv, BABEL_TLV_NEXT_HOP, len); + + tlv->ae = BABEL_AE_IP6; + put_ip6(&tlv->addr, ipa_to_ip6(addr)); + state->next_hop_v6_seen = 1; + + return len; + } + + return 0; +} + + static int babel_read_update(struct babel_tlv *hdr, union babel_msg *m, struct babel_parse_state *state) @@ -488,8 +549,33 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, break; case BABEL_AE_IP4: - /* TODO */ - return PARSE_IGNORE; + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + /* Cannot omit data if there is no saved prefix */ + if (tlv->omitted && !state->def_ip4_prefix_seen) + return PARSE_ERROR; + + /* Need next hop for v4 routes */ + if (ipa_zero(state->next_hop_v4)) + return PARSE_ERROR; + + /* Merge saved prefix and received prefix parts */ + memcpy(buf, state->def_ip4_prefix, tlv->omitted); + memcpy(buf + tlv->omitted, tlv->addr, len); + + ip4_addr prefix4 = get_ip4(buf); + net_fill_ip4(&msg->net, prefix4, tlv->plen); + + if (tlv->flags & BABEL_FLAG_DEF_PREFIX) + { + put_ip4(state->def_ip4_prefix, prefix4); + state->def_ip4_prefix_seen = 1; + } + + msg->next_hop = state->next_hop_v4; + + break; case BABEL_AE_IP6: if (tlv->plen > IP6_MAX_PREFIX_LENGTH) @@ -503,20 +589,23 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, memcpy(buf, state->def_ip6_prefix, tlv->omitted); memcpy(buf + tlv->omitted, tlv->addr, len); - ip6_addr prefix = get_ip6(buf); - net_fill_ip6(&msg->net, prefix, tlv->plen); + ip6_addr prefix6 = get_ip6(buf); + net_fill_ip6(&msg->net, prefix6, tlv->plen); if (tlv->flags & BABEL_FLAG_DEF_PREFIX) { - put_ip6(state->def_ip6_prefix, prefix); + put_ip6(state->def_ip6_prefix, prefix6); state->def_ip6_prefix_seen = 1; } if (tlv->flags & BABEL_FLAG_ROUTER_ID) { - state->router_id = ((u64) _I2(prefix)) << 32 | _I3(prefix); + state->router_id = ((u64) _I2(prefix6)) << 32 | _I3(prefix6); state->router_id_seen = 1; } + + msg->next_hop = state->next_hop_v6; + break; case BABEL_AE_IP6_LL: @@ -535,7 +624,6 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, } msg->router_id = state->router_id; - msg->next_hop = state->next_hop; msg->sender = state->saddr; return PARSE_SUCCESS; @@ -554,7 +642,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, * both of them. There is enough space for the Router-ID TLV, because * sizeof(struct babel_tlv_router_id) == sizeof(struct babel_tlv_update). * - * Router ID is not used for retractions, so do not us it in such case. + * Router ID is not used for retractions, so do not use it in such case. */ if ((msg->metric < BABEL_INFINITY) && (!state->router_id_seen || (msg->router_id != state->router_id))) @@ -563,6 +651,16 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, tlv = (struct babel_tlv_update *) NEXT_TLV(tlv); } + int l = babel_write_next_hop((void *)tlv, msg->next_hop, state, max_len - len0); + if (l < 0) + return 0; + + if (l) + { + len0 += l; + tlv = (struct babel_tlv_update *) NEXT_TLV(tlv); + } + uint len = sizeof(struct babel_tlv_update) + NET_SIZE(&msg->net); if (len0 + len > max_len) @@ -576,6 +674,12 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, tlv->ae = BABEL_AE_WILDCARD; tlv->plen = 0; } + else if(msg->net.type == NET_IP4) + { + tlv->ae = BABEL_AE_IP4; + tlv->plen = net4_pxlen(&msg->net); + put_ip4_px(tlv->addr, &msg->net); + } else { tlv->ae = BABEL_AE_IP6; @@ -610,8 +714,14 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, return PARSE_SUCCESS; case BABEL_AE_IP4: - /* TODO */ - return PARSE_IGNORE; + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen)) + return PARSE_ERROR; + + read_ip4_px(&msg->net, tlv->addr, tlv->plen); + return PARSE_SUCCESS; case BABEL_AE_IP6: if (tlv->plen > IP6_MAX_PREFIX_LENGTH) @@ -652,6 +762,12 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m, tlv->ae = BABEL_AE_WILDCARD; tlv->plen = 0; } + else if (msg->net.type == NET_IP4) + { + tlv->ae = BABEL_AE_IP4; + tlv->plen = net4_pxlen(&msg->net); + put_ip4_px(tlv->addr, &msg->net); + } else { tlv->ae = BABEL_AE_IP6; @@ -684,8 +800,14 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m, return PARSE_ERROR; case BABEL_AE_IP4: - /* TODO */ - return PARSE_IGNORE; + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen)) + return PARSE_ERROR; + + read_ip4_px(&msg->net, tlv->addr, tlv->plen); + return PARSE_SUCCESS; case BABEL_AE_IP6: if (tlv->plen > IP6_MAX_PREFIX_LENGTH) @@ -720,12 +842,21 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m, return 0; TLV_HDR(tlv, BABEL_TLV_SEQNO_REQUEST, len); - tlv->ae = BABEL_AE_IP6; - tlv->plen = net6_pxlen(&msg->net); + if (msg->net.type == NET_IP4) + { + tlv->ae = BABEL_AE_IP4; + tlv->plen = net4_pxlen(&msg->net); + put_ip4_px(tlv->addr, &msg->net); + } + else + { + tlv->ae = BABEL_AE_IP6; + tlv->plen = net6_pxlen(&msg->net); + put_ip6_px(tlv->addr, &msg->net); + } put_u16(&tlv->seqno, msg->seqno); tlv->hop_count = msg->hop_count; put_u64(&tlv->router_id, msg->router_id); - put_ip6_px(tlv->addr, &msg->net); return len; } @@ -935,10 +1066,10 @@ babel_process_packet(struct babel_pkt_header *pkt, int len, byte *end = (byte *)pkt + plen; struct babel_parse_state state = { - .proto = p, - .ifa = ifa, - .saddr = saddr, - .next_hop = saddr, + .proto = p, + .ifa = ifa, + .saddr = saddr, + .next_hop_v6 = saddr, }; if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) -- 2.13.0
On Mon, May 29, 2017 at 10:24:57PM +0200, Toke Høiland-Jørgensen wrote:
This adds support for dual-stack v4/v6 operation to the Babel protocol. Routing messages will be exchanged over IPv6, but IPv4 routes can be carried in the messages being exchanged. This matches how the reference Babel implementation (babeld) works.
Hi Thanks for the patch. I have one major remark - If i undestand it correctly, both IPv4 and IPv6 FIBs are always created and used, but IPv4 and IPv6 channels may or may not be configured and created. Although when a channel is not configured, Babel would not propagate routes (as they have to be imported to nest and back exported to Babel to be propagated outside), it is still strange to keep received routes. Also channel_is_active() is called but channel may not even exist? IMHO both IPv4 and IPv6 should be optional and implicitly enabled by definition of associated channels. -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
This adds support for dual-stack v4/v6 operation to the Babel protocol. Routing messages will be exchanged over IPv6, but IPv4 routes can be carried in the messages being exchanged. This matches how the reference Babel implementation (babeld) works. The nexthop address for v4 can be configured per interface, and will default to the first available IPv4 address on the given interface. For symmetry, a configuration option to configure the IPv6 nexthop address is also added. Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> --- Changes since v1: - Handle channel vars being NULL - Skip handling of updates for non-configured address family entirely proto/babel/babel.c | 162 +++++++++++++++++++++++++++++++++++---------- proto/babel/babel.h | 10 ++- proto/babel/config.Y | 6 +- proto/babel/packets.c | 179 +++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 294 insertions(+), 63 deletions(-) diff --git a/proto/babel/babel.c b/proto/babel/babel.c index 1b1d9f62..0b897980 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -39,6 +39,11 @@ #define OUR_ROUTE(r) (r->neigh == NULL) +#define V4_ACTIVE(p) (p->ip4_channel != NULL && \ + p->ip4_channel->channel_state == CS_UP) +#define V6_ACTIVE(p) (p->ip6_channel != NULL && \ + p->ip6_channel->channel_state == CS_UP) +#define AF_ACTIVE(p, n) (n.type == NET_IP4 ? V4_ACTIVE(p) : V6_ACTIVE(p)) /* * Is one number greater or equal than another mod 2^16? This is based on the @@ -78,13 +83,15 @@ babel_init_entry(void *E) static inline struct babel_entry * babel_find_entry(struct babel_proto *p, const net_addr *n) { - return fib_find(&p->rtable, n); + struct fib *rtable = (n->type == NET_IP4) ? &p->ip4_rtable : &p->ip6_rtable; + return fib_find(rtable, n); } static struct babel_entry * babel_get_entry(struct babel_proto *p, const net_addr *n) { - struct babel_entry *e = fib_get(&p->rtable, n); + struct fib *rtable = (n->type == NET_IP4) ? &p->ip4_rtable : &p->ip6_rtable; + struct babel_entry *e = fib_get(rtable, n); e->proto = p; return e; } @@ -224,15 +231,15 @@ babel_refresh_route(struct babel_route *r) } static void -babel_expire_routes(struct babel_proto *p) +babel_expire_rtable(struct fib *rtable) { struct babel_route *r, *rx; struct fib_iterator fit; - FIB_ITERATE_INIT(&fit, &p->rtable); + FIB_ITERATE_INIT(&fit, rtable); loop: - FIB_ITERATE_START(&p->rtable, &fit, struct babel_entry, e) + FIB_ITERATE_START(rtable, &fit, struct babel_entry, e) { int changed = 0; @@ -253,7 +260,7 @@ loop: /* * We have to restart the iteration because there may be a cascade of * synchronous events babel_select_route() -> nest table change -> - * babel_rt_notify() -> p->rtable change, invalidating hidden variables. + * babel_rt_notify() -> rtable change, invalidating hidden variables. */ FIB_ITERATE_PUT(&fit); @@ -267,13 +274,20 @@ loop: if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes)) { FIB_ITERATE_PUT(&fit); - fib_delete(&p->rtable, e); + fib_delete(rtable, e); goto loop; } } FIB_ITERATE_END; } +static void +babel_expire_routes(struct babel_proto *p) +{ + babel_expire_rtable(&p->ip4_rtable); + babel_expire_rtable(&p->ip6_rtable); +} + static struct babel_neighbor * babel_find_neighbor(struct babel_iface *ifa, ip_addr addr) { @@ -468,6 +482,12 @@ static void babel_announce_rte(struct babel_proto *p, struct babel_entry *e) { struct babel_route *r = e->selected_in; + struct channel *c; + + if (e->n.addr->type == NET_IP4) + c = p->ip4_channel; + else + c = p->ip6_channel; if (r) { @@ -490,12 +510,12 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e) rte->u.babel.router_id = r->router_id; rte->pflags = 0; - rte_update(&p->p, e->n.addr, rte); + rte_update2(c, e->n.addr, rte, p->p.main_source); } else { /* Retraction */ - rte_update(&p->p, e->n.addr, NULL); + rte_update2(c, e->n.addr, NULL, p->p.main_source); } } @@ -740,11 +760,11 @@ babel_unicast_seqno_request(struct babel_route *r) * transmitted entry is updated. */ static void -babel_send_update(struct babel_iface *ifa, bird_clock_t changed) +babel_send_update_rtable(struct babel_iface *ifa, bird_clock_t changed, struct fib *rtable) { struct babel_proto *p = ifa->proto; - FIB_WALK(&p->rtable, struct babel_entry, e) + FIB_WALK(rtable, struct babel_entry, e) { struct babel_route *r = e->selected_out; @@ -774,6 +794,9 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed) msg.update.router_id = r->router_id; net_copy(&msg.update.net, e->n.addr); + msg.update.next_hop = ((e->n.addr->type == NET_IP4) ? + ifa->cf->next_hop_v4 : ifa->cf->next_hop_v6); + babel_enqueue(&msg, ifa); /* Update feasibility distance for redistributed routes */ @@ -794,6 +817,15 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed) } static void +babel_send_update(struct babel_iface *ifa, bird_clock_t changed) +{ + struct babel_proto *p = ifa->proto; + + babel_send_update_rtable(ifa, changed, &p->ip4_rtable); + babel_send_update_rtable(ifa, changed, &p->ip6_rtable); +} + +static void babel_trigger_iface_update(struct babel_iface *ifa) { struct babel_proto *p = ifa->proto; @@ -1073,6 +1105,12 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa) return; } + if (!AF_ACTIVE(p, msg->net)) + { + DBG("Babel: Ignoring update for inactive address family.\n"); + return; + } + /* * RFC section 3.5.4: * @@ -1477,12 +1515,20 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con struct ifa *addr; WALK_LIST(addr, new->addrs) + { if (ipa_is_link_local(addr->ip)) ifa->addr = addr->ip; + if (ipa_zero(ifa->cf->next_hop_v4) && ipa_is_ip4(addr->ip)) + ifa->cf->next_hop_v4 = addr->ip; + } + if (ipa_zero(ifa->addr)) log(L_WARN "%s: Cannot find link-local addr on %s", p->p.name, new->name); + if (ipa_zero(ifa->cf->next_hop_v4) && V4_ACTIVE(p)) + log(L_WARN "%s: Cannot find IPv4 next hop addr on %s", p->p.name, new->name); + init_list(&ifa->neigh_list); ifa->hello_seqno = 1; @@ -1570,6 +1616,26 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b (new->tx_priority != old->tx_priority)) return 0; + + if (ipa_zero(new->next_hop_v4)) + { + struct ifa *addr; + WALK_LIST(addr, ifa->iface->addrs) + { + if (ipa_is_ip4(addr->ip)) + { + new->next_hop_v4 = addr->ip; + break; + } + } + } + + /* If no v4 address is found for next_hop we give up on reconfiguring and hope + that we can find one when the interface is brought down and back up + again. */ + if (ipa_zero(new->next_hop_v4) && V4_ACTIVE(p)) + return 0; + TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name); ifa->cf = new; @@ -1680,9 +1746,11 @@ babel_dump_iface(struct babel_iface *ifa) { struct babel_neighbor *n; - debug("Babel: Interface %s addr %I rxcost %d type %d hello seqno %d intervals %d %d\n", + debug("Babel: Interface %s addr %I rxcost %d type %d hello seqno %d intervals %d %d", ifa->ifname, ifa->addr, ifa->cf->rxcost, ifa->cf->type, ifa->hello_seqno, ifa->cf->hello_interval, ifa->cf->update_interval); + debug(" next hop v4 %I next hop v6 %I\n", ifa->cf->next_hop_v4, + ipa_zero(ifa->cf->next_hop_v6) ? ifa->addr : ifa->cf->next_hop_v6); WALK_LIST(n, ifa->neigh_list) { debug(" "); babel_dump_neighbor(n); } @@ -1699,7 +1767,12 @@ babel_dump(struct proto *P) WALK_LIST(ifa, p->interfaces) babel_dump_iface(ifa); - FIB_WALK(&p->rtable, struct babel_entry, e) + FIB_WALK(&p->ip4_rtable, struct babel_entry, e) + { + babel_dump_entry(e); + } + FIB_WALK_END; + FIB_WALK(&p->ip6_rtable, struct babel_entry, e) { babel_dump_entry(e); } @@ -1749,8 +1822,9 @@ babel_show_interfaces(struct proto *P, char *iff) } cli_msg(-1023, "%s:", p->p.name); - cli_msg(-1023, "%-10s %-6s %7s %6s %6s", - "Interface", "State", "RX cost", "Nbrs", "Timer"); + cli_msg(-1023, "%-10s %-6s %7s %6s %6s %-15s %s", + "Interface", "State", "RX cost", "Nbrs", "Timer", + "Next hop (v4)", "Next hop (v6)"); WALK_LIST(ifa, p->interfaces) { @@ -1762,8 +1836,11 @@ babel_show_interfaces(struct proto *P, char *iff) nbrs++; int timer = MIN(ifa->next_regular, ifa->next_hello) - now; - cli_msg(-1023, "%-10s %-6s %7u %6u %6u", - ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->rxcost, nbrs, MAX(timer, 0)); + cli_msg(-1023, "%-10s %-6s %7u %6u %6u %-15I %I", + ifa->iface->name, (ifa->up ? "Up" : "Down"), + ifa->cf->rxcost, nbrs, MAX(timer, 0), + ifa->cf->next_hop_v4, + ipa_zero(ifa->cf->next_hop_v6) ? ifa->addr : ifa->cf->next_hop_v6); } cli_msg(0, ""); @@ -1808,27 +1885,14 @@ babel_show_neighbors(struct proto *P, char *iff) cli_msg(0, ""); } -void -babel_show_entries(struct proto *P) +void babel_show_rtable(struct babel_proto *p, struct fib *rtable) { - struct babel_proto *p = (void *) P; struct babel_source *s = NULL; struct babel_route *r = NULL; char ridbuf[ROUTER_ID_64_LENGTH+1]; - if (p->p.proto_state != PS_UP) - { - cli_msg(-1025, "%s: is not up", p->p.name); - cli_msg(0, ""); - return; - } - - cli_msg(-1025, "%s:", p->p.name); - cli_msg(-1025, "%-29s %-23s %6s %5s %7s %7s", - "Prefix", "Router ID", "Metric", "Seqno", "Expires", "Sources"); - - FIB_WALK(&p->rtable, struct babel_entry, e) + FIB_WALK(rtable, struct babel_entry, e) { r = e->selected_in ? e->selected_in : e->selected_out; @@ -1853,6 +1917,26 @@ babel_show_entries(struct proto *P) } } FIB_WALK_END; +} + +void +babel_show_entries(struct proto *P) +{ + struct babel_proto *p = (void *) P; + + if (p->p.proto_state != PS_UP) + { + cli_msg(-1025, "%s: is not up", p->p.name); + cli_msg(0, ""); + return; + } + + cli_msg(-1025, "%s:", p->p.name); + cli_msg(-1025, "%-29s %-23s %6s %5s %7s %7s", + "Prefix", "Router ID", "Metric", "Seqno", "Expires", "Sources"); + + babel_show_rtable(p, &p->ip4_rtable); + babel_show_rtable(p, &p->ip6_rtable); cli_msg(0, ""); } @@ -2028,8 +2112,10 @@ static struct proto * babel_init(struct proto_config *CF) { struct proto *P = proto_new(CF); + struct babel_proto *p = (void *) P; - P->main_channel = proto_add_channel(P, proto_cf_main_channel(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)); P->if_notify = babel_if_notify; P->rt_notify = babel_rt_notify; @@ -2048,8 +2134,11 @@ babel_start(struct proto *P) struct babel_proto *p = (void *) P; struct babel_config *cf = (void *) P->cf; - fib_init(&p->rtable, P->pool, NET_IP6, sizeof(struct babel_entry), + 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), + OFFSETOF(struct babel_entry, n), 0, babel_init_entry); + init_list(&p->interfaces); p->timer = tm_new_set(P->pool, babel_timer, p, 0, 1); tm_start(p->timer, 2); @@ -2099,7 +2188,8 @@ babel_reconfigure(struct proto *P, struct proto_config *CF) TRACE(D_EVENTS, "Reconfiguring"); - if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) + 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)))) return 0; p->p.cf = CF; @@ -2117,7 +2207,7 @@ struct protocol proto_babel = { .template = "babel%d", .attr_class = EAP_BABEL, .preference = DEF_PREF_BABEL, - .channel_mask = NB_IP6, + .channel_mask = NB_IP, .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 792c9d60..236c1a3b 100644 --- a/proto/babel/babel.h +++ b/proto/babel/babel.h @@ -116,12 +116,20 @@ struct babel_iface_config { u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */ int tx_tos; int tx_priority; + + ip_addr next_hop_v4; + ip_addr next_hop_v6; }; struct babel_proto { struct proto p; timer *timer; - struct fib rtable; + struct fib ip4_rtable; + struct fib ip6_rtable; + + struct channel *ip4_channel; + struct channel *ip6_channel; + list interfaces; /* Interfaces we really know about (struct babel_iface) */ u64 router_id; u16 update_seqno; /* To be increased on request */ diff --git a/proto/babel/config.Y b/proto/babel/config.Y index cf8983fa..f22ae61c 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -21,7 +21,8 @@ CF_DEFINES CF_DECLS CF_KEYWORDS(BABEL, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, WIRED, -WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC) +WIRELESS, RX, TX, BUFFER, LENGTH, CHECK, LINK, BABEL_METRIC, NEXT, HOP, +IPV4, IPV6) CF_GRAMMAR @@ -30,7 +31,6 @@ CF_ADDTO(proto, babel_proto) babel_proto_start: proto_start BABEL { this_proto = proto_config_new(&proto_babel, $1); - this_proto->net_type = NET_IP6; init_list(&BABEL_CFG->iface_list); }; @@ -98,6 +98,8 @@ babel_iface_item: | TX tos { BABEL_IFACE->tx_tos = $2; } | TX PRIORITY expr { BABEL_IFACE->tx_priority = $3; } | CHECK LINK bool { BABEL_IFACE->check_link = $3; } + | NEXT HOP IPV4 ipa { BABEL_IFACE->next_hop_v4 = $4; if (!ipa_is_ip4($4)) cf_error("Must be an IPv4 address"); } + | NEXT HOP IPV6 ipa { BABEL_IFACE->next_hop_v6 = $4; if (!ipa_is_ip6($4)) cf_error("Must be an IPv6 address"); } ; babel_iface_opts: diff --git a/proto/babel/packets.c b/proto/babel/packets.c index 70cfc196..0f70898e 100644 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@ -112,7 +112,8 @@ struct babel_parse_state { struct babel_proto *proto; struct babel_iface *ifa; ip_addr saddr; - ip_addr next_hop; + ip_addr next_hop_v4; + ip_addr next_hop_v6; u64 router_id; /* Router ID used in subsequent updates */ u8 def_ip6_prefix[16]; /* Implicit IPv6 prefix in network order */ u8 def_ip4_prefix[4]; /* Implicit IPv4 prefix in network order */ @@ -130,7 +131,8 @@ enum parse_result { struct babel_write_state { u64 router_id; u8 router_id_seen; -// ip_addr next_hop; + u8 next_hop_v4_seen; + u8 next_hop_v6_seen; }; @@ -163,6 +165,21 @@ put_time16(void *p, u16 v) } static inline void +read_ip4_px(net_addr *n, const void *p, uint plen) +{ + ip4_addr addr = {0}; + memcpy(&addr, p, BYTES(plen)); + net_fill_ip4(n, ip4_ntoh(addr), plen); +} + +static inline void +put_ip4_px(void *p, net_addr *n) +{ + ip4_addr addr = ip4_hton(net4_prefix(n)); + memcpy(p, &addr, NET_SIZE(n)); +} + +static inline void read_ip6_px(net_addr *n, const void *p, uint plen) { ip6_addr addr = IPA_NONE; @@ -432,21 +449,24 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED, return PARSE_ERROR; case BABEL_AE_IP4: - /* TODO */ + if (TLV_OPT_LENGTH(tlv) < sizeof(ip4_addr)) + return PARSE_ERROR; + + state->next_hop_v4 = ipa_from_ip4(get_ip4(&tlv->addr)); return PARSE_IGNORE; case BABEL_AE_IP6: if (TLV_OPT_LENGTH(tlv) < sizeof(ip6_addr)) return PARSE_ERROR; - state->next_hop = ipa_from_ip6(get_ip6(&tlv->addr)); + state->next_hop_v6 = ipa_from_ip6(get_ip6(&tlv->addr)); return PARSE_IGNORE; case BABEL_AE_IP6_LL: if (TLV_OPT_LENGTH(tlv) < 8) return PARSE_ERROR; - state->next_hop = ipa_from_ip6(get_ip6_ll(&tlv->addr)); + state->next_hop_v6 = ipa_from_ip6(get_ip6_ll(&tlv->addr)); return PARSE_IGNORE; default: @@ -456,6 +476,47 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED, return PARSE_IGNORE; } +/* This is called directly from babel_write_update() and returns -1 if a next + hop should be written but there is not enough space. */ +static int +babel_write_next_hop(struct babel_tlv *hdr, ip_addr addr, + struct babel_write_state *state, uint max_len) +{ + struct babel_tlv_next_hop *tlv = (void *) hdr; + + + if (ipa_is_ip4(addr) && !state->next_hop_v4_seen) { + uint len = sizeof(struct babel_tlv_next_hop) + sizeof(ip4_addr); + if (len > max_len) + return -1; + + TLV_HDR(tlv, BABEL_TLV_NEXT_HOP, len); + + tlv->ae = BABEL_AE_IP4; + put_ip4(&tlv->addr, ipa_to_ip4(addr)); + state->next_hop_v4_seen = 1; + + return len; + } + else if (ipa_is_ip6(addr) && ip6_nonzero(addr) && !state->next_hop_v6_seen) + { + uint len = sizeof(struct babel_tlv_next_hop) + sizeof(ip6_addr); + if (len > max_len) + return -1; + + TLV_HDR(tlv, BABEL_TLV_NEXT_HOP, len); + + tlv->ae = BABEL_AE_IP6; + put_ip6(&tlv->addr, ipa_to_ip6(addr)); + state->next_hop_v6_seen = 1; + + return len; + } + + return 0; +} + + static int babel_read_update(struct babel_tlv *hdr, union babel_msg *m, struct babel_parse_state *state) @@ -488,8 +549,33 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, break; case BABEL_AE_IP4: - /* TODO */ - return PARSE_IGNORE; + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + /* Cannot omit data if there is no saved prefix */ + if (tlv->omitted && !state->def_ip4_prefix_seen) + return PARSE_ERROR; + + /* Need next hop for v4 routes */ + if (ipa_zero(state->next_hop_v4)) + return PARSE_ERROR; + + /* Merge saved prefix and received prefix parts */ + memcpy(buf, state->def_ip4_prefix, tlv->omitted); + memcpy(buf + tlv->omitted, tlv->addr, len); + + ip4_addr prefix4 = get_ip4(buf); + net_fill_ip4(&msg->net, prefix4, tlv->plen); + + if (tlv->flags & BABEL_FLAG_DEF_PREFIX) + { + put_ip4(state->def_ip4_prefix, prefix4); + state->def_ip4_prefix_seen = 1; + } + + msg->next_hop = state->next_hop_v4; + + break; case BABEL_AE_IP6: if (tlv->plen > IP6_MAX_PREFIX_LENGTH) @@ -503,20 +589,23 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, memcpy(buf, state->def_ip6_prefix, tlv->omitted); memcpy(buf + tlv->omitted, tlv->addr, len); - ip6_addr prefix = get_ip6(buf); - net_fill_ip6(&msg->net, prefix, tlv->plen); + ip6_addr prefix6 = get_ip6(buf); + net_fill_ip6(&msg->net, prefix6, tlv->plen); if (tlv->flags & BABEL_FLAG_DEF_PREFIX) { - put_ip6(state->def_ip6_prefix, prefix); + put_ip6(state->def_ip6_prefix, prefix6); state->def_ip6_prefix_seen = 1; } if (tlv->flags & BABEL_FLAG_ROUTER_ID) { - state->router_id = ((u64) _I2(prefix)) << 32 | _I3(prefix); + state->router_id = ((u64) _I2(prefix6)) << 32 | _I3(prefix6); state->router_id_seen = 1; } + + msg->next_hop = state->next_hop_v6; + break; case BABEL_AE_IP6_LL: @@ -535,7 +624,6 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, } msg->router_id = state->router_id; - msg->next_hop = state->next_hop; msg->sender = state->saddr; return PARSE_SUCCESS; @@ -554,7 +642,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, * both of them. There is enough space for the Router-ID TLV, because * sizeof(struct babel_tlv_router_id) == sizeof(struct babel_tlv_update). * - * Router ID is not used for retractions, so do not us it in such case. + * Router ID is not used for retractions, so do not use it in such case. */ if ((msg->metric < BABEL_INFINITY) && (!state->router_id_seen || (msg->router_id != state->router_id))) @@ -563,6 +651,16 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, tlv = (struct babel_tlv_update *) NEXT_TLV(tlv); } + int l = babel_write_next_hop((void *)tlv, msg->next_hop, state, max_len - len0); + if (l < 0) + return 0; + + if (l) + { + len0 += l; + tlv = (struct babel_tlv_update *) NEXT_TLV(tlv); + } + uint len = sizeof(struct babel_tlv_update) + NET_SIZE(&msg->net); if (len0 + len > max_len) @@ -576,6 +674,12 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, tlv->ae = BABEL_AE_WILDCARD; tlv->plen = 0; } + else if(msg->net.type == NET_IP4) + { + tlv->ae = BABEL_AE_IP4; + tlv->plen = net4_pxlen(&msg->net); + put_ip4_px(tlv->addr, &msg->net); + } else { tlv->ae = BABEL_AE_IP6; @@ -610,8 +714,14 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, return PARSE_SUCCESS; case BABEL_AE_IP4: - /* TODO */ - return PARSE_IGNORE; + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen)) + return PARSE_ERROR; + + read_ip4_px(&msg->net, tlv->addr, tlv->plen); + return PARSE_SUCCESS; case BABEL_AE_IP6: if (tlv->plen > IP6_MAX_PREFIX_LENGTH) @@ -652,6 +762,12 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m, tlv->ae = BABEL_AE_WILDCARD; tlv->plen = 0; } + else if (msg->net.type == NET_IP4) + { + tlv->ae = BABEL_AE_IP4; + tlv->plen = net4_pxlen(&msg->net); + put_ip4_px(tlv->addr, &msg->net); + } else { tlv->ae = BABEL_AE_IP6; @@ -684,8 +800,14 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m, return PARSE_ERROR; case BABEL_AE_IP4: - /* TODO */ - return PARSE_IGNORE; + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen)) + return PARSE_ERROR; + + read_ip4_px(&msg->net, tlv->addr, tlv->plen); + return PARSE_SUCCESS; case BABEL_AE_IP6: if (tlv->plen > IP6_MAX_PREFIX_LENGTH) @@ -720,12 +842,21 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m, return 0; TLV_HDR(tlv, BABEL_TLV_SEQNO_REQUEST, len); - tlv->ae = BABEL_AE_IP6; - tlv->plen = net6_pxlen(&msg->net); + if (msg->net.type == NET_IP4) + { + tlv->ae = BABEL_AE_IP4; + tlv->plen = net4_pxlen(&msg->net); + put_ip4_px(tlv->addr, &msg->net); + } + else + { + tlv->ae = BABEL_AE_IP6; + tlv->plen = net6_pxlen(&msg->net); + put_ip6_px(tlv->addr, &msg->net); + } put_u16(&tlv->seqno, msg->seqno); tlv->hop_count = msg->hop_count; put_u64(&tlv->router_id, msg->router_id); - put_ip6_px(tlv->addr, &msg->net); return len; } @@ -935,10 +1066,10 @@ babel_process_packet(struct babel_pkt_header *pkt, int len, byte *end = (byte *)pkt + plen; struct babel_parse_state state = { - .proto = p, - .ifa = ifa, - .saddr = saddr, - .next_hop = saddr, + .proto = p, + .ifa = ifa, + .saddr = saddr, + .next_hop_v6 = saddr, }; if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) -- 2.13.0
On Tue, May 30, 2017 at 03:20:42PM +0200, Toke Høiland-Jørgensen wrote:
This adds support for dual-stack v4/v6 operation to the Babel protocol. Routing messages will be exchanged over IPv6, but IPv4 routes can be carried in the messages being exchanged. This matches how the reference Babel implementation (babeld) works.
Hi Merged. I did some minor modifications. First, adding 'active' next_hop fields to struct babel_iface, so value in babel_iface_config is kept unmodified. Second, adding next_hop fields instead of just next_hop_seen fields to struct babel_write_state, so it is ready for third-party next hops. BTW, are third-party next hops allowed in Babel? I checked RFC 6126 but found nothing. Also note that the new next hop options are missing its documentation. Perhaps you could send following patch with that? -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
participants (2)
-
Ondrej Zajicek -
Toke Høiland-Jørgensen