From 0e51b4a368257d54b1e07ff74492ea4705de97f7 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Sat, 19 Aug 2017 21:59:41 -0300 Subject: [PATCH] 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 --- babeld/kernel.c | 10 ++-- lib/log.c | 2 + lib/zclient.c | 116 +++++++++++++++++++++++++++++++++++--- lib/zclient.h | 7 ++- zebra/zserv.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 262 insertions(+), 17 deletions(-) diff --git a/babeld/kernel.c b/babeld/kernel.c index 105a7d051..6cdd66e48 100644 --- a/babeld/kernel.c +++ b/babeld/kernel.c @@ -190,9 +190,8 @@ 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, - zclient, &api); + return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, + zclient, &api); } static int @@ -242,9 +241,8 @@ 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, - zclient, &api); + return zclient_route_send (add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, + zclient, &api); } int diff --git a/lib/log.c b/lib/log.c index b9ce9e69b..92b2d3b66 100644 --- a/lib/log.c +++ b/lib/log.c @@ -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), diff --git a/lib/zclient.c b/lib/zclient.c index 53e8569b7..3f5c7a0f6 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -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; } /* diff --git a/lib/zclient.h b/lib/zclient.h index 92aeabb07..2e450ed39 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -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 */ diff --git a/zebra/zserv.c b/zebra/zserv.c index e00e1d7c2..94e34865b 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -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; -- 2.39.5