lib/zserv: introduce address-family independent ZAPI message types

As noticed in 657cde1, the zapi_ipv[4|6]_route functions are broken in
many ways and that's the reason that many client daemons (e.g. ospfd,
isisd) need to send handcrafted messages to zebra.

The zapi_route() function introduced by Donald solves the problem
by providing a consistent way to send ipv4/ipv6 routes to zebra with
nexthops of any type, in all possible combinations including IPv4 routes
with IPv6 nexthops (for BGP unnumbered routes).

This patch goes a bit further and creates two new address-family
independent ZAPI message types that the client daemons can
use to advertise route information to zebra: ZEBRA_ROUTE_ADD and
ZEBRA_ROUTE_DELETE. The big advantage of having address-family independent
messages is that it allows us to remove a lot of duplicate code in zebra
and in the client daemons.

This patch also introduces the zapi_route_decode() function. It will be
used by zebra to decode route messages sent by the client daemons using
zclient_route_send(), which calls zapi_route_encode().

Later on we'll use this same pair of encode/decode functions to
send/receive redistributed routes from zebra to the client daemons,
taking the idea of removing code duplication to the next level.

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
Renato Westphal 2017-08-19 21:59:41 -03:00
parent bb1b9c47ca
commit 0e51b4a368
5 changed files with 262 additions and 17 deletions

View File

@ -190,8 +190,7 @@ kernel_route_v4(int add,
debugf(BABEL_DEBUG_ROUTE, "%s route (ipv4) to zebra",
add ? "adding" : "removing" );
return zapi_route (add ? ZEBRA_IPV4_ROUTE_ADD :
ZEBRA_IPV4_ROUTE_DELETE,
return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
zclient, &api);
}
@ -242,8 +241,7 @@ kernel_route_v6(int add, const unsigned char *pref, unsigned short plen,
debugf(BABEL_DEBUG_ROUTE, "%s route (ipv6) to zebra",
add ? "adding" : "removing" );
return zapi_route (add ? ZEBRA_IPV6_ROUTE_ADD :
ZEBRA_IPV6_ROUTE_DELETE,
return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
zclient, &api);
}

View File

@ -867,6 +867,8 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_INTERFACE_UP),
DESC_ENTRY(ZEBRA_INTERFACE_DOWN),
DESC_ENTRY(ZEBRA_INTERFACE_SET_MASTER),
DESC_ENTRY(ZEBRA_ROUTE_ADD),
DESC_ENTRY(ZEBRA_ROUTE_DELETE),
DESC_ENTRY(ZEBRA_IPV4_ROUTE_ADD),
DESC_ENTRY(ZEBRA_IPV4_ROUTE_DELETE),
DESC_ENTRY(ZEBRA_IPV6_ROUTE_ADD),

View File

@ -892,20 +892,23 @@ int zapi_ipv6_route(u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p,
return zclient_send_message(zclient);
}
int zapi_route(u_char cmd, struct zclient *zclient, struct zapi_route *api)
int zclient_route_send(u_char cmd, struct zclient *zclient,
struct zapi_route *api)
{
if (zapi_route_encode(cmd, zclient->obuf, api) < 0)
return -1;
return zclient_send_message(zclient);
}
int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api)
{
struct zapi_nexthop *api_nh;
int i;
int psize;
struct stream *s;
struct zapi_nexthop *api_nh;
/* Reset stream. */
s = zclient->obuf;
stream_reset(s);
zclient_create_header(s, cmd, api->vrf_id);
/* Put type and nexthop. */
stream_putc(s, api->type);
stream_putw(s, api->instance);
stream_putl(s, api->flags);
@ -913,6 +916,7 @@ int zapi_route(u_char cmd, struct zclient *zclient, struct zapi_route *api)
stream_putw(s, api->safi);
/* Put prefix information. */
stream_putc(s, api->prefix.family);
psize = PSIZE(api->prefix.prefixlen);
stream_putc(s, api->prefix.prefixlen);
stream_write(s, (u_char *)&api->prefix.u.prefix, psize);
@ -923,7 +927,7 @@ int zapi_route(u_char cmd, struct zclient *zclient, struct zapi_route *api)
stream_write(s, (u_char *)&api->src_prefix.prefix, psize);
}
/* Nexthop, ifindex, distance and metric information. */
/* Nexthops. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP)) {
/* limit the number of nexthops if necessary */
if (api->nexthop_num > MULTIPATH_NUM) {
@ -973,6 +977,7 @@ int zapi_route(u_char cmd, struct zclient *zclient, struct zapi_route *api)
}
}
/* Attributes. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_DISTANCE))
stream_putc(s, api->distance);
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_METRIC))
@ -985,7 +990,100 @@ int zapi_route(u_char cmd, struct zclient *zclient, struct zapi_route *api)
/* Put length at the first point of the stream. */
stream_putw_at(s, 0, stream_get_endp(s));
return zclient_send_message(zclient);
return 0;
}
int zapi_route_decode(struct stream *s, struct zapi_route *api)
{
struct zapi_nexthop *api_nh;
int i;
memset(api, 0, sizeof(*api));
/* Type, flags, message. */
api->type = stream_getc(s);
api->instance = stream_getw(s);
api->flags = stream_getl(s);
api->message = stream_getc(s);
api->safi = stream_getw(s);
/* Prefix. */
api->prefix.family = stream_getc(s);
switch (api->prefix.family) {
case AF_INET:
api->prefix.prefixlen = MIN(IPV4_MAX_PREFIXLEN, stream_getc(s));
break;
case AF_INET6:
api->prefix.prefixlen = MIN(IPV6_MAX_PREFIXLEN, stream_getc(s));
break;
}
stream_get(&api->prefix.u.prefix, s, PSIZE(api->prefix.prefixlen));
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_SRCPFX)) {
api->src_prefix.family = AF_INET6;
api->src_prefix.prefixlen = stream_getc(s);
stream_get(&api->src_prefix.prefix, s,
PSIZE(api->src_prefix.prefixlen));
if (api->prefix.family != AF_INET6
|| api->src_prefix.prefixlen == 0)
UNSET_FLAG(api->message, ZAPI_MESSAGE_SRCPFX);
}
/* Nexthops. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP)) {
api->nexthop_num = stream_getc(s);
if (api->nexthop_num > MULTIPATH_NUM) {
zlog_warn("%s: invalid number of nexthops (%u)",
__func__, api->nexthop_num);
return -1;
}
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
api_nh->type = stream_getc(s);
switch (api_nh->type) {
case NEXTHOP_TYPE_BLACKHOLE:
break;
case NEXTHOP_TYPE_IPV4:
api_nh->gate.ipv4.s_addr = stream_get_ipv4(s);
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
api_nh->gate.ipv4.s_addr = stream_get_ipv4(s);
api_nh->ifindex = stream_getl(s);
break;
case NEXTHOP_TYPE_IFINDEX:
api_nh->ifindex = stream_getl(s);
break;
case NEXTHOP_TYPE_IPV6:
stream_get(&api_nh->gate.ipv6, s, 16);
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
stream_get(&api_nh->gate.ipv6, s, 16);
api_nh->ifindex = stream_getl(s);
break;
}
/* For labeled-unicast, each nexthop is followed
* by label. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) {
stream_get(&api_nh->label, s,
sizeof(api_nh->label));
}
}
}
/* Attributes. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_DISTANCE))
api->distance = stream_getc(s);
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_METRIC))
api->metric = stream_getl(s);
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TAG))
api->tag = stream_getl(s);
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_MTU))
api->mtu = stream_getl(s);
return 0;
}
/*

View File

@ -59,6 +59,8 @@ typedef enum {
ZEBRA_INTERFACE_UP,
ZEBRA_INTERFACE_DOWN,
ZEBRA_INTERFACE_SET_MASTER,
ZEBRA_ROUTE_ADD,
ZEBRA_ROUTE_DELETE,
ZEBRA_IPV4_ROUTE_ADD,
ZEBRA_IPV4_ROUTE_DELETE,
ZEBRA_IPV6_ROUTE_ADD,
@ -426,7 +428,8 @@ extern int zapi_ipv6_route(u_char cmd, struct zclient *zclient,
extern int zapi_ipv4_route_ipv6_nexthop(u_char, struct zclient *,
struct prefix_ipv4 *,
struct zapi_ipv6 *);
extern int zapi_route(u_char cmd, struct zclient *zclient,
struct zapi_route *api);
extern int zclient_route_send(u_char, struct zclient *, struct zapi_route *);
extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *);
extern int zapi_route_decode(struct stream *, struct zapi_route *);
#endif /* _ZEBRA_ZCLIENT_H */

View File

@ -1157,6 +1157,144 @@ void zserv_nexthop_num_warn(const char *caller, const struct prefix *p,
}
}
static int zread_route_add(struct zserv *client, u_short length,
struct zebra_vrf *zvrf)
{
struct stream *s;
struct zapi_route api;
struct zapi_nexthop *api_nh;
afi_t afi;
struct prefix_ipv6 *src_p = NULL;
struct route_entry *re;
struct nexthop *nexthop;
int i, ret;
s = client->ibuf;
if (zapi_route_decode(s, &api) < 0)
return -1;
/* Allocate new route. */
re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
re->type = api.type;
re->instance = api.instance;
re->flags = api.flags;
re->uptime = time(NULL);
re->vrf_id = zvrf_id(zvrf);
re->table = zvrf->table_id;
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
for (i = 0; i < api.nexthop_num; i++) {
api_nh = &api.nexthops[i];
switch (api_nh->type) {
case NEXTHOP_TYPE_IFINDEX:
route_entry_nexthop_ifindex_add(
re, api_nh->ifindex);
break;
case NEXTHOP_TYPE_IPV4:
nexthop = route_entry_nexthop_ipv4_add(
re, &api_nh->gate.ipv4, NULL);
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
nexthop = route_entry_nexthop_ipv4_ifindex_add(
re, &api_nh->gate.ipv4, NULL,
api_nh->ifindex);
break;
case NEXTHOP_TYPE_IPV6:
nexthop = route_entry_nexthop_ipv6_add(
re, &api_nh->gate.ipv6);
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
nexthop = route_entry_nexthop_ipv6_ifindex_add(
re, &api_nh->gate.ipv6,
api_nh->ifindex);
break;
case NEXTHOP_TYPE_BLACKHOLE:
route_entry_nexthop_blackhole_add(re);
break;
}
/* MPLS labels for BGP-LU or Segment Routing */
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL)
&& api_nh->type != NEXTHOP_TYPE_IFINDEX
&& api_nh->type != NEXTHOP_TYPE_BLACKHOLE) {
enum lsp_types_t label_type;
label_type =
lsp_type_from_re_type(client->proto);
nexthop_add_labels(nexthop, label_type, 1,
&api_nh->label);
}
}
}
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE))
re->distance = api.distance;
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC))
re->metric = api.metric;
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_TAG))
re->tag = api.tag;
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_MTU))
re->mtu = api.mtu;
afi = family2afi(api.prefix.family);
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
src_p = &api.src_prefix;
ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re);
/* Stats */
switch (api.prefix.family) {
case AF_INET:
if (ret > 0)
client->v4_route_add_cnt++;
else if (ret < 0)
client->v4_route_upd8_cnt++;
break;
case AF_INET6:
if (ret > 0)
client->v6_route_add_cnt++;
else if (ret < 0)
client->v6_route_upd8_cnt++;
break;
}
return 0;
}
static int zread_route_del(struct zserv *client, u_short length,
struct zebra_vrf *zvrf)
{
struct stream *s;
struct zapi_route api;
afi_t afi;
struct prefix_ipv6 *src_p = NULL;
s = client->ibuf;
if (zapi_route_decode(s, &api) < 0)
return -1;
afi = family2afi(api.prefix.family);
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
src_p = &api.src_prefix;
rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance,
api.flags, &api.prefix, src_p, NULL, 0, zvrf->table_id,
api.metric);
/* Stats */
switch (api.prefix.family) {
case AF_INET:
client->v4_route_del_cnt++;
break;
case AF_INET6:
client->v6_route_del_cnt++;
break;
}
return 0;
}
/* This function support multiple nexthop. */
/*
* Parse the ZEBRA_IPV4_ROUTE_ADD sent from client. Update re and
@ -2334,6 +2472,12 @@ static int zebra_client_read(struct thread *thread)
case ZEBRA_INTERFACE_DELETE:
zread_interface_delete(client, length, zvrf);
break;
case ZEBRA_ROUTE_ADD:
zread_route_add(client, length, zvrf);
break;
case ZEBRA_ROUTE_DELETE:
zread_route_del(client, length, zvrf);
break;
case ZEBRA_IPV4_ROUTE_ADD:
zread_ipv4_add(client, length, zvrf);
break;