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.
This commit is contained in:
Ondrej Zajicek (work) 2018-02-13 16:39:36 +01:00
parent be17805c0b
commit 185a0a51f8
3 changed files with 224 additions and 28 deletions

View file

@ -1935,8 +1935,10 @@ babel_show_neighbors(struct proto *P, char *iff)
} }
static void 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) FIB_WALK(rtable, struct babel_entry, e)
{ {
struct babel_route *r = NULL; struct babel_route *r = NULL;
@ -1950,13 +1952,13 @@ babel_show_entries_(struct babel_proto *p UNUSED, struct fib *rtable)
srcs++; srcs++;
if (e->valid) 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); e->n.addr, e->router_id, e->metric, e->seqno, rts, srcs);
else if (r = e->selected) 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); e->n.addr, r->router_id, r->metric, r->seqno, rts, srcs);
else else
cli_msg(-1025, "%-24N %-23s %6s %5s %7u %7u", cli_msg(-1025, "%-*N %-23s %6s %5s %7u %7u", width,
e->n.addr, "<none>", "-", "-", rts, srcs); e->n.addr, "<none>", "-", "-", rts, srcs);
} }
FIB_WALK_END; FIB_WALK_END;
@ -1966,6 +1968,7 @@ void
babel_show_entries(struct proto *P) babel_show_entries(struct proto *P)
{ {
struct babel_proto *p = (void *) P; struct babel_proto *p = (void *) P;
int width = babel_sadr_enabled(p) ? -54 : -24;
if (p->p.proto_state != PS_UP) 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, "%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"); "Prefix", "Router ID", "Metric", "Seqno", "Routes", "Sources");
babel_show_entries_(p, &p->ip4_rtable); babel_show_entries_(p, &p->ip4_rtable);
@ -1985,8 +1988,10 @@ babel_show_entries(struct proto *P)
} }
static void 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) FIB_WALK(rtable, struct babel_entry, e)
{ {
struct babel_route *r; 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 ? '+' : ' '); char c = (r == e->selected) ? '*' : (r->feasible ? '+' : ' ');
btime time = r->expires ? r->expires - current_time() : 0; 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, e->n.addr, r->next_hop, r->neigh->ifa->ifname,
r->metric, c, r->seqno, MAX(time, 0)); r->metric, c, r->seqno, MAX(time, 0));
} }
@ -2006,6 +2011,7 @@ void
babel_show_routes(struct proto *P) babel_show_routes(struct proto *P)
{ {
struct babel_proto *p = (void *) P; struct babel_proto *p = (void *) P;
int width = babel_sadr_enabled(p) ? -54 : -24;
if (p->p.proto_state != PS_UP) 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, "%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"); "Prefix", "Nexthop", "Interface", "Metric", "Seqno", "Expires");
babel_show_routes_(p, &p->ip4_rtable); 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 * static struct proto *
babel_init(struct proto_config *CF) babel_init(struct proto_config *CF)
{ {
struct proto *P = proto_new(CF); struct proto *P = proto_new(CF);
struct babel_proto *p = (void *) P; 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->ip4_channel, cf->ip4_channel);
proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel);
P->if_notify = babel_if_notify; P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify; P->rt_notify = babel_rt_notify;
@ -2207,10 +2231,11 @@ babel_start(struct proto *P)
{ {
struct babel_proto *p = (void *) P; struct babel_proto *p = (void *) P;
struct babel_config *cf = (void *) P->cf; 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), fib_init(&p->ip4_rtable, P->pool, NET_IP4, sizeof(struct babel_entry),
OFFSETOF(struct babel_entry, n), 0, babel_init_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); OFFSETOF(struct babel_entry, n), 0, babel_init_entry);
init_list(&p->interfaces); 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_proto *p = (void *) P;
struct babel_config *new = (void *) CF; struct babel_config *new = (void *) CF;
u8 ip6_type = new->ip6_channel ? new->ip6_channel->net_type : NET_IP6;
TRACE(D_EVENTS, "Reconfiguring"); TRACE(D_EVENTS, "Reconfiguring");
if (!proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) || if (p->ip6_rtable.addr_type != ip6_type)
!proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6))) 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; return 0;
p->p.cf = CF; p->p.cf = CF;
@ -2280,9 +2309,10 @@ struct protocol proto_babel = {
.template = "babel%d", .template = "babel%d",
.attr_class = EAP_BABEL, .attr_class = EAP_BABEL,
.preference = DEF_PREF_BABEL, .preference = DEF_PREF_BABEL,
.channel_mask = NB_IP, .channel_mask = NB_IP | NB_IP6_SADR,
.proto_size = sizeof(struct babel_proto), .proto_size = sizeof(struct babel_proto),
.config_size = sizeof(struct babel_config), .config_size = sizeof(struct babel_config),
.postconfig = babel_postconfig,
.init = babel_init, .init = babel_init,
.dump = babel_dump, .dump = babel_dump,
.start = babel_start, .start = babel_start,

View file

@ -85,7 +85,10 @@ enum babel_tlv_type {
enum babel_subtlv_type { enum babel_subtlv_type {
BABEL_SUBTLV_PAD1 = 0, BABEL_SUBTLV_PAD1 = 0,
BABEL_SUBTLV_PADN = 1 BABEL_SUBTLV_PADN = 1,
/* Mandatory subtlvs */
BABEL_SUBTLV_SOURCE_PREFIX = 128,
}; };
enum babel_iface_type { enum babel_iface_type {
@ -109,6 +112,9 @@ struct babel_config {
struct proto_config c; struct proto_config c;
list iface_list; /* List of iface configs (struct babel_iface_config) */ list iface_list; /* List of iface configs (struct babel_iface_config) */
uint hold_time; /* Time to hold stale entries and unreachable routes */ 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 { struct babel_iface_config {
@ -303,7 +309,10 @@ struct babel_msg_update {
u16 seqno; u16 seqno;
u16 metric; u16 metric;
u64 router_id; u64 router_id;
union {
net_addr net; net_addr net;
net_addr_ip6_sadr net_sadr;
};
ip_addr next_hop; ip_addr next_hop;
ip_addr sender; ip_addr sender;
}; };
@ -311,7 +320,10 @@ struct babel_msg_update {
struct babel_msg_route_request { struct babel_msg_route_request {
u8 type; u8 type;
u8 full; u8 full;
union {
net_addr net; net_addr net;
net_addr_ip6_sadr net_sadr;
};
}; };
struct babel_msg_seqno_request { struct babel_msg_seqno_request {
@ -319,7 +331,10 @@ struct babel_msg_seqno_request {
u8 hop_count; u8 hop_count;
u16 seqno; u16 seqno;
u64 router_id; u64 router_id;
union {
net_addr net; net_addr net;
net_addr_ip6_sadr net_sadr;
};
ip_addr sender; ip_addr sender;
}; };
@ -339,6 +354,8 @@ struct babel_msg_node {
union babel_msg msg; 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 */ /* babel.c */
void babel_handle_ack_req(union babel_msg *msg, struct babel_iface *ifa); void babel_handle_ack_req(union babel_msg *msg, struct babel_iface *ifa);

View file

@ -105,6 +105,13 @@ struct babel_tlv_seqno_request {
u8 addr[0]; u8 addr[0];
} PACKED; } PACKED;
struct babel_subtlv_source_prefix {
u8 type;
u8 length;
u8 plen;
u8 addr[0];
} PACKED;
/* Hello flags */ /* Hello flags */
#define BABEL_HF_UNICAST 0x8000 #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_ip6_prefix_seen; /* def_ip6_prefix is valid */
u8 def_ip4_prefix_seen; /* def_ip4_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 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
u8 sadr_enabled;
}; };
enum parse_result { 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_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_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_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_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); 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_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_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 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 { struct babel_tlv_data {
u8 min_length; u8 min_length;
@ -640,6 +650,9 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
ip6_addr prefix6 = get_ip6(buf); ip6_addr prefix6 = get_ip6(buf);
net_fill_ip6(&msg->net, prefix6, tlv->plen); 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) if (tlv->flags & BABEL_UF_DEF_PREFIX)
{ {
put_ip6(state->def_ip6_prefix, prefix6); 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->seqno, msg->seqno);
put_u16(&tlv->metric, msg->metric); 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; return len0 + len;
} }
static int static int
babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, 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_tlv_route_request *tlv = (void *) hdr;
struct babel_msg_route_request *msg = &m->route_request; 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); read_ip6_px(&msg->net, tlv->addr, tlv->plen);
state->current_tlv_endpos += BYTES(tlv->plen); state->current_tlv_endpos += BYTES(tlv->plen);
if (state->sadr_enabled)
net_make_ip6_sadr(&msg->net);
return PARSE_SUCCESS; return PARSE_SUCCESS;
case BABEL_AE_IP6_LL: 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); 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; 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); read_ip6_px(&msg->net, tlv->addr, tlv->plen);
state->current_tlv_endpos += BYTES(tlv->plen); state->current_tlv_endpos += BYTES(tlv->plen);
if (state->sadr_enabled)
net_make_ip6_sadr(&msg->net);
return PARSE_SUCCESS; return PARSE_SUCCESS;
case BABEL_AE_IP6_LL: 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; tlv->hop_count = msg->hop_count;
put_u64(&tlv->router_id, msg->router_id); 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; 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 static inline int
babel_read_subtlvs(struct babel_tlv *hdr, babel_read_subtlvs(struct babel_tlv *hdr,
union babel_msg *msg UNUSED, union babel_msg *msg,
struct babel_parse_state *state) struct babel_parse_state *state)
{ {
struct babel_tlv *tlv; struct babel_tlv *tlv;
byte *pos, *end = (byte *) hdr + TLV_LENGTH(hdr);
int res;
for (tlv = (void *) hdr + state->current_tlv_endpos; for (tlv = (void *) hdr + state->current_tlv_endpos;
(void *) tlv < (void *) hdr + TLV_LENGTH(hdr); (byte *) tlv < end;
tlv = NEXT_TLV(tlv)) 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 * 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 * use a switch for dispatch instead of the mapping array we use for TLVs
*/ */
switch (tlv->type) switch (tlv->type)
{ {
case BABEL_SUBTLV_PAD1: case BABEL_SUBTLV_SOURCE_PREFIX:
case BABEL_SUBTLV_PADN: res = babel_read_source_prefix(tlv, msg, state);
/* FIXME: Framing errors in PADN are silently ignored, see babel_process_packet() */ if (res != PARSE_SUCCESS)
return res;
break; break;
case BABEL_SUBTLV_PADN:
default: default:
/* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */ /* 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; return PARSE_IGNORE;
}
break; break;
} }
} }
@ -1197,6 +1345,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
.ifa = ifa, .ifa = ifa,
.saddr = saddr, .saddr = saddr,
.next_hop_ip6 = saddr, .next_hop_ip6 = saddr,
.sadr_enabled = babel_sadr_enabled(p),
}; };
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))