1147 lines
27 KiB
C
1147 lines
27 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Zebra connect code.
|
|
* Copyright (C) Cumulus Networks, Inc.
|
|
* Donald Sharp
|
|
*/
|
|
#include <zebra.h>
|
|
|
|
#include "frrevent.h"
|
|
#include "command.h"
|
|
#include "network.h"
|
|
#include "prefix.h"
|
|
#include "stream.h"
|
|
#include "memory.h"
|
|
#include "zclient.h"
|
|
#include "nexthop.h"
|
|
#include "nexthop_group.h"
|
|
#include "link_state.h"
|
|
#include "tc.h"
|
|
|
|
#include "sharp_globals.h"
|
|
#include "sharp_nht.h"
|
|
#include "sharp_zebra.h"
|
|
|
|
/* Zebra structure to hold current status. */
|
|
struct zclient *zclient = NULL;
|
|
|
|
/* For registering threads. */
|
|
extern struct event_loop *master;
|
|
|
|
/* Privs info */
|
|
extern struct zebra_privs_t sharp_privs;
|
|
|
|
DEFINE_MTYPE_STATIC(SHARPD, ZC, "Test zclients");
|
|
|
|
/* Struct to hold list of test zclients */
|
|
struct sharp_zclient {
|
|
struct sharp_zclient *prev;
|
|
struct sharp_zclient *next;
|
|
struct zclient *client;
|
|
};
|
|
|
|
/* Head of test zclient list */
|
|
static struct sharp_zclient *sharp_clients_head;
|
|
|
|
static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS);
|
|
|
|
/* Utility to add a test zclient struct to the list */
|
|
static void add_zclient(struct zclient *client)
|
|
{
|
|
struct sharp_zclient *node;
|
|
|
|
node = XCALLOC(MTYPE_ZC, sizeof(struct sharp_zclient));
|
|
|
|
node->client = client;
|
|
|
|
node->next = sharp_clients_head;
|
|
if (sharp_clients_head)
|
|
sharp_clients_head->prev = node;
|
|
sharp_clients_head = node;
|
|
}
|
|
|
|
/* Interface addition message from zebra. */
|
|
static int sharp_ifp_create(struct interface *ifp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sharp_ifp_destroy(struct interface *ifp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int interface_address_add(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int interface_address_delete(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct connected *c;
|
|
|
|
c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
|
|
|
|
if (!c)
|
|
return 0;
|
|
|
|
connected_free(&c);
|
|
return 0;
|
|
}
|
|
|
|
static int sharp_ifp_up(struct interface *ifp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sharp_ifp_down(struct interface *ifp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int sharp_install_lsps_helper(bool install_p, bool update_p,
|
|
const struct prefix *p, uint8_t type,
|
|
int instance, uint32_t in_label,
|
|
const struct nexthop_group *nhg,
|
|
const struct nexthop_group *backup_nhg)
|
|
{
|
|
struct zapi_labels zl = {};
|
|
struct zapi_nexthop *znh;
|
|
const struct nexthop *nh;
|
|
int i, cmd, ret;
|
|
|
|
zl.type = ZEBRA_LSP_SHARP;
|
|
zl.local_label = in_label;
|
|
|
|
if (p) {
|
|
SET_FLAG(zl.message, ZAPI_LABELS_FTN);
|
|
prefix_copy(&zl.route.prefix, p);
|
|
zl.route.type = type;
|
|
zl.route.instance = instance;
|
|
}
|
|
|
|
/* List of nexthops is optional for delete */
|
|
i = 0;
|
|
if (nhg) {
|
|
for (ALL_NEXTHOPS_PTR(nhg, nh)) {
|
|
znh = &zl.nexthops[i];
|
|
|
|
/* Must have labels to be useful */
|
|
if (nh->nh_label == NULL ||
|
|
nh->nh_label->num_labels == 0)
|
|
continue;
|
|
|
|
if (nh->type == NEXTHOP_TYPE_IFINDEX ||
|
|
nh->type == NEXTHOP_TYPE_BLACKHOLE)
|
|
/* Hmm - can't really deal with these types */
|
|
continue;
|
|
|
|
ret = zapi_nexthop_from_nexthop(znh, nh);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
i++;
|
|
if (i >= MULTIPATH_NUM)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Whoops - no nexthops isn't very useful for install */
|
|
if (i == 0 && install_p)
|
|
return -1;
|
|
|
|
zl.nexthop_num = i;
|
|
|
|
/* Add optional backup nexthop info. Since these are used by index,
|
|
* we can't just skip over an invalid backup nexthop: we will
|
|
* invalidate the entire operation.
|
|
*/
|
|
if (backup_nhg != NULL) {
|
|
i = 0;
|
|
for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
|
|
znh = &zl.backup_nexthops[i];
|
|
|
|
/* Must have labels to be useful */
|
|
if (nh->nh_label == NULL ||
|
|
nh->nh_label->num_labels == 0)
|
|
return -1;
|
|
|
|
if (nh->type == NEXTHOP_TYPE_IFINDEX ||
|
|
nh->type == NEXTHOP_TYPE_BLACKHOLE)
|
|
/* Hmm - can't really deal with these types */
|
|
return -1;
|
|
|
|
ret = zapi_nexthop_from_nexthop(znh, nh);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
i++;
|
|
if (i >= MULTIPATH_NUM)
|
|
break;
|
|
}
|
|
|
|
if (i > 0)
|
|
SET_FLAG(zl.message, ZAPI_LABELS_HAS_BACKUPS);
|
|
|
|
zl.backup_nexthop_num = i;
|
|
}
|
|
|
|
|
|
if (install_p) {
|
|
if (update_p)
|
|
cmd = ZEBRA_MPLS_LABELS_REPLACE;
|
|
else
|
|
cmd = ZEBRA_MPLS_LABELS_ADD;
|
|
} else {
|
|
cmd = ZEBRA_MPLS_LABELS_DELETE;
|
|
}
|
|
|
|
if (zebra_send_mpls_labels(zclient, cmd, &zl) == ZCLIENT_SEND_FAILURE)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum where_to_restart {
|
|
SHARP_INSTALL_ROUTES_RESTART,
|
|
SHARP_DELETE_ROUTES_RESTART,
|
|
};
|
|
|
|
struct buffer_delay {
|
|
struct prefix p;
|
|
uint32_t count;
|
|
uint32_t routes;
|
|
vrf_id_t vrf_id;
|
|
uint8_t instance;
|
|
uint32_t nhgid;
|
|
uint32_t flags;
|
|
const struct nexthop_group *nhg;
|
|
const struct nexthop_group *backup_nhg;
|
|
enum where_to_restart restart;
|
|
char *opaque;
|
|
} wb;
|
|
|
|
/*
|
|
* route_add - Encodes a route to zebra
|
|
*
|
|
* This function returns true when the route was buffered
|
|
* by the underlying stream system
|
|
*/
|
|
static bool route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance,
|
|
uint32_t nhgid, const struct nexthop_group *nhg,
|
|
const struct nexthop_group *backup_nhg, uint32_t flags,
|
|
char *opaque)
|
|
{
|
|
struct zapi_route api;
|
|
struct zapi_nexthop *api_nh;
|
|
struct nexthop *nh;
|
|
int i = 0;
|
|
|
|
memset(&api, 0, sizeof(api));
|
|
api.vrf_id = vrf_id;
|
|
api.type = ZEBRA_ROUTE_SHARP;
|
|
api.instance = instance;
|
|
api.safi = SAFI_UNICAST;
|
|
memcpy(&api.prefix, p, sizeof(*p));
|
|
|
|
api.flags = flags;
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
|
|
|
/* Only send via ID if nhgroup has been successfully installed */
|
|
if (nhgid && sharp_nhgroup_id_is_installed(nhgid)) {
|
|
zapi_route_set_nhg_id(&api, &nhgid);
|
|
} else {
|
|
for (ALL_NEXTHOPS_PTR(nhg, nh)) {
|
|
/* Check if we set a VNI label */
|
|
if (nh->nh_label &&
|
|
(nh->nh_label_type == ZEBRA_LSP_EVPN))
|
|
SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
|
|
|
|
api_nh = &api.nexthops[i];
|
|
|
|
zapi_nexthop_from_nexthop(api_nh, nh);
|
|
|
|
i++;
|
|
}
|
|
api.nexthop_num = i;
|
|
}
|
|
|
|
/* Include backup nexthops, if present */
|
|
if (backup_nhg && backup_nhg->nexthop) {
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS);
|
|
|
|
i = 0;
|
|
for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
|
|
api_nh = &api.backup_nexthops[i];
|
|
|
|
zapi_backup_nexthop_from_nexthop(api_nh, nh);
|
|
|
|
i++;
|
|
}
|
|
|
|
api.backup_nexthop_num = i;
|
|
}
|
|
|
|
if (strlen(opaque)) {
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_OPAQUE);
|
|
api.opaque.length = strlen(opaque) + 1;
|
|
assert(api.opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH);
|
|
memcpy(api.opaque.data, opaque, api.opaque.length);
|
|
}
|
|
|
|
if (zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api) ==
|
|
ZCLIENT_SEND_BUFFERED)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* route_delete - Encodes a route for deletion to zebra
|
|
*
|
|
* This function returns true when the route sent was
|
|
* buffered by the underlying stream system.
|
|
*/
|
|
static bool route_delete(struct prefix *p, vrf_id_t vrf_id, uint8_t instance)
|
|
{
|
|
struct zapi_route api;
|
|
|
|
memset(&api, 0, sizeof(api));
|
|
api.vrf_id = vrf_id;
|
|
api.type = ZEBRA_ROUTE_SHARP;
|
|
api.safi = SAFI_UNICAST;
|
|
api.instance = instance;
|
|
memcpy(&api.prefix, p, sizeof(*p));
|
|
|
|
if (zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api) ==
|
|
ZCLIENT_SEND_BUFFERED)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static void sharp_install_routes_restart(struct prefix *p, uint32_t count,
|
|
vrf_id_t vrf_id, uint8_t instance,
|
|
uint32_t nhgid,
|
|
const struct nexthop_group *nhg,
|
|
const struct nexthop_group *backup_nhg,
|
|
uint32_t routes, uint32_t flags,
|
|
char *opaque)
|
|
{
|
|
uint32_t temp, i;
|
|
bool v4 = false;
|
|
|
|
if (p->family == AF_INET) {
|
|
v4 = true;
|
|
temp = ntohl(p->u.prefix4.s_addr);
|
|
} else
|
|
temp = ntohl(p->u.val32[3]);
|
|
|
|
for (i = count; i < routes; i++) {
|
|
bool buffered = route_add(p, vrf_id, (uint8_t)instance, nhgid,
|
|
nhg, backup_nhg, flags, opaque);
|
|
if (v4)
|
|
p->u.prefix4.s_addr = htonl(++temp);
|
|
else
|
|
p->u.val32[3] = htonl(++temp);
|
|
|
|
if (buffered) {
|
|
wb.p = *p;
|
|
wb.count = i + 1;
|
|
wb.routes = routes;
|
|
wb.vrf_id = vrf_id;
|
|
wb.instance = instance;
|
|
wb.nhgid = nhgid;
|
|
wb.nhg = nhg;
|
|
wb.flags = flags;
|
|
wb.backup_nhg = backup_nhg;
|
|
wb.opaque = opaque;
|
|
wb.restart = SHARP_INSTALL_ROUTES_RESTART;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id,
|
|
uint8_t instance, uint32_t nhgid,
|
|
const struct nexthop_group *nhg,
|
|
const struct nexthop_group *backup_nhg,
|
|
uint32_t routes, uint32_t flags, char *opaque)
|
|
{
|
|
zlog_debug("Inserting %u routes", routes);
|
|
|
|
/* Only use backup route/nexthops if present */
|
|
if (backup_nhg && (backup_nhg->nexthop == NULL))
|
|
backup_nhg = NULL;
|
|
|
|
monotime(&sg.r.t_start);
|
|
sharp_install_routes_restart(p, 0, vrf_id, instance, nhgid, nhg,
|
|
backup_nhg, routes, flags, opaque);
|
|
}
|
|
|
|
static void sharp_remove_routes_restart(struct prefix *p, uint32_t count,
|
|
vrf_id_t vrf_id, uint8_t instance,
|
|
uint32_t routes)
|
|
{
|
|
uint32_t temp, i;
|
|
bool v4 = false;
|
|
|
|
if (p->family == AF_INET) {
|
|
v4 = true;
|
|
temp = ntohl(p->u.prefix4.s_addr);
|
|
} else
|
|
temp = ntohl(p->u.val32[3]);
|
|
|
|
for (i = count; i < routes; i++) {
|
|
bool buffered = route_delete(p, vrf_id, (uint8_t)instance);
|
|
|
|
if (v4)
|
|
p->u.prefix4.s_addr = htonl(++temp);
|
|
else
|
|
p->u.val32[3] = htonl(++temp);
|
|
|
|
if (buffered) {
|
|
wb.p = *p;
|
|
wb.count = i + 1;
|
|
wb.vrf_id = vrf_id;
|
|
wb.instance = instance;
|
|
wb.routes = routes;
|
|
wb.restart = SHARP_DELETE_ROUTES_RESTART;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id,
|
|
uint8_t instance, uint32_t routes)
|
|
{
|
|
zlog_debug("Removing %u routes", routes);
|
|
|
|
monotime(&sg.r.t_start);
|
|
|
|
sharp_remove_routes_restart(p, 0, vrf_id, instance, routes);
|
|
}
|
|
|
|
static void handle_repeated(bool installed)
|
|
{
|
|
struct prefix p = sg.r.orig_prefix;
|
|
sg.r.repeat--;
|
|
|
|
if (sg.r.repeat <= 0)
|
|
return;
|
|
|
|
if (installed) {
|
|
sg.r.removed_routes = 0;
|
|
sharp_remove_routes_helper(&p, sg.r.vrf_id, sg.r.inst,
|
|
sg.r.total_routes);
|
|
}
|
|
|
|
if (!installed) {
|
|
sg.r.installed_routes = 0;
|
|
sharp_install_routes_helper(
|
|
&p, sg.r.vrf_id, sg.r.inst, sg.r.nhgid,
|
|
&sg.r.nhop_group, &sg.r.backup_nhop_group,
|
|
sg.r.total_routes, sg.r.flags, sg.r.opaque);
|
|
}
|
|
}
|
|
|
|
static void sharp_zclient_buffer_ready(void)
|
|
{
|
|
switch (wb.restart) {
|
|
case SHARP_INSTALL_ROUTES_RESTART:
|
|
sharp_install_routes_restart(
|
|
&wb.p, wb.count, wb.vrf_id, wb.instance, wb.nhgid,
|
|
wb.nhg, wb.backup_nhg, wb.routes, wb.flags, wb.opaque);
|
|
return;
|
|
case SHARP_DELETE_ROUTES_RESTART:
|
|
sharp_remove_routes_restart(&wb.p, wb.count, wb.vrf_id,
|
|
wb.instance, wb.routes);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int route_notify_owner(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct timeval r;
|
|
struct prefix p;
|
|
enum zapi_route_notify_owner note;
|
|
uint32_t table;
|
|
|
|
if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e, NULL,
|
|
NULL))
|
|
return -1;
|
|
|
|
switch (note) {
|
|
case ZAPI_ROUTE_INSTALLED:
|
|
sg.r.installed_routes++;
|
|
if (sg.r.total_routes == sg.r.installed_routes) {
|
|
monotime(&sg.r.t_end);
|
|
timersub(&sg.r.t_end, &sg.r.t_start, &r);
|
|
zlog_debug("Installed All Items %jd.%ld",
|
|
(intmax_t)r.tv_sec, (long)r.tv_usec);
|
|
handle_repeated(true);
|
|
}
|
|
break;
|
|
case ZAPI_ROUTE_FAIL_INSTALL:
|
|
zlog_debug("Failed install of route");
|
|
break;
|
|
case ZAPI_ROUTE_BETTER_ADMIN_WON:
|
|
zlog_debug("Better Admin Distance won over us");
|
|
break;
|
|
case ZAPI_ROUTE_REMOVED:
|
|
sg.r.removed_routes++;
|
|
if (sg.r.total_routes == sg.r.removed_routes) {
|
|
monotime(&sg.r.t_end);
|
|
timersub(&sg.r.t_end, &sg.r.t_start, &r);
|
|
zlog_debug("Removed all Items %jd.%ld",
|
|
(intmax_t)r.tv_sec, (long)r.tv_usec);
|
|
handle_repeated(false);
|
|
}
|
|
break;
|
|
case ZAPI_ROUTE_REMOVE_FAIL:
|
|
zlog_debug("Route removal Failure");
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void zebra_connected(struct zclient *zclient)
|
|
{
|
|
zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, true);
|
|
zclient_send_reg_requests(zclient, VRF_DEFAULT);
|
|
|
|
/*
|
|
* Do not actually turn this on yet
|
|
* This is just the start of the infrastructure needed here
|
|
* This can be fixed at a later time.
|
|
*
|
|
* zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
|
|
* ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT);
|
|
*/
|
|
}
|
|
|
|
void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label)
|
|
{
|
|
zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP);
|
|
}
|
|
|
|
void nhg_add(uint32_t id, const struct nexthop_group *nhg,
|
|
const struct nexthop_group *backup_nhg)
|
|
{
|
|
struct zapi_nhg api_nhg = {};
|
|
struct zapi_nexthop *api_nh;
|
|
struct nexthop *nh;
|
|
bool is_valid = true;
|
|
|
|
api_nhg.id = id;
|
|
|
|
api_nhg.resilience = nhg->nhgr;
|
|
|
|
for (ALL_NEXTHOPS_PTR(nhg, nh)) {
|
|
if (api_nhg.nexthop_num >= MULTIPATH_NUM) {
|
|
zlog_warn(
|
|
"%s: number of nexthops greater than max multipath size, truncating",
|
|
__func__);
|
|
break;
|
|
}
|
|
|
|
/* Unresolved nexthops will lead to failure - only send
|
|
* nexthops that zebra will consider valid.
|
|
*/
|
|
if (nh->ifindex == 0)
|
|
continue;
|
|
|
|
api_nh = &api_nhg.nexthops[api_nhg.nexthop_num];
|
|
|
|
zapi_nexthop_from_nexthop(api_nh, nh);
|
|
api_nhg.nexthop_num++;
|
|
}
|
|
|
|
if (api_nhg.nexthop_num == 0) {
|
|
if (sharp_nhgroup_id_is_installed(id)) {
|
|
zlog_debug("%s: nhg %u: no nexthops, deleting nexthop group", __func__,
|
|
id);
|
|
zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg);
|
|
return;
|
|
}
|
|
zlog_debug("%s: nhg %u not sent: no valid nexthops", __func__,
|
|
id);
|
|
is_valid = false;
|
|
goto done;
|
|
}
|
|
|
|
if (backup_nhg) {
|
|
for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) {
|
|
if (api_nhg.backup_nexthop_num >= MULTIPATH_NUM) {
|
|
zlog_warn(
|
|
"%s: number of backup nexthops greater than max multipath size, truncating",
|
|
__func__);
|
|
break;
|
|
}
|
|
|
|
/* Unresolved nexthop: will be rejected by zebra.
|
|
* That causes a problem, since the primary nexthops
|
|
* rely on array indexing into the backup nexthops. If
|
|
* that array isn't valid, the backup indexes won't be
|
|
* valid.
|
|
*/
|
|
if (nh->ifindex == 0) {
|
|
zlog_debug("%s: nhg %u: invalid backup nexthop",
|
|
__func__, id);
|
|
is_valid = false;
|
|
break;
|
|
}
|
|
|
|
api_nh = &api_nhg.backup_nexthops
|
|
[api_nhg.backup_nexthop_num];
|
|
|
|
zapi_backup_nexthop_from_nexthop(api_nh, nh);
|
|
api_nhg.backup_nexthop_num++;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (is_valid)
|
|
zclient_nhg_send(zclient, ZEBRA_NHG_ADD, &api_nhg);
|
|
}
|
|
|
|
void nhg_del(uint32_t id)
|
|
{
|
|
struct zapi_nhg api_nhg = {};
|
|
|
|
api_nhg.id = id;
|
|
|
|
zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg);
|
|
}
|
|
|
|
void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import,
|
|
bool watch, bool connected)
|
|
{
|
|
int command;
|
|
|
|
command = ZEBRA_NEXTHOP_REGISTER;
|
|
|
|
if (!watch)
|
|
command = ZEBRA_NEXTHOP_UNREGISTER;
|
|
|
|
if (zclient_send_rnh(zclient, command, p, SAFI_UNICAST, connected,
|
|
false, vrf_id) == ZCLIENT_SEND_FAILURE)
|
|
zlog_warn("%s: Failure to send nexthop to zebra", __func__);
|
|
}
|
|
|
|
static int sharp_debug_nexthops(struct zapi_route *api)
|
|
{
|
|
int i;
|
|
|
|
if (api->nexthop_num == 0) {
|
|
zlog_debug(" Not installed");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < api->nexthop_num; i++) {
|
|
struct zapi_nexthop *znh = &api->nexthops[i];
|
|
|
|
switch (znh->type) {
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
case NEXTHOP_TYPE_IPV4:
|
|
zlog_debug(
|
|
" Nexthop %pI4, type: %d, ifindex: %d, vrf: %d, label_num: %d",
|
|
&znh->gate.ipv4.s_addr, znh->type, znh->ifindex,
|
|
znh->vrf_id, znh->label_num);
|
|
break;
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
|
case NEXTHOP_TYPE_IPV6:
|
|
zlog_debug(
|
|
" Nexthop %pI6, type: %d, ifindex: %d, vrf: %d, label_num: %d",
|
|
&znh->gate.ipv6, znh->type, znh->ifindex,
|
|
znh->vrf_id, znh->label_num);
|
|
break;
|
|
case NEXTHOP_TYPE_IFINDEX:
|
|
zlog_debug(" Nexthop IFINDEX: %d, ifindex: %d",
|
|
znh->type, znh->ifindex);
|
|
break;
|
|
case NEXTHOP_TYPE_BLACKHOLE:
|
|
zlog_debug(" Nexthop blackhole");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static void sharp_nexthop_update(struct vrf *vrf, struct prefix *matched,
|
|
struct zapi_route *nhr)
|
|
{
|
|
struct sharp_nh_tracker *nht;
|
|
|
|
zlog_debug("Received update for %pFX actual match: %pFX metric: %u",
|
|
matched, &nhr->prefix, nhr->metric);
|
|
|
|
nht = sharp_nh_tracker_get(matched);
|
|
nht->nhop_num = nhr->nexthop_num;
|
|
nht->updates++;
|
|
|
|
sharp_debug_nexthops(nhr);
|
|
}
|
|
|
|
static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct zapi_route api;
|
|
|
|
if (zapi_route_decode(zclient->ibuf, &api) < 0)
|
|
zlog_warn("%s: Decode of redistribute failed: %d", __func__,
|
|
ZEBRA_REDISTRIBUTE_ROUTE_ADD);
|
|
|
|
zlog_debug("%s: %pFX (%s)", zserv_command_string(cmd), &api.prefix,
|
|
zebra_route_string(api.type));
|
|
|
|
sharp_debug_nexthops(&api);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sharp_redistribute_vrf(struct vrf *vrf, int type, bool turn_on)
|
|
{
|
|
zebra_redistribute_send(turn_on ? ZEBRA_REDISTRIBUTE_ADD
|
|
: ZEBRA_REDISTRIBUTE_DELETE,
|
|
zclient, AFI_IP, type, 0, vrf->vrf_id);
|
|
}
|
|
|
|
static zclient_handler *const sharp_opaque_handlers[] = {
|
|
[ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler,
|
|
};
|
|
|
|
/* Add a zclient with a specified session id, for testing. */
|
|
int sharp_zclient_create(uint32_t session_id)
|
|
{
|
|
struct zclient *client;
|
|
struct sharp_zclient *node;
|
|
|
|
/* Check for duplicates */
|
|
for (node = sharp_clients_head; node != NULL; node = node->next) {
|
|
if (node->client->session_id == session_id)
|
|
return -1;
|
|
}
|
|
|
|
client = zclient_new(master, &zclient_options_default,
|
|
sharp_opaque_handlers,
|
|
array_size(sharp_opaque_handlers));
|
|
client->sock = -1;
|
|
client->session_id = session_id;
|
|
|
|
zclient_init(client, ZEBRA_ROUTE_SHARP, 0, &sharp_privs);
|
|
|
|
/* Enqueue on the list of test clients */
|
|
add_zclient(client);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Delete one of the extra test zclients */
|
|
int sharp_zclient_delete(uint32_t session_id)
|
|
{
|
|
struct sharp_zclient *node;
|
|
|
|
/* Search for session */
|
|
for (node = sharp_clients_head; node != NULL; node = node->next) {
|
|
if (node->client->session_id == session_id) {
|
|
/* Dequeue from list */
|
|
if (node->next)
|
|
node->next->prev = node->prev;
|
|
if (node->prev)
|
|
node->prev->next = node->next;
|
|
if (node == sharp_clients_head)
|
|
sharp_clients_head = node->next;
|
|
|
|
/* Clean up zclient */
|
|
zclient_stop(node->client);
|
|
zclient_free(node->client);
|
|
|
|
/* Free memory */
|
|
XFREE(MTYPE_ZC, node);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *const type2txt[] = {"Generic", "Vertex", "Edge", "Subnet"};
|
|
static const char *const status2txt[] = {"Unknown", "New", "Update",
|
|
"Delete", "Sync", "Orphan"};
|
|
/* Handler for opaque messages */
|
|
static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct stream *s;
|
|
struct zapi_opaque_msg info;
|
|
struct ls_element *lse;
|
|
|
|
s = zclient->ibuf;
|
|
|
|
if (zclient_opaque_decode(s, &info) != 0)
|
|
return -1;
|
|
|
|
zlog_debug("%s: [%u] received opaque type %u", __func__,
|
|
zclient->session_id, info.type);
|
|
|
|
if (info.type == LINK_STATE_UPDATE) {
|
|
lse = ls_stream2ted(sg.ted, s, true);
|
|
if (lse) {
|
|
zlog_debug(" |- Got %s %s from Link State Database",
|
|
status2txt[lse->status],
|
|
type2txt[lse->type]);
|
|
lse->status = SYNC;
|
|
} else
|
|
zlog_debug(
|
|
"%s: Error to convert Stream into Link State",
|
|
__func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler for opaque notification messages */
|
|
static int sharp_opq_notify_handler(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct stream *s;
|
|
struct zapi_opaque_notif_info info;
|
|
|
|
s = zclient->ibuf;
|
|
|
|
if (zclient_opaque_notif_decode(s, &info) != 0)
|
|
return -1;
|
|
|
|
if (info.reg)
|
|
zlog_debug("%s: received opaque notification REG, type %u => %d/%d/%d",
|
|
__func__, info.msg_type, info.proto, info.instance,
|
|
info.session_id);
|
|
else
|
|
zlog_debug("%s: received opaque notification UNREG, type %u",
|
|
__func__, info.msg_type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Send OPAQUE messages, using subtype 'type'.
|
|
*/
|
|
void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
|
|
uint32_t session_id, uint32_t count)
|
|
{
|
|
uint8_t buf[32];
|
|
int ret;
|
|
uint32_t i;
|
|
|
|
/* Prepare a small payload */
|
|
for (i = 0; i < sizeof(buf); i++) {
|
|
if (type < 255)
|
|
buf[i] = type;
|
|
else
|
|
buf[i] = 255;
|
|
}
|
|
|
|
/* Send some messages - broadcast and unicast are supported */
|
|
for (i = 0; i < count; i++) {
|
|
if (proto == 0)
|
|
ret = zclient_send_opaque(zclient, type, buf,
|
|
sizeof(buf));
|
|
else
|
|
ret = zclient_send_opaque_unicast(zclient, type, proto,
|
|
instance, session_id,
|
|
buf, sizeof(buf));
|
|
if (ret == ZCLIENT_SEND_FAILURE) {
|
|
zlog_debug("%s: send_opaque() failed => %d", __func__,
|
|
ret);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Register/unregister for opaque notifications from zebra about 'type'.
|
|
*/
|
|
void sharp_zebra_opaque_notif_reg(bool is_reg, uint32_t type)
|
|
{
|
|
if (is_reg)
|
|
zclient_opaque_request_notify(zclient, type);
|
|
else
|
|
zclient_opaque_drop_notify(zclient, type);
|
|
}
|
|
|
|
/*
|
|
* Send OPAQUE registration messages, using subtype 'type'.
|
|
*/
|
|
void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
|
|
uint32_t session_id, uint32_t type)
|
|
{
|
|
struct stream *s;
|
|
|
|
s = zclient->obuf;
|
|
stream_reset(s);
|
|
|
|
if (is_reg)
|
|
zclient_create_header(s, ZEBRA_OPAQUE_REGISTER, VRF_DEFAULT);
|
|
else
|
|
zclient_create_header(s, ZEBRA_OPAQUE_UNREGISTER, VRF_DEFAULT);
|
|
|
|
/* Send sub-type */
|
|
stream_putl(s, type);
|
|
|
|
/* Add zclient info */
|
|
stream_putc(s, proto);
|
|
stream_putw(s, instance);
|
|
stream_putl(s, session_id);
|
|
|
|
/* Put length at the first point of the stream. */
|
|
stream_putw_at(s, 0, stream_get_endp(s));
|
|
|
|
(void)zclient_send_message(zclient);
|
|
}
|
|
|
|
/* Link State registration */
|
|
void sharp_zebra_register_te(void)
|
|
{
|
|
/* First register to received Link State Update messages */
|
|
zclient_register_opaque(zclient, LINK_STATE_UPDATE);
|
|
|
|
/* Then, request initial TED with SYNC message */
|
|
ls_request_sync(zclient);
|
|
}
|
|
|
|
void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p)
|
|
{
|
|
zclient_send_neigh_discovery_req(zclient, ifp, p);
|
|
}
|
|
|
|
static int nhg_notify_owner(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
enum zapi_nhg_notify_owner note;
|
|
uint32_t id;
|
|
|
|
if (!zapi_nhg_notify_decode(zclient->ibuf, &id, ¬e))
|
|
return -1;
|
|
|
|
switch (note) {
|
|
case ZAPI_NHG_INSTALLED:
|
|
sharp_nhgroup_id_set_installed(id, true);
|
|
zlog_debug("Installed nhg %u", id);
|
|
break;
|
|
case ZAPI_NHG_FAIL_INSTALL:
|
|
zlog_debug("Failed install of nhg %u", id);
|
|
break;
|
|
case ZAPI_NHG_REMOVED:
|
|
sharp_nhgroup_id_set_installed(id, false);
|
|
zlog_debug("Removed nhg %u", id);
|
|
break;
|
|
case ZAPI_NHG_REMOVE_FAIL:
|
|
zlog_debug("Failed removal of nhg %u", id);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sharp_zebra_srv6_manager_get_locator_chunk(const char *locator_name)
|
|
{
|
|
return srv6_manager_get_locator_chunk(zclient, locator_name);
|
|
}
|
|
|
|
int sharp_zebra_srv6_manager_release_locator_chunk(const char *locator_name)
|
|
{
|
|
return srv6_manager_release_locator_chunk(zclient, locator_name);
|
|
}
|
|
|
|
static int sharp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct stream *s = NULL;
|
|
struct srv6_locator_chunk s6c = {};
|
|
struct listnode *node, *nnode;
|
|
struct sharp_srv6_locator *loc;
|
|
|
|
s = zclient->ibuf;
|
|
zapi_srv6_locator_chunk_decode(s, &s6c);
|
|
|
|
for (ALL_LIST_ELEMENTS(sg.srv6_locators, node, nnode, loc)) {
|
|
struct prefix_ipv6 *chunk = NULL;
|
|
struct listnode *chunk_node;
|
|
struct prefix_ipv6 *c;
|
|
|
|
if (strcmp(loc->name, s6c.locator_name) != 0) {
|
|
zlog_err("%s: Locator name unmatch %s:%s", __func__,
|
|
loc->name, s6c.locator_name);
|
|
continue;
|
|
}
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, c))
|
|
if (!prefix_cmp(c, &s6c.prefix))
|
|
return 0;
|
|
|
|
chunk = prefix_ipv6_new();
|
|
*chunk = s6c.prefix;
|
|
listnode_add(loc->chunks, chunk);
|
|
return 0;
|
|
}
|
|
|
|
zlog_err("%s: can't get locator_chunk!!", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int sharp_zebra_process_neigh(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
union sockunion addr = {}, lladdr = {};
|
|
struct zapi_neigh_ip api = {};
|
|
struct interface *ifp;
|
|
|
|
zlog_debug("Received a neighbor event");
|
|
zclient_neigh_ip_decode(zclient->ibuf, &api);
|
|
|
|
if (api.ip_in.ipa_type == AF_UNSPEC)
|
|
return 0;
|
|
|
|
sockunion_family(&addr) = api.ip_in.ipa_type;
|
|
memcpy((uint8_t *)sockunion_get_addr(&addr), &api.ip_in.ip.addr,
|
|
family2addrsize(api.ip_in.ipa_type));
|
|
|
|
sockunion_family(&lladdr) = api.ip_out.ipa_type;
|
|
if (api.ip_out.ipa_type != AF_UNSPEC)
|
|
memcpy((uint8_t *)sockunion_get_addr(&lladdr),
|
|
&api.ip_out.ip.addr,
|
|
family2addrsize(api.ip_out.ipa_type));
|
|
ifp = if_lookup_by_index(api.index, vrf_id);
|
|
if (!ifp) {
|
|
zlog_debug("Failed to lookup interface for neighbor entry: %u for %u",
|
|
api.index, vrf_id);
|
|
return 0;
|
|
}
|
|
|
|
zlog_debug("Received: %s %pSU dev %s lladr %pSU",
|
|
(cmd == ZEBRA_NEIGH_ADDED) ? "NEW" : "DEL", &addr, ifp->name,
|
|
&lladdr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down)
|
|
{
|
|
zlog_debug("Sending zebra to set %s protodown %s", ifp->name,
|
|
down ? "on" : "off");
|
|
|
|
if (zclient_send_interface_protodown(zclient, ifp->vrf->vrf_id, ifp,
|
|
down) == ZCLIENT_SEND_FAILURE)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sharp_zebra_send_tc_filter_rate(struct interface *ifp,
|
|
const struct prefix *source,
|
|
const struct prefix *destination,
|
|
uint8_t ip_proto, uint16_t src_port,
|
|
uint16_t dst_port, uint64_t rate)
|
|
{
|
|
#define SHARPD_TC_HANDLE 0x0001
|
|
struct stream *s;
|
|
|
|
s = zclient->obuf;
|
|
|
|
struct tc_qdisc q = {.ifindex = ifp->ifindex, .kind = TC_QDISC_HTB};
|
|
|
|
zapi_tc_qdisc_encode(ZEBRA_TC_QDISC_INSTALL, s, &q);
|
|
if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
|
|
return -1;
|
|
|
|
struct tc_class c = {.ifindex = ifp->ifindex,
|
|
.handle = SHARPD_TC_HANDLE & 0xffff,
|
|
.kind = TC_QDISC_HTB,
|
|
.u.htb.ceil = rate,
|
|
.u.htb.rate = rate};
|
|
|
|
zapi_tc_class_encode(ZEBRA_TC_CLASS_ADD, s, &c);
|
|
if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
|
|
return -1;
|
|
|
|
struct tc_filter f = {.ifindex = ifp->ifindex,
|
|
.handle = SHARPD_TC_HANDLE,
|
|
.priority = 0x1,
|
|
.kind = TC_FILTER_FLOWER,
|
|
.u.flower.filter_bm = 0};
|
|
|
|
#ifdef ETH_P_IP
|
|
f.protocol = ETH_P_IP;
|
|
#else
|
|
f.protocol = 0x0800;
|
|
#endif
|
|
|
|
f.u.flower.filter_bm |= TC_FLOWER_IP_PROTOCOL;
|
|
f.u.flower.ip_proto = ip_proto;
|
|
f.u.flower.filter_bm |= TC_FLOWER_SRC_IP;
|
|
prefix_copy(&f.u.flower.src_ip, source);
|
|
f.u.flower.filter_bm |= TC_FLOWER_DST_IP;
|
|
prefix_copy(&f.u.flower.dst_ip, destination);
|
|
f.u.flower.filter_bm |= TC_FLOWER_SRC_PORT;
|
|
f.u.flower.src_port_min = f.u.flower.src_port_max = src_port;
|
|
f.u.flower.filter_bm |= TC_FLOWER_DST_PORT;
|
|
f.u.flower.dst_port_min = f.u.flower.dst_port_max = dst_port;
|
|
f.u.flower.classid = SHARPD_TC_HANDLE & 0xffff;
|
|
|
|
zapi_tc_filter_encode(ZEBRA_TC_FILTER_ADD, s, &f);
|
|
if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sharp_zebra_register_neigh(vrf_id_t vrf_id, afi_t afi, bool reg)
|
|
{
|
|
zclient_register_neigh(zclient, vrf_id, afi, reg);
|
|
}
|
|
|
|
|
|
static zclient_handler *const sharp_handlers[] = {
|
|
[ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
|
|
[ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
|
|
[ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner,
|
|
[ZEBRA_NHG_NOTIFY_OWNER] = nhg_notify_owner,
|
|
[ZEBRA_REDISTRIBUTE_ROUTE_ADD] = sharp_redistribute_route,
|
|
[ZEBRA_REDISTRIBUTE_ROUTE_DEL] = sharp_redistribute_route,
|
|
[ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler,
|
|
[ZEBRA_OPAQUE_NOTIFY] = sharp_opq_notify_handler,
|
|
[ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] =
|
|
sharp_zebra_process_srv6_locator_chunk,
|
|
[ZEBRA_NEIGH_ADDED] = sharp_zebra_process_neigh,
|
|
[ZEBRA_NEIGH_REMOVED] = sharp_zebra_process_neigh,
|
|
};
|
|
|
|
void sharp_zebra_init(void)
|
|
{
|
|
hook_register_prio(if_real, 0, sharp_ifp_create);
|
|
hook_register_prio(if_up, 0, sharp_ifp_up);
|
|
hook_register_prio(if_down, 0, sharp_ifp_down);
|
|
hook_register_prio(if_unreal, 0, sharp_ifp_destroy);
|
|
|
|
zclient = zclient_new(master, &zclient_options_default, sharp_handlers,
|
|
array_size(sharp_handlers));
|
|
|
|
zclient_init(zclient, ZEBRA_ROUTE_SHARP, 0, &sharp_privs);
|
|
zclient->zebra_connected = zebra_connected;
|
|
zclient->zebra_buffer_write_ready = sharp_zclient_buffer_ready;
|
|
zclient->nexthop_update = sharp_nexthop_update;
|
|
}
|
|
|
|
void sharp_zebra_terminate(void)
|
|
{
|
|
struct sharp_zclient *node = sharp_clients_head;
|
|
|
|
while (node) {
|
|
sharp_zclient_delete(node->client->session_id);
|
|
|
|
node = sharp_clients_head;
|
|
}
|
|
|
|
zclient_stop(zclient);
|
|
zclient_free(zclient);
|
|
}
|