diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 6034576935..fde72da4c8 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1079,10 +1079,8 @@ static void peer_free(struct peer *peer) XFREE(MTYPE_TMP, peer->notify.data); memset(&peer->notify, 0, sizeof(struct bgp_notify)); - if (peer->clear_node_queue) { - work_queue_free(peer->clear_node_queue); - peer->clear_node_queue = NULL; - } + if (peer->clear_node_queue) + work_queue_free_and_null(&peer->clear_node_queue); bgp_sync_delete(peer); @@ -7639,10 +7637,8 @@ void bgp_terminate(void) bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_PEER_UNCONFIG); - if (bm->process_main_queue) { - work_queue_free(bm->process_main_queue); - bm->process_main_queue = NULL; - } + if (bm->process_main_queue) + work_queue_free_and_null(&bm->process_main_queue); if (bm->t_rmap_update) BGP_TIMER_OFF(bm->t_rmap_update); diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index c7d64bf1ed..348f1557e2 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -4336,7 +4336,7 @@ void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h) h->import_mac = NULL; } - work_queue_free(h->deferred_close_q); + work_queue_free_and_null(&h->deferred_close_q); if (h->rfp != NULL) rfp_stop(h->rfp); diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 2a8a465b70..c71f59563f 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -571,10 +571,8 @@ void rfapiRibClear(struct rfapi_descriptor *rfd) } } } - if (rfd->updated_responses_queue) { - work_queue_free(rfd->updated_responses_queue); - rfd->updated_responses_queue = NULL; - } + if (rfd->updated_responses_queue) + work_queue_free_and_null(&rfd->updated_responses_queue); } /* diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h new file mode 100644 index 0000000000..bc6688012c --- /dev/null +++ b/include/linux/fib_rules.h @@ -0,0 +1,74 @@ +#ifndef __LINUX_FIB_RULES_H +#define __LINUX_FIB_RULES_H + +#include +#include + +/* rule is permanent, and cannot be deleted */ +#define FIB_RULE_PERMANENT 0x00000001 +#define FIB_RULE_INVERT 0x00000002 +#define FIB_RULE_UNRESOLVED 0x00000004 +#define FIB_RULE_IIF_DETACHED 0x00000008 +#define FIB_RULE_DEV_DETACHED FIB_RULE_IIF_DETACHED +#define FIB_RULE_OIF_DETACHED 0x00000010 + +/* try to find source address in routing lookups */ +#define FIB_RULE_FIND_SADDR 0x00010000 + +struct fib_rule_hdr { + __u8 family; + __u8 dst_len; + __u8 src_len; + __u8 tos; + + __u8 table; + __u8 res1; /* reserved */ + __u8 res2; /* reserved */ + __u8 action; + + __u32 flags; +}; + +enum { + FRA_UNSPEC, + FRA_DST, /* destination address */ + FRA_SRC, /* source address */ + FRA_IIFNAME, /* interface name */ +#define FRA_IFNAME FRA_IIFNAME + FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */ + FRA_UNUSED2, + FRA_PRIORITY, /* priority/preference */ + FRA_UNUSED3, + FRA_UNUSED4, + FRA_UNUSED5, + FRA_FWMARK, /* mark */ + FRA_FLOW, /* flow/class id */ + FRA_UNUSED6, + FRA_SUPPRESS_IFGROUP, + FRA_SUPPRESS_PREFIXLEN, + FRA_TABLE, /* Extended table id */ + FRA_FWMASK, /* mask for netfilter mark */ + FRA_OIFNAME, + FRA_PAD, + FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + __FRA_MAX +}; + +#define FRA_MAX (__FRA_MAX - 1) + +enum { + FR_ACT_UNSPEC, + FR_ACT_TO_TBL, /* Pass to fixed table */ + FR_ACT_GOTO, /* Jump to another rule */ + FR_ACT_NOP, /* No operation */ + FR_ACT_RES3, + FR_ACT_RES4, + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ + __FR_ACT_MAX, +}; + +#define FR_ACT_MAX (__FR_ACT_MAX - 1) + +#endif diff --git a/include/subdir.am b/include/subdir.am index 7a12b2ffae..db5ed06c61 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -7,4 +7,5 @@ noinst_HEADERS += \ include/linux/rtnetlink.h \ include/linux/socket.h \ include/linux/net_namespace.h \ + include/linux/fib_rules.h \ # end diff --git a/lib/command.c b/lib/command.c index 6d04ad83c0..5697c1d812 100644 --- a/lib/command.c +++ b/lib/command.c @@ -65,6 +65,7 @@ const char *node_names[] = { "logical-router", // LOGICALROUTER_NODE, "vrf", // VRF_NODE, "interface", // INTERFACE_NODE, + "nexthop-group", // NH_GROUP_NODE, "zebra", // ZEBRA_NODE, "table", // TABLE_NODE, "rip", // RIP_NODE, @@ -1294,6 +1295,7 @@ void cmd_exit(struct vty *vty) case PW_NODE: case LOGICALROUTER_NODE: case VRF_NODE: + case NH_GROUP_NODE: case ZEBRA_NODE: case BGP_NODE: case RIP_NODE: @@ -1379,6 +1381,7 @@ DEFUN (config_end, case PW_NODE: case LOGICALROUTER_NODE: case VRF_NODE: + case NH_GROUP_NODE: case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: diff --git a/lib/command.h b/lib/command.h index 56e70abf6f..0febf903a3 100644 --- a/lib/command.h +++ b/lib/command.h @@ -88,6 +88,7 @@ enum node_type { LOGICALROUTER_NODE, /* Logical-Router node. */ VRF_NODE, /* VRF mode node. */ INTERFACE_NODE, /* Interface mode node. */ + NH_GROUP_NODE, /* Nexthop-Group mode node. */ ZEBRA_NODE, /* zebra connection node. */ TABLE_NODE, /* rtm_table selection node. */ RIP_NODE, /* RIP protocol mode node. */ diff --git a/lib/log.c b/lib/log.c index 6330c53702..9e33ef9102 100644 --- a/lib/log.c +++ b/lib/log.c @@ -961,6 +961,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_PW_SET), DESC_ENTRY(ZEBRA_PW_UNSET), DESC_ENTRY(ZEBRA_PW_STATUS_UPDATE), + DESC_ENTRY(ZEBRA_RULE_ADD), + DESC_ENTRY(ZEBRA_RULE_DELETE), + DESC_ENTRY(ZEBRA_RULE_NOTIFY_OWNER), }; #undef DESC_ENTRY diff --git a/lib/nexthop.c b/lib/nexthop.c index 6809a01469..cee34e85c7 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -143,49 +143,6 @@ struct nexthop *nexthop_new(void) return XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop)); } -/* Add nexthop to the end of a nexthop list. */ -void nexthop_add(struct nexthop **target, struct nexthop *nexthop) -{ - struct nexthop *last; - - for (last = *target; last && last->next; last = last->next) - ; - if (last) - last->next = nexthop; - else - *target = nexthop; - nexthop->prev = last; -} - -void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, - struct nexthop *rparent) -{ - struct nexthop *nexthop; - struct nexthop *nh1; - - for (nh1 = nh; nh1; nh1 = nh1->next) { - nexthop = nexthop_new(); - nexthop->vrf_id = nh1->vrf_id; - nexthop->ifindex = nh1->ifindex; - nexthop->type = nh1->type; - nexthop->flags = nh1->flags; - memcpy(&nexthop->gate, &nh1->gate, sizeof(nh1->gate)); - memcpy(&nexthop->src, &nh1->src, sizeof(nh1->src)); - memcpy(&nexthop->rmap_src, &nh1->rmap_src, - sizeof(nh1->rmap_src)); - nexthop->rparent = rparent; - if (nh1->nh_label) - nexthop_add_labels(nexthop, nh1->nh_label_type, - nh1->nh_label->num_labels, - &nh1->nh_label->label[0]); - nexthop_add(tnh, nexthop); - - if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) - copy_nexthops(&nexthop->resolved, nh1->resolved, - nexthop); - } -} - /* Free nexthop. */ void nexthop_free(struct nexthop *nexthop) { diff --git a/lib/nexthop.h b/lib/nexthop.h index 0be949688f..0ca8a0063a 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -109,24 +109,8 @@ struct nexthop { struct mpls_label_stack *nh_label; }; -/* The following for loop allows to iterate over the nexthop - * structure of routes. - * - * head: The pointer to the first nexthop in the chain. - * - * nexthop: The pointer to the current nexthop, either in the - * top-level chain or in a resolved chain. - */ -#define ALL_NEXTHOPS(head, nexthop) \ - (nexthop) = (head); \ - (nexthop); \ - (nexthop) = nexthop_next(nexthop) - struct nexthop *nexthop_new(void); -void nexthop_add(struct nexthop **target, struct nexthop *nexthop); -void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, - struct nexthop *rparent); void nexthop_free(struct nexthop *nexthop); void nexthops_free(struct nexthop *nexthop); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c new file mode 100644 index 0000000000..e7f10487d1 --- /dev/null +++ b/lib/nexthop_group.c @@ -0,0 +1,100 @@ +/* + * Nexthop Group structure definition. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include +#include + +#ifndef VTYSH_EXTRACT_PL +#include "lib/nexthop_group_clippy.c" +#endif + +/* Add nexthop to the end of a nexthop list. */ +void nexthop_add(struct nexthop **target, struct nexthop *nexthop) +{ + struct nexthop *last; + + for (last = *target; last && last->next; last = last->next) + ; + if (last) + last->next = nexthop; + else + *target = nexthop; + nexthop->prev = last; +} + +void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, + struct nexthop *rparent) +{ + struct nexthop *nexthop; + struct nexthop *nh1; + + for (nh1 = nh; nh1; nh1 = nh1->next) { + nexthop = nexthop_new(); + nexthop->vrf_id = nh1->vrf_id; + nexthop->ifindex = nh1->ifindex; + nexthop->type = nh1->type; + nexthop->flags = nh1->flags; + memcpy(&nexthop->gate, &nh1->gate, sizeof(nh1->gate)); + memcpy(&nexthop->src, &nh1->src, sizeof(nh1->src)); + memcpy(&nexthop->rmap_src, &nh1->rmap_src, + sizeof(nh1->rmap_src)); + nexthop->rparent = rparent; + if (nh1->nh_label) + nexthop_add_labels(nexthop, nh1->nh_label_type, + nh1->nh_label->num_labels, + &nh1->nh_label->label[0]); + nexthop_add(tnh, nexthop); + + if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) + copy_nexthops(&nexthop->resolved, nh1->resolved, + nexthop); + } +} + +DEFPY (nexthop_group, + nexthop_group_cmd, + "nexthop-group NAME", + "Enter into the nexthop-group submode\n" + "Specify the NAME of the nexthop-group\n") +{ + return CMD_SUCCESS; +} + +struct cmd_node nexthop_group_node = { + NH_GROUP_NODE, + "%s(config-nh-group)# ", + 1 +}; + +static int nexthop_group_write(struct vty *vty) +{ + vty_out(vty, "!\n"); + + return 1; +} + +void nexthop_group_init(void) +{ + install_node(&nexthop_group_node, nexthop_group_write); + install_element(CONFIG_NODE, &nexthop_group_cmd); +} diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h new file mode 100644 index 0000000000..561fe96425 --- /dev/null +++ b/lib/nexthop_group.h @@ -0,0 +1,54 @@ +/* + * Nexthop Group structure definition. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __NEXTHOP_GROUP__ +#define __NEXTHOP_GROUP__ + +/* + * What is a nexthop group? + * + * A nexthop group is a collection of nexthops that make up + * the ECMP path for the route. + * + * This module provides a proper abstraction to this idea. + */ +struct nexthop_group { + struct nexthop *nexthop; +}; + +void nexthop_group_init(void); + +void nexthop_add(struct nexthop **target, struct nexthop *nexthop); +void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, + struct nexthop *rparent); + +/* The following for loop allows to iterate over the nexthop + * structure of routes. + * + * head: The pointer to the first nexthop in the chain. + * + * nexthop: The pointer to the current nexthop, either in the + * top-level chain or in a resolved chain. + */ +#define ALL_NEXTHOPS(head, nhop) \ + (nhop) = (head.nexthop); \ + (nhop); \ + (nhop) = nexthop_next(nhop) +#endif diff --git a/lib/subdir.am b/lib/subdir.am index c8da5a2a8c..7d85b7a24d 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -45,6 +45,7 @@ lib_libfrr_la_SOURCES = \ lib/nexthop.c \ lib/netns_linux.c \ lib/netns_other.c \ + lib/nexthop_group.c \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ @@ -81,6 +82,8 @@ lib_libfrr_la_SOURCES = \ lib/plist_clippy.c: $(CLIPPY_DEPS) lib/plist.lo: lib/plist_clippy.c +lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) +lib/nexthop_group.lo: lib/nexthop_group_clippy.c pkginclude_HEADERS += \ lib/bfd.h \ @@ -124,6 +127,7 @@ pkginclude_HEADERS += \ lib/mpls.h \ lib/network.h \ lib/nexthop.h \ + lib/nexthop_group.h \ lib/ns.h \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ diff --git a/lib/workqueue.c b/lib/workqueue.c index d4ff3ee6ce..1af51c06c1 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -101,7 +101,7 @@ struct work_queue *work_queue_new(struct thread_master *m, return new; } -void work_queue_free(struct work_queue *wq) +void work_queue_free_original(struct work_queue *wq) { if (wq->thread != NULL) thread_cancel(wq->thread); @@ -119,6 +119,12 @@ void work_queue_free(struct work_queue *wq) return; } +void work_queue_free_and_null(struct work_queue **wq) +{ + work_queue_free_original(*wq); + *wq = NULL; +} + bool work_queue_is_scheduled(struct work_queue *wq) { return (wq->thread != NULL); diff --git a/lib/workqueue.h b/lib/workqueue.h index de49cb87fb..c9785de09a 100644 --- a/lib/workqueue.h +++ b/lib/workqueue.h @@ -28,7 +28,7 @@ DECLARE_MTYPE(WORK_QUEUE) /* Hold time for the initial schedule of a queue run, in millisec */ -#define WORK_QUEUE_DEFAULT_HOLD 50 +#define WORK_QUEUE_DEFAULT_HOLD 50 /* action value, for use by item processor and item error handlers */ typedef enum { @@ -148,8 +148,22 @@ static inline void work_queue_item_dequeue(struct work_queue *wq, * anything to it */ extern struct work_queue *work_queue_new(struct thread_master *, const char *); + /* destroy work queue */ -extern void work_queue_free(struct work_queue *); +/* + * The usage of work_queue_free is being transitioned to pass + * in the double pointer to remove use after free's. + */ +#if CONFDATE > 20190205 +CPP_NOTICE("work_queue_free without double pointer is deprecated, please fixup") +#endif +extern void work_queue_free_and_null(struct work_queue **); +extern void work_queue_free_original(struct work_queue *); +#define work_queue_free(X) \ + do { \ + work_queue_free_original((X)); \ + CPP_WARN("Please use work_queue_free_and_null"); \ + } while (0) /* Add the supplied data as an item onto the workqueue */ extern void work_queue_add(struct work_queue *, void *); diff --git a/lib/zclient.c b/lib/zclient.c index f853824bbb..fa3a5f6691 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1058,6 +1058,8 @@ int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api) stream_putl(s, api->tag); if (CHECK_FLAG(api->message, ZAPI_MESSAGE_MTU)) stream_putl(s, api->mtu); + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + stream_putl(s, api->tableid); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -1207,6 +1209,8 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETL(s, api->tag); if (CHECK_FLAG(api->message, ZAPI_MESSAGE_MTU)) STREAM_GETL(s, api->mtu); + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + STREAM_GETL(s, api->tableid); stream_failure: return 0; @@ -1233,6 +1237,35 @@ stream_failure: return false; } +bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, + uint32_t *priority, uint32_t *unique, + ifindex_t *ifindex, + enum zapi_rule_notify_owner *note) +{ + uint32_t prio, seq, uni; + ifindex_t ifi; + + STREAM_GET(note, s, sizeof(*note)); + + STREAM_GETL(s, seq); + STREAM_GETL(s, prio); + STREAM_GETL(s, uni); + STREAM_GETL(s, ifi); + + if (zclient_debug) + zlog_debug("%s: %u %u %u %u", __PRETTY_FUNCTION__, + seq, prio, uni, ifi); + *seqno = seq; + *priority = prio; + *unique = uni; + *ifindex = ifi; + + return true; + +stream_failure: + return false; +} + struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) { struct nexthop *n = nexthop_new(); @@ -2377,6 +2410,10 @@ static int zclient_read(struct thread *thread) (*zclient->route_notify_owner)(command, zclient, length, vrf_id); break; + case ZEBRA_RULE_NOTIFY_OWNER: + if (zclient->rule_notify_owner) + (*zclient->rule_notify_owner)(command, zclient, length, + vrf_id); default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index 1848440db2..1aa94b641c 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -129,6 +129,9 @@ typedef enum { ZEBRA_PW_SET, ZEBRA_PW_UNSET, ZEBRA_PW_STATUS_UPDATE, + ZEBRA_RULE_ADD, + ZEBRA_RULE_DELETE, + ZEBRA_RULE_NOTIFY_OWNER, } zebra_message_types_t; struct redist_proto { @@ -215,6 +218,8 @@ struct zclient { int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t); int (*route_notify_owner)(int command, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id); + int (*rule_notify_owner)(int command, struct zclient *zclient, + uint16_t length, vrf_id_t vrf_id); }; /* Zebra API message flag. */ @@ -225,6 +230,12 @@ struct zclient { #define ZAPI_MESSAGE_MTU 0x10 #define ZAPI_MESSAGE_SRCPFX 0x20 #define ZAPI_MESSAGE_LABEL 0x40 +/* + * This should only be used by a DAEMON that needs to communicate + * the table being used is not in the VRF. You must pass the + * default vrf, else this will be ignored. + */ +#define ZAPI_MESSAGE_TABLEID 0x80 /* Zserv protocol message header */ struct zserv_header { @@ -289,6 +300,8 @@ struct zapi_route { vrf_id_t vrf_id; + uint32_t tableid; + struct ethaddr rmac; }; @@ -350,6 +363,12 @@ enum zapi_route_notify_owner { ZAPI_ROUTE_REMOVE_FAIL, }; +enum zapi_rule_notify_owner { + ZAPI_RULE_FAIL_INSTALL, + ZAPI_RULE_INSTALLED, + ZAPI_RULE_REMOVED, +}; + /* Zebra MAC types */ #define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/ #define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/ @@ -520,6 +539,10 @@ extern int zapi_route_decode(struct stream *, struct zapi_route *); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note); +bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, + uint32_t *priority, uint32_t *unique, + ifindex_t *ifindex, + enum zapi_rule_notify_owner *note); extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); diff --git a/tests/lib/test_nexthop_iter.c b/tests/lib/test_nexthop_iter.c index 8d7353d4dc..f21f3bbb33 100644 --- a/tests/lib/test_nexthop_iter.c +++ b/tests/lib/test_nexthop_iter.c @@ -60,7 +60,7 @@ static void str_appendf(char **buf, const char *format, ...) * and its expected representation */ struct nexthop_chain { /* Head of the chain */ - struct nexthop *head; + struct nexthop_group head; /* Last nexthop in top chain */ struct nexthop *current_top; /* Last nexthop in current recursive chain */ @@ -85,12 +85,12 @@ static void nexthop_chain_add_top(struct nexthop_chain *nc) nh = calloc(sizeof(*nh), 1); assert(nh); - if (nc->head) { + if (nc->head.nexthop) { nc->current_top->next = nh; nh->prev = nc->current_top; nc->current_top = nh; } else { - nc->head = nc->current_top = nh; + nc->head.nexthop = nc->current_top = nh; } nc->current_recursive = NULL; str_appendf(&nc->repr, "%p\n", nh); @@ -166,8 +166,8 @@ static void nexthop_clear_recursive(struct nexthop *tcur) } static void nexthop_chain_clear(struct nexthop_chain *nc) { - nexthop_clear_recursive(nc->head); - nc->head = nc->current_top = nc->current_recursive = NULL; + nexthop_clear_recursive(nc->head.nexthop); + nc->head.nexthop = nc->current_top = nc->current_recursive = NULL; free(nc->repr); nc->repr = NULL; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 5f87d4c7f5..7dfe7753cf 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1499,6 +1499,7 @@ static int vtysh_exit(struct vty *vty) case PW_NODE: case LOGICALROUTER_NODE: case VRF_NODE: + case NH_GROUP_NODE: case ZEBRA_NODE: case BGP_NODE: case RIP_NODE: diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 0b3b6eed45..52b2692090 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -45,6 +45,7 @@ #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" #include "zebra/if_netlink.h" +#include "zebra/rule_netlink.h" #ifndef SO_RCVBUFFORCE #define SO_RCVBUFFORCE (33) @@ -85,6 +86,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_NEWNEIGH, "RTM_NEWNEIGH"}, {RTM_DELNEIGH, "RTM_DELNEIGH"}, {RTM_GETNEIGH, "RTM_GETNEIGH"}, + {RTM_NEWRULE, "RTM_NEWRULE"}, + {RTM_DELRULE, "RTM_DELRULE"}, + {RTM_GETRULE, "RTM_GETRULE"}, {0}}; static const struct message rtproto_str[] = { @@ -240,28 +244,24 @@ static int netlink_information_fetch(struct sockaddr_nl *snl, switch (h->nlmsg_type) { case RTM_NEWROUTE: return netlink_route_change(snl, h, ns_id, startup); - break; case RTM_DELROUTE: return netlink_route_change(snl, h, ns_id, startup); - break; case RTM_NEWLINK: return netlink_link_change(snl, h, ns_id, startup); - break; case RTM_DELLINK: return netlink_link_change(snl, h, ns_id, startup); - break; case RTM_NEWADDR: return netlink_interface_addr(snl, h, ns_id, startup); - break; case RTM_DELADDR: return netlink_interface_addr(snl, h, ns_id, startup); - break; case RTM_NEWNEIGH: return netlink_neigh_change(snl, h, ns_id); - break; case RTM_DELNEIGH: return netlink_neigh_change(snl, h, ns_id); - break; + case RTM_NEWRULE: + return netlink_rule_change(snl, h, ns_id, startup); + case RTM_DELRULE: + return netlink_rule_change(snl, h, ns_id, startup); default: if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Unknown netlink nlmsg_type %d vrf %u\n", @@ -788,7 +788,8 @@ void kernel_init(struct zebra_ns *zns) /* Initialize netlink sockets */ groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_MROUTE - | RTMGRP_NEIGH; + | RTMGRP_NEIGH + | RTNLGRP_IPV4_RULE | RTNLGRP_IPV6_RULE; snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); diff --git a/zebra/main.c b/zebra/main.c index 6a08247f11..00d853ea26 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -140,7 +140,7 @@ static void sigint(void) SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN); } if (zebrad.lsp_process_q) - work_queue_free(zebrad.lsp_process_q); + work_queue_free_and_null(&zebrad.lsp_process_q); vrf_terminate(); ns_walk_func(zebra_ns_disabled); @@ -151,7 +151,7 @@ static void sigint(void) route_map_finish(); list_delete_and_null(&zebrad.client_list); - work_queue_free(zebrad.ribq); + work_queue_free_and_null(&zebrad.ribq); meta_queue_free(zebrad.mq); frr_fini(); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index c03d755b2a..a7b2361ac6 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -519,8 +519,8 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, afi = family2afi(rn->p.family); if (rmap_name) ret = zebra_import_table_route_map_check( - afi, re->type, &rn->p, re->nexthop, re->vrf_id, re->tag, - rmap_name); + afi, re->type, &rn->p, re->ng.nexthop, re->vrf_id, + re->tag, rmap_name); if (ret != RMAP_MATCH) { zebra_del_import_table_entry(rn, re); @@ -552,7 +552,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, newre->nexthop_num = 0; newre->uptime = time(NULL); newre->instance = re->table; - route_entry_copy_nexthops(newre, re->nexthop); + route_entry_copy_nexthops(newre, re->ng.nexthop); rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre); @@ -568,8 +568,8 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) prefix_copy(&p, &rn->p); rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, - re->flags, &p, NULL, re->nexthop, zebrad.rtm_table_default, - re->metric, false, NULL); + re->flags, &p, NULL, re->ng.nexthop, + zebrad.rtm_table_default, re->metric, false, NULL); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index 5f03f1a131..5dd444dce0 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -29,6 +29,7 @@ #include "table.h" #include "queue.h" #include "nexthop.h" +#include "nexthop_group.h" #include "vrf.h" #include "if.h" #include "mpls.h" @@ -43,7 +44,7 @@ struct route_entry { struct route_entry *prev; /* Nexthop structure */ - struct nexthop *nexthop; + struct nexthop_group ng; /* Tag */ route_tag_t tag; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index e95665feb4..2b758c58d8 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1398,7 +1398,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, /* Count overall nexthops so we can decide whether to use singlepath * or multipath case. */ nexthop_num = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; if (cmd == RTM_NEWROUTE && !NEXTHOP_IS_ACTIVE(nexthop->flags)) @@ -1413,7 +1413,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, /* Singlepath case. */ if (nexthop_num == 1 || multipath_num == 1) { nexthop_num = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { /* * So we want to cover 2 types of blackhole * routes here: @@ -1513,7 +1513,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, rtnh = RTA_DATA(rta); nexthop_num = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (nexthop_num >= multipath_num) break; diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 1aa402672e..433faf789a 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -123,7 +123,7 @@ static int kernel_rtm_ipv4(int cmd, struct prefix *p, struct route_entry *re) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -303,7 +303,7 @@ static int kernel_rtm_ipv6(int cmd, struct prefix *p, struct route_entry *re) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 95bc8db1c9..38321bc416 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -263,4 +263,8 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if) { } +void kernel_read_pbr_rules(struct zebra_ns *zns) +{ +} + #endif /* SUNOS_5 */ diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index ec29d1820e..e992046078 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -25,7 +25,9 @@ #include "vty.h" #include "zebra/rt.h" +#include "zebra/zebra_pbr.h" #include "zebra/rt_netlink.h" +#include "zebra/rule_netlink.h" void route_read(struct zebra_ns *zns) { @@ -53,4 +55,9 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if) netlink_neigh_read_for_vlan(zns, vlan_if); } +void kernel_read_pbr_rules(struct zebra_ns *zns) +{ + netlink_rules_read(zns); +} + #endif /* GNU_LINUX */ diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index 4f5a80612e..fba67e3d0c 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -30,6 +30,7 @@ #include "zebra/rt.h" #include "zebra/kernel_socket.h" +#include "zebra/zebra_pbr.h" /* Kernel routing table read up by sysctl function. */ void route_read(struct zebra_ns *zns) @@ -92,4 +93,8 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if) { } +void kernel_read_pbr_rules(struct zebra_ns *zns) +{ +} + #endif /* !defined(GNU_LINUX) && !defined(SUNOS_5) */ diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c new file mode 100644 index 0000000000..2122f9f5fa --- /dev/null +++ b/zebra/rule_netlink.c @@ -0,0 +1,267 @@ +/* + * Zebra Policy Based Routing (PBR) interaction with the kernel using + * netlink. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#ifdef HAVE_NETLINK + +#include "if.h" +#include "prefix.h" +#include "vrf.h" + +#include +#include "zebra/zserv.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/rt.h" +#include "zebra/interface.h" +#include "zebra/debug.h" +#include "zebra/rtadv.h" +#include "zebra/kernel_netlink.h" +#include "zebra/rule_netlink.h" +#include "zebra/zebra_pbr.h" + +/* definitions */ + +/* static function declarations */ + +/* Private functions */ + +/* Install or uninstall specified rule for a specific interface. + * Form netlink message and ship it. Currently, notify status after + * waiting for netlink status. + */ +static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) +{ + int family; + int bytelen; + struct { + struct nlmsghdr n; + struct fib_rule_hdr frh; + char buf[NL_PKT_BUF_SIZE]; + } req; + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct sockaddr_nl snl; + char buf1[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; + + memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); + family = PREFIX_FAMILY(&rule->filter.src_ip); + bytelen = (family == AF_INET ? 4 : 16); + + req.n.nlmsg_type = cmd; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + + req.frh.family = family; + req.frh.action = FR_ACT_TO_TBL; + + if (cmd == RTM_NEWRULE) + req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + /* rule's pref # */ + addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->priority); + + /* interface on which applied */ + if (rule->ifp) + addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifp->name, + strlen(rule->ifp->name) + 1); + + /* source IP, if specified */ + if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { + req.frh.src_len = rule->filter.src_ip.prefixlen; + addattr_l(&req.n, sizeof(req), FRA_SRC, + &rule->filter.src_ip.u.prefix, bytelen); + } + /* destination IP, if specified */ + if (IS_RULE_FILTERING_ON_DST_IP(rule)) { + req.frh.dst_len = rule->filter.dst_ip.prefixlen; + addattr_l(&req.n, sizeof(req), FRA_DST, + &rule->filter.dst_ip.u.prefix, bytelen); + } + + /* Route table to use to forward, if filter criteria matches. */ + if (rule->action.table < 256) + req.frh.table = rule->action.table; + else { + req.frh.table = RT_TABLE_UNSPEC; + addattr32(&req.n, sizeof(req), FRA_TABLE, + rule->action.table); + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Tx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", + nl_msg_type_to_str(cmd), nl_family_to_str(family), + rule->ifp ? rule->ifp->name : "Unknown", + rule->ifp ? rule->ifp->ifindex : 0, rule->priority, + prefix2str(&rule->filter.src_ip, buf1, sizeof(buf1)), + prefix2str(&rule->filter.dst_ip, buf2, sizeof(buf2)), + rule->action.table); + + /* Ship off the message. + * Note: Currently, netlink_talk() is a blocking call which returns + * back the status. + */ + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + return netlink_talk(netlink_talk_filter, &req.n, + &zns->netlink_cmd, zns, 0); +} + + +/* Public functions */ +/* + * Install specified rule for a specific interface. The preference is what + * goes in the rule to denote relative ordering; it may or may not be the + * same as the rule's user-defined sequence number. + */ +void kernel_add_pbr_rule(struct zebra_pbr_rule *rule) +{ + int ret = 0; + + ret = netlink_rule_update(RTM_NEWRULE, rule); + kernel_pbr_rule_add_del_status(rule, + (!ret) ? SOUTHBOUND_INSTALL_SUCCESS + : SOUTHBOUND_INSTALL_FAILURE); +} + +/* + * Uninstall specified rule for a specific interface. + */ +void kernel_del_pbr_rule(struct zebra_pbr_rule *rule) +{ + int ret = 0; + + ret = netlink_rule_update(RTM_DELRULE, rule); + kernel_pbr_rule_add_del_status(rule, + (!ret) ? SOUTHBOUND_DELETE_SUCCESS + : SOUTHBOUND_DELETE_FAILURE); +} + +/* + * Handle netlink notification informing a rule add or delete. + * Handling of an ADD is TBD. + * DELs are notified up, if other attributes indicate it may be a + * notification of interest. The expectation is that if this corresponds + * to a PBR rule added by FRR, it will be readded. + */ +int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id, int startup) +{ + struct zebra_ns *zns; + struct fib_rule_hdr *frh; + struct rtattr *tb[FRA_MAX + 1]; + int len; + char *ifname; + struct zebra_pbr_rule rule; + char buf1[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; + + /* Basic validation followed by extracting attributes. */ + if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) + return 0; + + /* TBD */ + if (h->nlmsg_type == RTM_NEWRULE) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); + if (len < 0) + return -1; + + frh = NLMSG_DATA(h); + if (frh->family != AF_INET && frh->family != AF_INET6) + return 0; + if (frh->action != FR_ACT_TO_TBL) + return 0; + + memset(tb, 0, sizeof(tb)); + netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); + + /* TBD: We don't care about rules not specifying an IIF. */ + if (tb[FRA_IFNAME] == NULL) + return 0; + + /* If we don't know the interface, we don't care. */ + ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); + zns = zebra_ns_lookup(ns_id); + rule.ifp = if_lookup_by_name_per_ns(zns, ifname); + if (!rule.ifp) + return 0; + + memset(&rule, 0, sizeof(rule)); + if (tb[FRA_PRIORITY]) + rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); + + if (tb[FRA_SRC]) { + if (frh->family == AF_INET) + memcpy(&rule.filter.src_ip.u.prefix4, + RTA_DATA(tb[FRA_SRC]), 4); + else + memcpy(&rule.filter.src_ip.u.prefix6, + RTA_DATA(tb[FRA_SRC]), 16); + rule.filter.src_ip.prefixlen = frh->src_len; + rule.filter.filter_bm |= PBR_FILTER_SRC_IP; + } + + if (tb[FRA_DST]) { + if (frh->family == AF_INET) + memcpy(&rule.filter.dst_ip.u.prefix4, + RTA_DATA(tb[FRA_DST]), 4); + else + memcpy(&rule.filter.dst_ip.u.prefix6, + RTA_DATA(tb[FRA_DST]), 16); + rule.filter.dst_ip.prefixlen = frh->dst_len; + rule.filter.filter_bm |= PBR_FILTER_DST_IP; + } + + if (tb[FRA_TABLE]) + rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]); + else + rule.action.table = frh->table; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", + nl_msg_type_to_str(h->nlmsg_type), + nl_family_to_str(frh->family), rule.ifp->name, + rule.ifp->ifindex, rule.priority, + prefix2str(&rule.filter.src_ip, buf1, sizeof(buf1)), + prefix2str(&rule.filter.dst_ip, buf2, sizeof(buf2)), + rule.action.table); + + return kernel_pbr_rule_del(&rule); +} + +/* + * Get to know existing PBR rules in the kernel - typically called at startup. + * TBD. + */ +int netlink_rules_read(struct zebra_ns *zns) +{ + return 0; +} + +#endif /* HAVE_NETLINK */ diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h new file mode 100644 index 0000000000..3a9b51309e --- /dev/null +++ b/zebra/rule_netlink.h @@ -0,0 +1,42 @@ +/* + * Zebra Policy Based Routing (PBR) interaction with the kernel using + * netlink - public definitions and function declarations. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_RULE_NETLINK_H +#define _ZEBRA_RULE_NETLINK_H + +#ifdef HAVE_NETLINK + +/* + * Handle netlink notification informing a rule add or delete. + */ +extern int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id, int startup); + +/* + * Get to know existing PBR rules in the kernel - typically called at startup. + */ +extern int netlink_rules_read(struct zebra_ns *zns); + +#endif /* HAVE_NETLINK */ + +#endif /* _ZEBRA_RULE_NETLINK_H */ diff --git a/zebra/rule_socket.c b/zebra/rule_socket.c new file mode 100644 index 0000000000..46c53f9e02 --- /dev/null +++ b/zebra/rule_socket.c @@ -0,0 +1,51 @@ +/* + * Zebra Policy Based Routing (PBR) interaction with the kernel using + * netlink. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#ifndef HAVE_NETLINK + +#include "if.h" +#include "prefix.h" +#include "vrf.h" + +#include "zebra/zserv.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/rt.h" +#include "zebra/interface.h" +#include "zebra/debug.h" +#include "zebra/rtadv.h" +#include "zebra/kernel_netlink.h" +#include "zebra/rule_netlink.h" +#include "zebra/zebra_pbr.h" + +void kernel_add_pbr_rule(struct zebra_pbr_rule *rule) +{ +} +void kernel_del_pbr_rule(struct zebra_pbr_rule *rule) +{ +} + +#endif diff --git a/zebra/subdir.am b/zebra/subdir.am index bb7439c0f6..ef157b7539 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -45,6 +45,8 @@ zebra_zebra_SOURCES = \ zebra/rtread_getmsg.c \ zebra/rtread_netlink.c \ zebra/rtread_sysctl.c \ + zebra/rule_netlink.c \ + zebra/rule_socket.c \ zebra/zebra_l2.c \ zebra/zebra_memory.c \ zebra/zebra_mpls.c \ @@ -54,6 +56,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_mpls_vty.c \ zebra/zebra_mroute.c \ zebra/zebra_ns.c \ + zebra/zebra_pbr.c \ zebra/zebra_ptm.c \ zebra/zebra_ptm_redistribute.c \ zebra/zebra_pw.c \ @@ -90,12 +93,14 @@ noinst_HEADERS += \ zebra/rt.h \ zebra/rt_netlink.h \ zebra/rtadv.h \ + zebra/rule_netlink.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ zebra/zebra_memory.h \ zebra/zebra_mpls.h \ zebra/zebra_mroute.h \ zebra/zebra_ns.h \ + zebra/zebra_pbr.h \ zebra/zebra_ptm.h \ zebra/zebra_ptm_redistribute.h \ zebra/zebra_pw.h \ diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 27c7891372..97a0e142fb 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -249,7 +249,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->rtm_type = RTN_UNICAST; ri->metric = &re->metric; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (ri->num_nhs >= multipath_num) break; diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index b850f1fb1e..e661b6efc7 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -170,7 +170,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, * Figure out the set of nexthops to be added to the message. */ num_nhs = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (num_nhs >= multipath_num) break; diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 2dc98127f5..0af06806d3 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -182,7 +182,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, * the label advertised by the recursive nexthop (plus we don't have the * logic yet to push multiple labels). */ - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { /* Skip inactive and recursive entries. */ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; @@ -637,7 +637,7 @@ static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, || !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) continue; - for (match_nh = match->nexthop; match_nh; + for (match_nh = match->ng.nexthop; match_nh; match_nh = match_nh->next) { if (match->type == ZEBRA_ROUTE_CONNECT || nexthop->ifindex == match_nh->ifindex) { @@ -688,10 +688,10 @@ static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe, break; } - if (!match || !match->nexthop) + if (!match || !match->ng.nexthop) return 0; - nexthop->ifindex = match->nexthop->ifindex; + nexthop->ifindex = match->ng.nexthop->ifindex; return 1; } @@ -2286,7 +2286,7 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, if (re == NULL) return -1; - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: @@ -2504,7 +2504,7 @@ void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi) for (rn = route_top(table); rn; rn = route_next(rn)) { update = 0; RNODE_FOREACH_RE (rn, re) { - for (nexthop = re->nexthop; nexthop; + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { if (nexthop->nh_label_type != ZEBRA_LSP_LDP) continue; diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 580ff3eec1..192e8ad413 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -36,6 +36,7 @@ #include "debug.h" #include "zebra_netns_notify.h" #include "zebra_netns_id.h" +#include "zebra_pbr.h" extern struct zebra_privs_t zserv_privs; @@ -211,12 +212,15 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) struct zebra_ns_table *znst; struct zebra_ns *zns = (struct zebra_ns *)(*info); + hash_clean(zns->rules_hash, zebra_pbr_rules_free); + hash_free(zns->rules_hash); while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) { znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables); RB_REMOVE(zebra_ns_table_head, &zns->ns_tables, znst); zebra_ns_free_table(znst); } + route_table_finish(zns->if_table); zebra_vxlan_ns_disable(zns); #if defined(HAVE_RTADV) @@ -257,6 +261,9 @@ int zebra_ns_init(void) /* Default NS is activated */ zebra_ns_enable(ns_id, (void **)&dzns); + dzns->rules_hash = + hash_create_size(8, zebra_pbr_rules_hash_key, + zebra_pbr_rules_hash_equal, "Rules Hash"); if (vrf_is_backend_netns()) { ns_add_hook(NS_NEW_HOOK, zebra_ns_new); ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled); diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 3a998a49ff..19ecba1f0e 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -23,6 +23,7 @@ #define __ZEBRA_NS_H__ #include +#include #ifdef HAVE_NETLINK /* Socket interface to kernel */ @@ -70,6 +71,8 @@ struct zebra_ns { struct zebra_ns_table_head ns_tables; + struct hash *rules_hash; + /* Back pointer */ struct ns *ns; }; diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c new file mode 100644 index 0000000000..4da09dc538 --- /dev/null +++ b/zebra/zebra_pbr.c @@ -0,0 +1,162 @@ +/* Zebra Policy Based Routing (PBR) main handling. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include +#include + +#include "zebra/zebra_pbr.h" +#include "zebra/rt.h" + +/* definitions */ + +/* static function declarations */ + +/* Private functions */ + +/* Public functions */ +void zebra_pbr_rules_free(void *arg) +{ + struct zebra_pbr_rule *rule; + + rule = (struct zebra_pbr_rule *)arg; + + kernel_del_pbr_rule(rule); + XFREE(MTYPE_TMP, rule); +} + +uint32_t zebra_pbr_rules_hash_key(void *arg) +{ + struct zebra_pbr_rule *rule; + uint32_t key; + + rule = (struct zebra_pbr_rule *)arg; + key = jhash_3words(rule->seq, rule->priority, rule->action.table, + prefix_hash_key(&rule->filter.src_ip)); + if (rule->ifp) + key = jhash_1word(rule->ifp->ifindex, key); + else + key = jhash_1word(0, key); + + return jhash_3words(rule->filter.src_port, rule->filter.dst_port, + prefix_hash_key(&rule->filter.dst_ip), + jhash_1word(rule->unique, key)); +} + +int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) +{ + const struct zebra_pbr_rule *r1, *r2; + + r1 = (const struct zebra_pbr_rule *)arg1; + r2 = (const struct zebra_pbr_rule *)arg2; + + if (r1->seq != r2->seq) + return 0; + + if (r1->priority != r2->priority) + return 0; + + if (r1->unique != r2->unique) + return 0; + + if (r1->action.table != r2->action.table) + return 0; + + if (r1->filter.src_port != r2->filter.src_port) + return 0; + + if (r1->filter.dst_port != r2->filter.dst_port) + return 0; + + if (!prefix_same(&r1->filter.src_ip, &r2->filter.src_ip)) + return 0; + + if (!prefix_same(&r1->filter.dst_ip, &r2->filter.dst_ip)) + return 0; + + if (r1->ifp != r2->ifp) + return 0; + + return 1; +} + +static void *pbr_rule_alloc_intern(void *arg) +{ + struct zebra_pbr_rule *zpr; + struct zebra_pbr_rule *new; + + zpr = (struct zebra_pbr_rule *)arg; + + new = XCALLOC(MTYPE_TMP, sizeof(*new)); + + memcpy(new, zpr, sizeof(*zpr)); + + return new; +} + +void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule) +{ + (void)hash_get(zns->rules_hash, rule, pbr_rule_alloc_intern); + kernel_add_pbr_rule(rule); +} + +void zebra_pbr_del_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule) +{ + struct zebra_pbr_rule *lookup; + + lookup = hash_lookup(zns->rules_hash, rule); + kernel_del_pbr_rule(rule); + + if (lookup) + XFREE(MTYPE_TMP, lookup); + else + zlog_warn("%s: Rule being deleted we know nothing about", + __PRETTY_FUNCTION__); +} + +/* + * Handle success or failure of rule (un)install in the kernel. + */ +void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, + enum southbound_results res) +{ + switch (res) { + case SOUTHBOUND_INSTALL_SUCCESS: + zsend_rule_notify_owner(rule, ZAPI_RULE_INSTALLED); + break; + case SOUTHBOUND_INSTALL_FAILURE: + zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_INSTALL); + break; + case SOUTHBOUND_DELETE_SUCCESS: + break; + case SOUTHBOUND_DELETE_FAILURE: + break; + } +} + +/* + * Handle rule delete notification from kernel. + */ +int kernel_pbr_rule_del(struct zebra_pbr_rule *rule) +{ + return 0; +} diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h new file mode 100644 index 0000000000..4b28b6ec35 --- /dev/null +++ b/zebra/zebra_pbr.h @@ -0,0 +1,138 @@ +/* + * Zebra Policy Based Routing (PBR) Data structures and definitions + * These are public definitions referenced by multiple files. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_PBR_H +#define _ZEBRA_PBR_H + +#include + +#include "prefix.h" +#include "if.h" + +#include "rt.h" + +/* + * A PBR filter + * + * The filter or match criteria in a PBR rule. + * For simplicity, all supported filters are grouped into a structure rather + * than delineating further. A bitmask denotes which filters are actually + * specified. + */ +struct zebra_pbr_filter { + uint32_t filter_bm; +#define PBR_FILTER_SRC_IP (1 << 0) +#define PBR_FILTER_DST_IP (1 << 1) +#define PBR_FILTER_SRC_PORT (1 << 2) +#define PBR_FILTER_DST_PORT (1 << 3) + + /* Source and Destination IP address with masks. */ + struct prefix src_ip; + struct prefix dst_ip; + + /* Source and Destination higher-layer (TCP/UDP) port numbers. */ + uint16_t src_port; + uint16_t dst_port; +}; + +#define IS_RULE_FILTERING_ON_SRC_IP(r) \ + (r->filter.filter_bm & PBR_FILTER_SRC_IP) +#define IS_RULE_FILTERING_ON_DST_IP(r) \ + (r->filter.filter_bm & PBR_FILTER_DST_IP) +#define IS_RULE_FILTERING_ON_SRC_PORT(r) \ + (r->filter.filter_bm & PBR_FILTER_SRC_PORT) +#define IS_RULE_FILTERING_ON_DST_PORT(r) \ + (r->filter.filter_bm & PBR_FILTER_DST_PORT) + +/* + * A PBR action + * + * The action corresponding to a PBR rule. + * While the user specifies the action in a particular way, the forwarding + * plane implementation (Linux only) requires that to be encoded into a + * route table and the rule then point to that route table; in some cases, + * the user criteria may directly point to a table too. + */ +struct zebra_pbr_action { + uint32_t table; +}; + +/* + * A PBR rule + * + * This is a combination of the filter criteria and corresponding action. + * Rules also have a user-defined sequence number which defines the relative + * order amongst rules. + */ +struct zebra_pbr_rule { + /* + * Originating zclient sock fd, so we can know who to send + * back to. + */ + int sock; + + uint32_t seq; + uint32_t priority; + struct interface *ifp; + uint32_t unique; + struct zebra_pbr_filter filter; + struct zebra_pbr_action action; +}; + +void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule); +void zebra_pbr_del_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule); + +/* + * Install specified rule for a specific interface. + * It is possible that the user-defined sequence number and the one in the + * forwarding plane may not coincide, hence the API requires a separate + * rule priority - maps to preference/FRA_PRIORITY on Linux. + */ +extern void kernel_add_pbr_rule(struct zebra_pbr_rule *rule); + +/* + * Uninstall specified rule for a specific interface. + */ +extern void kernel_del_pbr_rule(struct zebra_pbr_rule *rule); + +/* + * Get to know existing PBR rules in the kernel - typically called at startup. + */ +extern void kernel_read_pbr_rules(struct zebra_ns *zns); + +enum southbound_results; +/* + * Handle success or failure of rule (un)install in the kernel. + */ +extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, + enum southbound_results res); + +/* + * Handle rule delete notification from kernel. + */ +extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule); + +extern void zebra_pbr_rules_free(void *arg); +extern uint32_t zebra_pbr_rules_hash_key(void *arg); +extern int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2); +#endif /* _ZEBRA_PBR_H */ diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 96bee36be6..68ad69397f 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -256,7 +256,7 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw) * Need to ensure that there's a label binding for all nexthops. * Otherwise, ECMP for this route could render the pseudowire unusable. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (!nexthop->nh_label) { if (IS_ZEBRA_DEBUG_PW) zlog_warn("%s: unlabeled route for %s", diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c5906f5829..1204da92fb 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -183,7 +183,7 @@ int zebra_check_addr(struct prefix *p) /* Add nexthop to the end of a rib node's nexthop list */ void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop) { - nexthop_add(&re->nexthop, nexthop); + nexthop_add(&re->ng.nexthop, nexthop); re->nexthop_num++; } @@ -193,8 +193,8 @@ void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop) */ void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh) { - assert(!re->nexthop); - copy_nexthops(&re->nexthop, nh, NULL); + assert(!re->ng.nexthop); + copy_nexthops(&re->ng.nexthop, nh, NULL); for (struct nexthop *nexthop = nh; nexthop; nexthop = nexthop->next) re->nexthop_num++; } @@ -207,7 +207,7 @@ void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop) if (nexthop->prev) nexthop->prev->next = nexthop->next; else - re->nexthop = nexthop->next; + re->ng.nexthop = nexthop->next; re->nexthop_num--; } @@ -507,7 +507,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ - newhop = match->nexthop; + newhop = match->ng.nexthop; if (newhop) { if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -516,7 +516,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 1; } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { resolved = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) { + for (ALL_NEXTHOPS(match->ng, newhop)) { if (!CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_FIB)) continue; @@ -539,7 +539,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return resolved; } else if (re->type == ZEBRA_ROUTE_STATIC) { resolved = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) { + for (ALL_NEXTHOPS(match->ng, newhop)) { if (!CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_FIB)) continue; @@ -610,7 +610,7 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, } else { if (match->type != ZEBRA_ROUTE_CONNECT) { int found = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) + for (ALL_NEXTHOPS(match->ng, newhop)) if (CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_FIB)) { found = 1; @@ -733,7 +733,7 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) if (match->type == ZEBRA_ROUTE_CONNECT) return match; - for (ALL_NEXTHOPS(match->nexthop, nexthop)) + for (ALL_NEXTHOPS(match->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) return match; @@ -793,7 +793,7 @@ int rib_lookup_ipv4_route(struct prefix_ipv4 *p, union sockunion *qgate, /* Ok, we have a cood candidate, let's check it's nexthop list... */ nexthops_active = 0; - for (ALL_NEXTHOPS(match->nexthop, nexthop)) + for (ALL_NEXTHOPS(match->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { nexthops_active = 1; if (nexthop->gate.ipv4.s_addr == sockunion2ip(qgate)) @@ -959,7 +959,7 @@ static int nexthop_active_update(struct route_node *rn, struct route_entry *re, re->nexthop_active_num = 0; UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { /* No protocol daemon provides src and so we're skipping * tracking it */ prev_src = nexthop->rmap_src; @@ -1003,7 +1003,7 @@ int zebra_rib_labeled_unicast(struct route_entry *re) if (re->type != ZEBRA_ROUTE_BGP) return 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) if (!nexthop->nh_label || !nexthop->nh_label->num_labels) return 0; @@ -1023,7 +1023,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p, switch (res) { case SOUTHBOUND_INSTALL_SUCCESS: dest->selected_fib = re; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -1056,7 +1056,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p, */ if (dest->selected_fib == re) dest->selected_fib = NULL; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); zsend_route_notify_owner(re, p, ZAPI_ROUTE_REMOVED); @@ -1089,15 +1089,15 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, srcdest_rnode_prefixes(rn, &p, &src_p); if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); return; } else { struct nexthop *prev; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE); - for (ALL_NEXTHOPS(re->nexthop, prev)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_DUPLICATE); + for (ALL_NEXTHOPS(re->ng, prev)) { if (prev == nexthop) break; if (nexthop_same_firsthop(nexthop, prev)) { @@ -1138,7 +1138,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) srcdest_rnode_prefixes(rn, &p, &src_p); if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); return; } @@ -1388,7 +1388,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, if (!RIB_SYSTEM_ROUTE(old)) rib_uninstall_kernel(rn, old); } else { - for (nexthop = old->nexthop; nexthop; + for (nexthop = old->ng.nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1444,7 +1444,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, if (!RIB_SYSTEM_ROUTE(new)) { bool in_fib = false; - for (ALL_NEXTHOPS(new->nexthop, nexthop)) + for (ALL_NEXTHOPS(new->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { in_fib = true; @@ -1676,13 +1676,16 @@ static void rib_process(struct route_node *rn) /* Redistribute SELECTED entry */ if (old_selected != new_selected || selected_changed) { - struct nexthop *nexthop; + struct nexthop *nexthop = NULL; /* Check if we have a FIB route for the destination, otherwise, * don't redistribute it */ - for (ALL_NEXTHOPS(new_fib ? new_fib->nexthop : NULL, nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { - break; + if (new_fib) { + for (ALL_NEXTHOPS(new_fib->ng, nexthop)) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB)) { + break; + } } } if (!nexthop) @@ -2116,9 +2119,9 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) /* free RE and nexthops */ if (re->type == ZEBRA_ROUTE_STATIC) - zebra_deregister_rnh_static_nexthops(re->vrf_id, re->nexthop, + zebra_deregister_rnh_static_nexthops(re->vrf_id, re->ng.nexthop, rn); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); } @@ -2182,7 +2185,7 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", func, re->nexthop_num, re->nexthop_active_num); - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { inet_ntop(p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN); zlog_debug("%s: %s %s[%u] with flags %s%s%s", func, (nexthop->rparent ? " NH" : "NH"), straddr, @@ -2357,7 +2360,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, /* If this route is kernel route, set FIB flag to the route. */ if (RIB_SYSTEM_ROUTE(re)) - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); /* Link new re to node.*/ @@ -2443,7 +2446,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, continue; if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric) continue; - if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->nexthop) + if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng.nexthop) && rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) { if (rtnh->ifindex != nh->ifindex) continue; @@ -2456,7 +2459,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, same = re; break; } - for (ALL_NEXTHOPS(re->nexthop, rtnh)) + for (ALL_NEXTHOPS(re->ng, rtnh)) if (nexthop_same_no_recurse(rtnh, nh)) { same = re; break; @@ -2493,7 +2496,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, } if (allow_delete) { /* Unset flags. */ - for (rtnh = fib->nexthop; rtnh; + for (rtnh = fib->ng.nexthop; rtnh; rtnh = rtnh->next) UNSET_FLAG(rtnh->flags, NEXTHOP_FLAG_FIB); @@ -2547,7 +2550,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { struct nexthop *tmp_nh; - for (ALL_NEXTHOPS(re->nexthop, tmp_nh)) { + for (ALL_NEXTHOPS(re->ng, tmp_nh)) { struct ipaddr vtep_ip; memset(&vtep_ip, 0, sizeof(struct ipaddr)); @@ -2642,7 +2645,7 @@ static void rib_update_table(struct route_table *table, continue; } - for (nh = re->nexthop; nh; nh = nh->next) + for (nh = re->ng.nexthop; nh; nh = nh->next) if (!(nh->type == NEXTHOP_TYPE_IPV4 || nh->type == NEXTHOP_TYPE_IPV6)) break; @@ -2759,7 +2762,7 @@ static void rib_sweep_table(struct route_table *table) * to a different spot (ie startup ) * this decision needs to be revisited */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); rib_uninstall_kernel(rn, re); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 1e9fe875e1..dd3fe17702 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -355,7 +355,8 @@ static int zebra_rnh_apply_nht_rmap(int family, struct route_node *prn, rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6; if (prn && re) { - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; + nexthop = nexthop->next) { ret = zebra_nht_route_map_check(rmap_family, proto, &prn->p, re, nexthop); if (ret != RMAP_DENYMATCH) { @@ -428,7 +429,7 @@ static void zebra_rnh_eval_import_check_entry(vrf_id_t vrfid, int family, struct nexthop *nexthop; if (re && (rnh->state == NULL)) { - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { state_changed = 1; break; @@ -559,7 +560,7 @@ static void zebra_rnh_process_static_routes(vrf_id_t vrfid, int family, * be having multiple. We care here only about * registered nexthops. */ - for (nexthop = sre->nexthop; nexthop; + for (nexthop = sre->ng.nexthop; nexthop; nexthop = nexthop->next) { switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -673,7 +674,7 @@ zebra_rnh_resolve_nexthop_entry(vrf_id_t vrfid, int family, if (re->type == ZEBRA_ROUTE_NHRP) { struct nexthop *nexthop; - for (nexthop = re->nexthop; nexthop; + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if (nexthop->type == NEXTHOP_TYPE_IFINDEX) @@ -925,8 +926,8 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, return; /* free RE and nexthops */ - zebra_deregister_rnh_static_nexthops(vrf_id, re->nexthop, rn); - nexthops_free(re->nexthop); + zebra_deregister_rnh_static_nexthops(vrf_id, re->ng.nexthop, rn); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); } @@ -949,7 +950,7 @@ static void copy_state(struct rnh *rnh, struct route_entry *re, state->metric = re->metric; state->vrf_id = re->vrf_id; - route_entry_copy_nexthops(state, re->nexthop); + route_entry_copy_nexthops(state, re->ng.nexthop); rnh->state = state; } @@ -1022,7 +1023,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, num = 0; nump = stream_get_endp(s); stream_putc(s, 0); - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if ((CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) || CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -1115,7 +1116,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty) if (rnh->state) { vty_out(vty, " resolved via %s\n", zebra_route_string(rnh->state->type)); - for (nexthop = rnh->state->nexthop; nexthop; + for (nexthop = rnh->state->ng.nexthop; nexthop; nexthop = nexthop->next) print_nh(nexthop, vty); } else diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index 48218effd1..3ab208d30b 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -285,8 +285,8 @@ static void check_replace(struct route_node *np2, struct route_entry *re2, return; } - if (in_addr_cmp((u_char *)&(*re)->nexthop->gate.ipv4, - (u_char *)&re2->nexthop->gate.ipv4) + if (in_addr_cmp((u_char *)&(*re)->ng.nexthop->gate.ipv4, + (u_char *)&re2->ng.nexthop->gate.ipv4) <= 0) return; @@ -370,9 +370,9 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], for (*np = route_top(table); *np; *np = route_next(*np)) { if (!in_addr_cmp(&(*np)->p.u.prefix, (u_char *)&dest)) { RNODE_FOREACH_RE (*np, *re) { - if (!in_addr_cmp((u_char *)&(*re) - ->nexthop->gate - .ipv4, + if (!in_addr_cmp( + (u_char *)&(*re) + ->ng.nexthop->gate.ipv4, (u_char *)&nexthop)) if (proto == proto_trans((*re)->type)) @@ -404,9 +404,10 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], if ((policy < policy2) || ((policy == policy2) && (proto < proto2)) || ((policy == policy2) && (proto == proto2) - && (in_addr_cmp((u_char *)&re2->nexthop - ->gate.ipv4, - (u_char *)&nexthop) + && (in_addr_cmp( + (u_char *)&re2->ng.nexthop + ->gate.ipv4, + (u_char *)&nexthop) >= 0))) check_replace(np2, re2, np, re); } @@ -430,7 +431,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], { struct nexthop *nexthop; - nexthop = (*re)->nexthop; + nexthop = (*re)->ng.nexthop; if (nexthop) { pnt = (u_char *)&nexthop->gate.ipv4; for (i = 0; i < 4; i++) @@ -459,7 +460,7 @@ static u_char *ipFwTable(struct variable *v, oid objid[], size_t *objid_len, if (!np) return NULL; - nexthop = re->nexthop; + nexthop = re->ng.nexthop; if (!nexthop) return NULL; diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index b4c5b70da0..f3921790a6 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -300,7 +300,7 @@ void static_uninstall_route(afi_t afi, safi_t safi, struct prefix *p, } /* Lookup nexthop. */ - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if (static_nexthop_same(nexthop, si)) break; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 582ff3110b..9fe3c707bb 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -743,7 +743,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, tm->tm_hour); vty_out(vty, " ago\n"); - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { char addrstr[32]; vty_out(vty, " %c%s", @@ -922,7 +922,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_string_add(json_route, "uptime", buf); - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { json_nexthop = json_object_new_object(); if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) @@ -1088,8 +1088,8 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, } /* Nexthop information. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { - if (nexthop == re->nexthop) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { + if (nexthop == re->ng.nexthop) { /* Prefix information. */ len = vty_out(vty, "%c", zebra_route_char(re->type)); if (re->instance) @@ -1842,7 +1842,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, * In case of ECMP, count only once. */ cnt = 0; - for (nexthop = re->nexthop; (!cnt && nexthop); + for (nexthop = re->ng.nexthop; (!cnt && nexthop); nexthop = nexthop->next) { cnt++; rib_cnt[ZEBRA_ROUTE_TOTAL]++; diff --git a/zebra/zserv.c b/zebra/zserv.c index 8275ee1a3c..1a2ad7f8b4 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -60,6 +60,7 @@ #include "zebra/label_manager.h" #include "zebra/zebra_vxlan.h" #include "zebra/rt.h" +#include "zebra/zebra_pbr.h" /* Event list of zebra. */ enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; @@ -616,7 +617,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = re->nexthop_active_num; } - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; @@ -978,7 +979,7 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client, * we * are looking up. Therefore, we will just iterate over the top * chain of nexthops. */ - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) num += zsend_write_nexthop(s, nexthop); @@ -1042,6 +1043,44 @@ int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, return zebra_server_send_message(client); } +void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, + enum zapi_rule_notify_owner note) +{ + struct listnode *node; + struct zserv *client; + struct stream *s; + + if (IS_ZEBRA_DEBUG_PACKET) { + zlog_debug("%s: Notifying %u", + __PRETTY_FUNCTION__, rule->unique); + } + + for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) { + if (rule->sock == client->sock) + break; + } + + if (!client) + return; + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_RULE_NOTIFY_OWNER, VRF_DEFAULT); + stream_put(s, ¬e, sizeof(note)); + stream_putl(s, rule->seq); + stream_putl(s, rule->priority); + stream_putl(s, rule->unique); + if (rule->ifp) + stream_putl(s, rule->ifp->ifindex); + else + stream_putl(s, 0); + + stream_putw_at(s, 0, stream_get_endp(s)); + + zebra_server_send_message(client); +} + /* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ int zsend_router_id_update(struct zserv *client, struct prefix *p, vrf_id_t vrf_id) @@ -1164,7 +1203,10 @@ static int zread_route_add(struct zserv *client, u_short length, re->flags = api.flags; re->uptime = time(NULL); re->vrf_id = vrf_id; - re->table = zvrf->table_id; + if (api.tableid && vrf_id == VRF_DEFAULT) + re->table = api.tableid; + else + re->table = zvrf->table_id; if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { for (i = 0; i < api.nexthop_num; i++) { @@ -1233,7 +1275,7 @@ static int zread_route_add(struct zserv *client, u_short length, zlog_warn( "%s: Nexthops Specified: %d but we failed to properly create one", __PRETTY_FUNCTION__, api.nexthop_num); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1265,7 +1307,7 @@ static int zread_route_add(struct zserv *client, u_short length, if (afi != AFI_IP6 && CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) { zlog_warn("%s: Received SRC Prefix but afi is not v6", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1434,7 +1476,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, zlog_warn( "%s: Please use ZEBRA_ROUTE_ADD if you want to pass v6 nexthops", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; break; @@ -1445,7 +1487,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, zlog_warn( "%s: Specified nexthop type: %d does not exist", __PRETTY_FUNCTION__, nexthop_type); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1485,7 +1527,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, return 0; stream_failure: - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1651,7 +1693,7 @@ static int zread_ipv4_route_ipv6_nexthop_add(struct zserv *client, zlog_warn( "%s: Please use ZEBRA_ROUTE_ADD if you want to pass non v6 nexthops", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1714,7 +1756,7 @@ static int zread_ipv4_route_ipv6_nexthop_add(struct zserv *client, return 0; stream_failure: - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1847,7 +1889,7 @@ static int zread_ipv6_add(struct zserv *client, u_short length, zlog_warn( "%s: Please use ZEBRA_ROUTE_ADD if you want to pass non v6 nexthops", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1908,7 +1950,7 @@ static int zread_ipv6_add(struct zserv *client, u_short length, return 0; stream_failure: - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; @@ -2584,6 +2626,65 @@ stream_failure: return; } +static inline void zread_rule(uint16_t command, struct zserv *client, + uint16_t length, struct zebra_vrf *zvrf) +{ + struct zebra_pbr_rule zpr; + struct stream *s; + uint32_t total, i; + ifindex_t ifindex; + + s = client->ibuf; + STREAM_GETL(s, total); + + for (i = 0; i < total; i++) { + memset(&zpr, 0, sizeof(zpr)); + + zpr.sock = client->sock; + STREAM_GETL(s, zpr.seq); + STREAM_GETL(s, zpr.priority); + STREAM_GETL(s, zpr.unique); + STREAM_GETC(s, zpr.filter.src_ip.family); + STREAM_GETC(s, zpr.filter.src_ip.prefixlen); + STREAM_GET(&zpr.filter.src_ip.u.prefix, s, + prefix_blen(&zpr.filter.src_ip)); + STREAM_GETW(s, zpr.filter.src_port); + STREAM_GETC(s, zpr.filter.dst_ip.family); + STREAM_GETC(s, zpr.filter.dst_ip.prefixlen); + STREAM_GET(&zpr.filter.dst_ip.u.prefix, s, + prefix_blen(&zpr.filter.dst_ip)); + STREAM_GETW(s, zpr.filter.dst_port); + STREAM_GETL(s, zpr.action.table); + STREAM_GETL(s, ifindex); + + zpr.ifp = if_lookup_by_index(ifindex, VRF_UNKNOWN); + if (!zpr.ifp) { + zlog_debug("FAiled to lookup ifindex: %u", ifindex); + return; + } + + if (!is_default_prefix(&zpr.filter.src_ip)) + zpr.filter.filter_bm |= PBR_FILTER_SRC_IP; + + if (!is_default_prefix(&zpr.filter.dst_ip)) + zpr.filter.filter_bm |= PBR_FILTER_DST_IP; + + if (zpr.filter.src_port) + zpr.filter.filter_bm |= PBR_FILTER_SRC_PORT; + + if (zpr.filter.dst_port) + zpr.filter.filter_bm |= PBR_FILTER_DST_PORT; + + if (command == ZEBRA_RULE_ADD) + zebra_pbr_add_rule(zvrf->zns, &zpr); + else + zebra_pbr_del_rule(zvrf->zns, &zpr); + } + +stream_failure: + return; +} + static inline void zserv_handle_commands(struct zserv *client, uint16_t command, uint16_t length, struct zebra_vrf *zvrf) @@ -2728,6 +2829,10 @@ static inline void zserv_handle_commands(struct zserv *client, uint16_t command, case ZEBRA_PW_UNSET: zread_pseudowire(command, client, length, zvrf); break; + case ZEBRA_RULE_ADD: + case ZEBRA_RULE_DELETE: + zread_rule(command, client, length, zvrf); + break; default: zlog_info("Zebra received unknown command %d", command); break; diff --git a/zebra/zserv.h b/zebra/zserv.h index 4f0f5b0461..8519693726 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -31,6 +31,7 @@ #include "zebra/zebra_ns.h" #include "zebra/zebra_pw.h" +//#include "zebra/zebra_pbr.h" /* Default port information. */ #define ZEBRA_VTY_PORT 2601 @@ -178,6 +179,10 @@ extern int zsend_pw_update(struct zserv *, struct zebra_pw *); extern int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, enum zapi_route_notify_owner note); +struct zebra_pbr_rule; +extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, + enum zapi_rule_notify_owner note); + extern void zserv_nexthop_num_warn(const char *, const struct prefix *, const unsigned int); extern int zebra_server_send_message(struct zserv *client);