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>
debugf(BABEL_DEBUG_ROUTE, "%s route (ipv4) to zebra",
add ? "adding" : "removing" );
- return zapi_route (add ? ZEBRA_IPV4_ROUTE_ADD :
- ZEBRA_IPV4_ROUTE_DELETE,
- zclient, &api);
+ return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
}
static int
debugf(BABEL_DEBUG_ROUTE, "%s route (ipv6) to zebra",
add ? "adding" : "removing" );
- return zapi_route (add ? ZEBRA_IPV6_ROUTE_ADD :
- ZEBRA_IPV6_ROUTE_DELETE,
- zclient, &api);
+ return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
}
int
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),
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);
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);
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) {
}
}
+ /* Attributes. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_DISTANCE))
stream_putc(s, api->distance);
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_METRIC))
/* 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;
}
/*
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,
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 */
}
}
+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
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;