Commit 4324025f authored by Ondřej Zajíček's avatar Ondřej Zajíček

Babel: Add support for dual-stack IPv4/IPv6 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.

Thanks to Toke Høiland-Jørgensen <toke@toke.dk> for the patch.
parent 801fd81e
This diff is collapsed.
......@@ -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_ip4;
ip_addr next_hop_ip6;
};
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 */
......@@ -151,6 +159,8 @@ struct babel_iface {
char *ifname;
sock *sk;
ip_addr addr;
ip_addr next_hop_ip4;
ip_addr next_hop_ip6;
int tx_length;
list neigh_list; /* List of neighbors seen on this iface (struct babel_neighbor) */
list msg_queue;
......
......@@ -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_ip4 = $4; if (!ipa_is_ip4($4)) cf_error("Must be an IPv4 address"); }
| NEXT HOP IPV6 ipa { BABEL_IFACE->next_hop_ip6 = $4; if (!ipa_is_ip6($4)) cf_error("Must be an IPv6 address"); }
;
babel_iface_opts:
......
......@@ -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_ip4;
ip_addr next_hop_ip6;
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;
ip_addr next_hop_ip4;
ip_addr next_hop_ip6;
};
......@@ -162,6 +164,21 @@ put_time16(void *p, u16 v)
put_u16(p, v * BABEL_TIME_UNITS);
}
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)
{
......@@ -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_ip4 = 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_ip6 = 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_ip6 = ipa_from_ip6(get_ip6_ll(&tlv->addr));
return PARSE_IGNORE;
default:
......@@ -456,6 +476,51 @@ 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_zero(addr))
{
/* Should not happen */
return 0;
}
else if (ipa_is_ip4(addr) && !ipa_equal(addr, state->next_hop_ip4))
{
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_ip4 = addr;
return len;
}
else if (ipa_is_ip6(addr) && !ipa_equal(addr, state->next_hop_ip6))
{
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_ip6 = addr;
return len;
}
return 0;
}
static int
babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
struct babel_parse_state *state)
......@@ -488,8 +553,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_ip4))
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_ip4;
break;
case BABEL_AE_IP6:
if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
......@@ -503,20 +593,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_ip6;
break;
case BABEL_AE_IP6_LL:
......@@ -535,7 +628,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;
......@@ -545,7 +637,6 @@ static uint
babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
struct babel_write_state *state, uint max_len)
{
struct babel_tlv_update *tlv = (void *) hdr;
struct babel_msg_update *msg = &m->update;
uint len0 = 0;
......@@ -554,15 +645,34 @@ 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)))
{
len0 = babel_write_router_id(hdr, msg->router_id, state, max_len);
tlv = (struct babel_tlv_update *) NEXT_TLV(tlv);
hdr = NEXT_TLV(hdr);
}
/*
* We also may add Next Hop TLV for regular updates. It may fail for not
* enough space or it may be unnecessary as the next hop is the same as the
* last one already announced. So we handle all three cases.
*/
if (msg->metric < BABEL_INFINITY)
{
int l = babel_write_next_hop(hdr, msg->next_hop, state, max_len - len0);
if (l < 0)
return 0;
if (l)
{
len0 += l;
hdr = NEXT_TLV(hdr);
}
}
struct babel_tlv_update *tlv = (void *) hdr;
uint len = sizeof(struct babel_tlv_update) + NET_SIZE(&msg->net);
if (len0 + len > max_len)
......@@ -576,6 +686,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 +726,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 +774,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 +812,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 +854,23 @@ 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;
}
......@@ -799,7 +944,7 @@ static uint
babel_write_queue(struct babel_iface *ifa, list *queue)
{
struct babel_proto *p = ifa->proto;
struct babel_write_state state = {};
struct babel_write_state state = { .next_hop_ip6 = ifa->addr };
if (EMPTY_LIST(*queue))
return 0;
......@@ -935,10 +1080,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_ip6 = saddr,
};
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment