frr/zebra/rt_socket.c
Donald Sharp 7ddbadd7f7 zebra: When installing a mroute, allow it to flow
Currently the mroute code was not allowing the mroute
to be sent to the dataplane.  This leaves us with a
situation where the routes being installed where never
being set as installed and additionally nht against
the mrib would not work if the route came into existence
after the nexthop tracking was asked for.

Turns out all the pieces where there to let this work.
Modify the code to pass it to the dplane and to send
it back up as having worked.

Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2024-10-30 07:47:32 -04:00

423 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Kernel routing table updates by routing socket.
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*/
#include <zebra.h>
#include <net/route.h>
#ifndef HAVE_NETLINK
#ifdef __OpenBSD__
#include <netmpls/mpls.h>
#endif
#include "if.h"
#include "prefix.h"
#include "sockunion.h"
#include "log.h"
#include "privs.h"
#include "vxlan.h"
#include "lib_errors.h"
#include "zebra/debug.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
#include "zebra/kernel_socket.h"
#include "zebra/zebra_mpls.h"
#include "zebra/zebra_errors.h"
extern struct zebra_privs_t zserv_privs;
#ifdef __OpenBSD__
static int kernel_rtm_add_labels(struct mpls_label_stack *nh_label,
struct sockaddr_mpls *smpls)
{
if (nh_label->num_labels > 1) {
flog_warn(EC_ZEBRA_MAX_LABELS_PUSH,
"%s: can't push %u labels at once (maximum is 1)",
__func__, nh_label->num_labels);
return -1;
}
memset(smpls, 0, sizeof(*smpls));
smpls->smpls_len = sizeof(*smpls);
smpls->smpls_family = AF_MPLS;
smpls->smpls_label = htonl(nh_label->label[0] << MPLS_LABEL_OFFSET);
return 0;
}
#endif
/* Interface between zebra message and rtm message. */
static int kernel_rtm(int cmd, const struct prefix *p,
const struct nexthop_group *ng, uint32_t metric)
{
union sockunion sin_dest, sin_mask, sin_gate;
#ifdef __OpenBSD__
struct sockaddr_mpls smpls;
#endif
union sockunion *smplsp = NULL;
struct nexthop *nexthop;
int nexthop_num = 0;
ifindex_t ifindex = 0;
bool gate = false;
int error;
char gate_buf[INET6_BUFSIZ];
enum blackhole_type bh_type = BLACKHOLE_UNSPEC;
/*
* We only have the ability to ADD or DELETE at this point
* in time.
*/
if (cmd != RTM_ADD && cmd != RTM_DELETE) {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("%s: %pFX odd command %s", __func__, p,
lookup_msg(rtm_type_str, cmd, NULL));
return 0;
}
memset(&sin_dest, 0, sizeof(sin_dest));
memset(&sin_gate, 0, sizeof(sin_gate));
memset(&sin_mask, 0, sizeof(sin_mask));
switch (p->family) {
case AF_INET:
sin_dest.sin.sin_family = AF_INET;
sin_dest.sin.sin_addr = p->u.prefix4;
sin_gate.sin.sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sin_dest.sin.sin_len = sizeof(struct sockaddr_in);
sin_gate.sin.sin_len = sizeof(struct sockaddr_in);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
break;
case AF_INET6:
sin_dest.sin6.sin6_family = AF_INET6;
sin_dest.sin6.sin6_addr = p->u.prefix6;
sin_gate.sin6.sin6_family = AF_INET6;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sin_dest.sin6.sin6_len = sizeof(struct sockaddr_in6);
sin_gate.sin6.sin6_len = sizeof(struct sockaddr_in6);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
break;
}
/* Make gateway. */
for (ALL_NEXTHOPS_PTR(ng, nexthop)) {
/*
* We only want to use the actual good nexthops
*/
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ||
!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
continue;
smplsp = NULL;
gate = false;
snprintf(gate_buf, sizeof(gate_buf), "NULL");
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
sin_gate.sin.sin_addr = nexthop->gate.ipv4;
sin_gate.sin.sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sin_gate.sin.sin_len = sizeof(struct sockaddr_in);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
ifindex = nexthop->ifindex;
gate = true;
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
sin_gate.sin6.sin6_addr = nexthop->gate.ipv6;
sin_gate.sin6.sin6_family = AF_INET6;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sin_gate.sin6.sin6_len = sizeof(struct sockaddr_in6);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
ifindex = nexthop->ifindex;
/* Under kame set interface index to link local address */
#ifdef KAME
#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
do { \
(a).s6_addr[2] = ((i) >> 8) & 0xff; \
(a).s6_addr[3] = (i)&0xff; \
} while (0)
if (IN6_IS_ADDR_LINKLOCAL(&sin_gate.sin6.sin6_addr))
SET_IN6_LINKLOCAL_IFINDEX(
sin_gate.sin6.sin6_addr,
ifindex);
#endif /* KAME */
gate = true;
break;
case NEXTHOP_TYPE_IFINDEX:
ifindex = nexthop->ifindex;
break;
case NEXTHOP_TYPE_BLACKHOLE:
bh_type = nexthop->bh_type;
switch (p->family) {
case AF_INET: {
struct in_addr loopback;
loopback.s_addr = htonl(INADDR_LOOPBACK);
sin_gate.sin.sin_addr = loopback;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sin_gate.sin.sin_len =
sizeof(struct sockaddr_in);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
gate = true;
} break;
case AF_INET6: {
struct in6_addr loopback;
inet_pton(AF_INET6, "::1", &loopback);
sin_gate.sin6.sin6_addr = loopback;
sin_gate.sin6.sin6_family = AF_INET6;
#ifdef HAVE_STRUCTSOCKADDR_SA_LEN
sin_gate.sin6.sin6_len =
sizeof(struct sockaddr_in6);
#endif /* HAVE_STRUCTSOCKADDR_SA_LEN */
gate = true;
} break;
}
}
switch (p->family) {
case AF_INET:
masklen2ip(p->prefixlen, &sin_mask.sin.sin_addr);
sin_mask.sin.sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sin_mask.sin.sin_len = sizeof(struct sockaddr_in);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
break;
case AF_INET6:
masklen2ip6(p->prefixlen, &sin_mask.sin6.sin6_addr);
sin_mask.sin6.sin6_family = AF_INET6;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
sin_mask.sin6.sin6_len = sizeof(struct sockaddr_in6);
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
break;
}
#ifdef __OpenBSD__
if (nexthop->nh_label) {
if (kernel_rtm_add_labels(nexthop->nh_label,
&smpls) != 0)
continue;
smplsp = (union sockunion *)&smpls;
}
#endif
error = rtm_write(cmd, &sin_dest, &sin_mask,
gate ? &sin_gate : NULL, smplsp,
ifindex, bh_type, metric);
if (IS_ZEBRA_DEBUG_KERNEL) {
if (!gate) {
zlog_debug(
"%s: %pFX: attention! gate not found for re",
__func__, p);
} else {
switch (p->family) {
case AF_INET:
inet_ntop(AF_INET,
&sin_gate.sin.sin_addr,
gate_buf, sizeof(gate_buf));
break;
case AF_INET6:
inet_ntop(AF_INET6,
&sin_gate.sin6.sin6_addr,
gate_buf, sizeof(gate_buf));
break;
default:
snprintf(gate_buf, sizeof(gate_buf),
"(invalid-af)");
break;
}
}
}
switch (error) {
/* We only flag nexthops as being in FIB if
* rtm_write() did its work. */
case ZEBRA_ERR_NOERROR:
nexthop_num++;
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("%s: %pFX: successfully did NH %s",
__func__, p, gate_buf);
if (cmd == RTM_ADD)
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
break;
/* The only valid case for this error is
* kernel's failure to install a multipath
* route, which is common for FreeBSD. This
* should be ignored silently, but logged as an error
* otherwise.
*/
case ZEBRA_ERR_RTEXIST:
if (cmd != RTM_ADD)
flog_err(EC_LIB_SYSTEM_CALL,
"%s: rtm_write() returned %d for command %d",
__func__, error, cmd);
continue;
/* Note any unexpected status returns */
case ZEBRA_ERR_RTNOEXIST:
if (cmd != RTM_DELETE)
flog_err(EC_LIB_SYSTEM_CALL,
"%s: rtm_write() returned %d for command %d",
__func__, error, cmd);
break;
default:
flog_err(
EC_LIB_SYSTEM_CALL,
"%s: %pFX: rtm_write() unexpectedly returned %d for command %s",
__func__, p, error,
lookup_msg(rtm_type_str, cmd, NULL));
break;
}
} /* for (ALL_NEXTHOPS(...))*/
/* If there was no useful nexthop, then complain. */
if (nexthop_num == 0) {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"%s: No useful nexthops were found in RIB prefix %pFX",
__func__, p);
return 1;
}
return 0; /*XXX*/
}
/*
* Update or delete a prefix from the kernel,
* using info from a dataplane context struct.
*/
enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
{
enum zebra_dplane_result res = ZEBRA_DPLANE_REQUEST_SUCCESS;
uint32_t type, old_type;
if (dplane_ctx_get_src(ctx) != NULL) {
zlog_err("route add: IPv6 sourcedest routes unsupported!");
return ZEBRA_DPLANE_REQUEST_FAILURE;
}
type = dplane_ctx_get_type(ctx);
old_type = dplane_ctx_get_old_type(ctx);
frr_with_privs(&zserv_privs) {
if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {
if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST)
kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx),
dplane_ctx_get_ng(ctx),
dplane_ctx_get_metric(ctx));
} else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) {
if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST)
kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx),
dplane_ctx_get_ng(ctx),
dplane_ctx_get_metric(ctx));
} else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) {
/* Must do delete and add separately -
* no update available
*/
if (!RSYSTEM_ROUTE(old_type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST)
kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx),
dplane_ctx_get_old_ng(ctx),
dplane_ctx_get_old_metric(ctx));
if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST)
kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx),
dplane_ctx_get_ng(ctx),
dplane_ctx_get_metric(ctx));
} else {
zlog_err("Invalid routing socket update op %s (%u)",
dplane_op2str(dplane_ctx_get_op(ctx)),
dplane_ctx_get_op(ctx));
res = ZEBRA_DPLANE_REQUEST_FAILURE;
}
} /* Elevated privs */
return res;
}
enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
{
return ZEBRA_DPLANE_REQUEST_SUCCESS;
}
int kernel_neigh_register(vrf_id_t vrf_id, struct zserv *client, bool reg)
{
/* TODO */
return 0;
}
int kernel_neigh_update(int add, int ifindex, void *addr, char *lla, int llalen,
ns_id_t ns_id, uint8_t family, bool permanent)
{
/* TODO */
return 0;
}
/* NYI on routing-socket platforms, but we've always returned 'success'... */
enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx)
{
return ZEBRA_DPLANE_REQUEST_SUCCESS;
}
extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute)
{
return 0;
}
/*
* Update MAC, using dataplane context object. No-op here for now.
*/
enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)
{
return ZEBRA_DPLANE_REQUEST_SUCCESS;
}
extern int kernel_interface_set_master(struct interface *master,
struct interface *slave)
{
return 0;
}
uint32_t kernel_get_speed(struct interface *ifp, int *error)
{
return ifp->speed;
}
int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip)
{
return 0;
}
int kernel_del_mac_nh(uint32_t nh_id)
{
return 0;
}
int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt,
struct nh_grp *nh_ids)
{
return 0;
}
int kernel_del_mac_nhg(uint32_t nhg_id)
{
return 0;
}
#endif /* !HAVE_NETLINK */