summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/bfd.c3
-rw-r--r--lib/command.c5
-rw-r--r--lib/elf_py.c120
-rw-r--r--lib/if.c23
-rw-r--r--lib/log.c10
-rw-r--r--lib/routemap.c26
-rw-r--r--lib/routemap.h3
-rw-r--r--lib/routemap_cli.c71
-rw-r--r--lib/routemap_northbound.c31
-rw-r--r--lib/zclient.c71
-rw-r--r--lib/zclient.h33
11 files changed, 342 insertions, 54 deletions
diff --git a/lib/bfd.c b/lib/bfd.c
index 176269cc5b..276d6c685e 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -988,6 +988,9 @@ void bfd_sess_show(struct vty *vty, struct json_object *json,
json_object *json_bfd = NULL;
char time_buf[64];
+ if (!bsp)
+ return;
+
/* Show type. */
if (json) {
json_bfd = json_object_new_object();
diff --git a/lib/command.c b/lib/command.c
index 770e2fc5ac..d2798b5002 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -2226,18 +2226,19 @@ DEFUN (no_banner_motd,
DEFUN(find,
find_cmd,
- "find REGEX",
+ "find REGEX...",
"Find CLI command matching a regular expression\n"
"Search pattern (POSIX regex)\n")
{
- char *pattern = argv[1]->arg;
const struct cmd_node *node;
const struct cmd_element *cli;
vector clis;
regex_t exp = {};
+ char *pattern = argv_concat(argv, argc, 1);
int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
+ XFREE(MTYPE_TMP, pattern);
if (cr != 0) {
switch (cr) {
diff --git a/lib/elf_py.c b/lib/elf_py.c
index d26e443b82..b47aa3d795 100644
--- a/lib/elf_py.c
+++ b/lib/elf_py.c
@@ -1032,7 +1032,7 @@ static char *elfdata_strptr(Elf_Data *data, size_t offset)
static void elffile_add_dynreloc(struct elffile *w, Elf_Data *reldata,
size_t entries, Elf_Data *symdata,
- Elf_Data *strdata)
+ Elf_Data *strdata, Elf_Type typ)
{
size_t i;
@@ -1041,12 +1041,59 @@ static void elffile_add_dynreloc(struct elffile *w, Elf_Data *reldata,
size_t symidx;
GElf_Rela *rela;
GElf_Sym *sym;
+ GElf_Addr rel_offs = 0;
relw = (struct elfreloc *)typeobj_elfreloc.tp_alloc(
&typeobj_elfreloc, 0);
relw->ef = w;
- rela = relw->rela = gelf_getrela(reldata, i, &relw->_rela);
+ if (typ == ELF_T_REL) {
+ GElf_Rel _rel, *rel;
+ GElf_Addr offs;
+
+ rel = gelf_getrel(reldata, i, &_rel);
+ relw->rela = &relw->_rela;
+ relw->rela->r_offset = rel->r_offset;
+ relw->rela->r_info = rel->r_info;
+ relw->rela->r_addend = 0;
+ relw->relative = true;
+
+ /* REL uses the pointer contents itself instead of the
+ * RELA addend field :( ... theoretically this could
+ * be some weird platform specific encoding, but since
+ * we only care about data relocations it should
+ * always be a pointer...
+ */
+ if (elffile_virt2file(w, rel->r_offset, &offs)) {
+ Elf_Data *ptr, *conv;
+ GElf_Addr tmp;
+ Elf_Data mem = {
+ .d_buf = (void *)&tmp,
+ .d_type = ELF_T_ADDR,
+ .d_version = EV_CURRENT,
+ .d_size = sizeof(tmp),
+ .d_off = 0,
+ .d_align = 0,
+ };
+
+ ptr = elf_getdata_rawchunk(w->elf, offs,
+ w->elfclass / 8,
+ ELF_T_ADDR);
+
+ conv = gelf_xlatetom(w->elf, &mem, ptr,
+ w->mmap[EI_DATA]);
+ if (conv) {
+ memcpy(&rel_offs, conv->d_buf,
+ conv->d_size);
+
+ relw->relative = false;
+ relw->rela->r_addend = rel_offs;
+ }
+ }
+ } else
+ relw->rela = gelf_getrela(reldata, i, &relw->_rela);
+
+ rela = relw->rela;
symidx = relw->symidx = GELF_R_SYM(rela->r_info);
sym = relw->sym = gelf_getsym(symdata, symidx, &relw->_sym);
if (sym) {
@@ -1062,9 +1109,16 @@ static void elffile_add_dynreloc(struct elffile *w, Elf_Data *reldata,
relw->st_value = 0;
}
- debugf("dynreloc @ %016llx sym %5llu %016llx %s\n",
- (long long)rela->r_offset, (unsigned long long)symidx,
- (long long)rela->r_addend, relw->symname);
+ if (typ == ELF_T_RELA)
+ debugf("dynrela @ %016llx sym %5llu %016llx %s\n",
+ (long long)rela->r_offset,
+ (unsigned long long)symidx,
+ (long long)rela->r_addend, relw->symname);
+ else
+ debugf("dynrel @ %016llx sym %5llu (%016llx) %s\n",
+ (long long)rela->r_offset,
+ (unsigned long long)symidx,
+ (unsigned long long)rel_offs, relw->symname);
elfrelocs_add(&w->dynrelocs, relw);
}
@@ -1166,8 +1220,10 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args,
Elf_Data *dyndata = elf_getdata_rawchunk(w->elf,
phdr->p_offset, phdr->p_filesz, ELF_T_DYN);
- GElf_Addr dynrela = 0, symtab = 0, strtab = 0;
- size_t dynrelasz = 0, dynrelaent = 0, strsz = 0;
+ GElf_Addr dynrela = 0, dynrel = 0, symtab = 0, strtab = 0;
+ size_t dynrelasz = 0, dynrelaent = 0;
+ size_t dynrelsz = 0, dynrelent = 0;
+ size_t strsz = 0;
GElf_Dyn _dyn, *dyn;
for (size_t j = 0;; j++) {
@@ -1198,16 +1254,20 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args,
dynrelaent = dyn->d_un.d_val;
break;
+ case DT_REL:
+ dynrel = dyn->d_un.d_ptr;
+ break;
case DT_RELSZ:
- if (dyn->d_un.d_val)
- fprintf(stderr,
- "WARNING: ignoring non-empty DT_REL!\n");
+ dynrelsz = dyn->d_un.d_val;
+ break;
+ case DT_RELENT:
+ dynrelent = dyn->d_un.d_val;
break;
}
}
GElf_Addr offset;
- Elf_Data *symdata = NULL, *strdata = NULL, *reladata = NULL;
+ Elf_Data *symdata = NULL, *strdata = NULL;
if (elffile_virt2file(w, symtab, &offset))
symdata = elf_getdata_rawchunk(w->elf, offset,
@@ -1217,19 +1277,37 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args,
strdata = elf_getdata_rawchunk(w->elf, offset,
strsz, ELF_T_BYTE);
- if (!dynrela || !dynrelasz || !dynrelaent)
- continue;
+ size_t c;
- if (!elffile_virt2file(w, dynrela, &offset))
- continue;
+ if (dynrela && dynrelasz && dynrelaent
+ && elffile_virt2file(w, dynrela, &offset)) {
+ Elf_Data *reladata = NULL;
- debugf("dynrela @%llx/%llx+%llx\n", (long long)dynrela,
- (long long)offset, (long long)dynrelasz);
+ debugf("dynrela @%llx/%llx+%llx\n", (long long)dynrela,
+ (long long)offset, (long long)dynrelasz);
- reladata = elf_getdata_rawchunk(w->elf, offset, dynrelasz,
- ELF_T_RELA);
- elffile_add_dynreloc(w, reladata, dynrelasz / dynrelaent,
- symdata, strdata);
+ reladata = elf_getdata_rawchunk(w->elf, offset,
+ dynrelasz, ELF_T_RELA);
+
+ c = dynrelasz / dynrelaent;
+ elffile_add_dynreloc(w, reladata, c, symdata, strdata,
+ ELF_T_RELA);
+ }
+
+ if (dynrel && dynrelsz && dynrelent
+ && elffile_virt2file(w, dynrel, &offset)) {
+ Elf_Data *reldata = NULL;
+
+ debugf("dynrel @%llx/%llx+%llx\n", (long long)dynrel,
+ (long long)offset, (long long)dynrelsz);
+
+ reldata = elf_getdata_rawchunk(w->elf, offset, dynrelsz,
+ ELF_T_REL);
+
+ c = dynrelsz / dynrelent;
+ elffile_add_dynreloc(w, reldata, c, symdata, strdata,
+ ELF_T_REL);
+ }
}
#endif
diff --git a/lib/if.c b/lib/if.c
index 629ef4e708..f8a693d8f3 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -1056,15 +1056,30 @@ struct connected *connected_get_linklocal(struct interface *ifp)
void if_terminate(struct vrf *vrf)
{
struct interface *ifp;
+ bool delete;
+
+ /*
+ * If the default VRF is being terminated or has
+ * already been terminated it means that
+ * the program is shutting down and we need to
+ * delete all the interfaces. Otherwise, we only
+ * need to move VRF's interfaces to the default VRF.
+ */
+ delete = vrf_is_backend_netns() || vrf->vrf_id == VRF_DEFAULT
+ || !vrf_lookup_by_id(VRF_DEFAULT);
while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name);
- if (ifp->node) {
- ifp->node->info = NULL;
- route_unlock_node(ifp->node);
+ if (delete) {
+ if (ifp->node) {
+ ifp->node->info = NULL;
+ route_unlock_node(ifp->node);
+ }
+ if_delete(&ifp);
+ } else {
+ if_update_to_new_vrf(ifp, VRF_DEFAULT);
}
- if_delete(&ifp);
}
}
diff --git a/lib/log.c b/lib/log.c
index e078d8e2a7..ca2f501686 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -464,7 +464,15 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST),
DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY),
DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_ADD),
- DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_DEL)};
+ DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_DEL),
+ DESC_ENTRY(ZEBRA_NHRP_NEIGH_ADDED),
+ DESC_ENTRY(ZEBRA_NHRP_NEIGH_REMOVED),
+ DESC_ENTRY(ZEBRA_NHRP_NEIGH_GET),
+ DESC_ENTRY(ZEBRA_NHRP_NEIGH_REGISTER),
+ DESC_ENTRY(ZEBRA_NHRP_NEIGH_UNREGISTER),
+ DESC_ENTRY(ZEBRA_NEIGH_IP_ADD),
+ DESC_ENTRY(ZEBRA_NEIGH_IP_DEL),
+ DESC_ENTRY(ZEBRA_CONFIGURE_ARP)};
#undef DESC_ENTRY
static const struct zebra_desc_table unknown = {0, "unknown", '?'};
diff --git a/lib/routemap.c b/lib/routemap.c
index 33c65ac333..b2cb299fd3 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -2906,29 +2906,6 @@ void route_map_notify_dependencies(const char *affected_name,
}
/* VTY related functions. */
-DEFUN(no_routemap_optimization, no_routemap_optimization_cmd,
- "no route-map optimization",
- NO_STR
- "route-map\n"
- "optimization\n")
-{
- VTY_DECLVAR_CONTEXT(route_map_index, index);
-
- index->map->optimization_disabled = true;
- return CMD_SUCCESS;
-}
-
-DEFUN(routemap_optimization, routemap_optimization_cmd,
- "route-map optimization",
- "route-map\n"
- "optimization\n")
-{
- VTY_DECLVAR_CONTEXT(route_map_index, index);
-
- index->map->optimization_disabled = false;
- return CMD_SUCCESS;
-}
-
static void clear_route_map_helper(struct route_map *map)
{
struct route_map_index *index;
@@ -3241,8 +3218,5 @@ void route_map_init(void)
install_element(ENABLE_NODE, &debug_rmap_cmd);
install_element(ENABLE_NODE, &no_debug_rmap_cmd);
- install_element(RMAP_NODE, &routemap_optimization_cmd);
- install_element(RMAP_NODE, &no_routemap_optimization_cmd);
-
install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd);
}
diff --git a/lib/routemap.h b/lib/routemap.h
index 6385193bbf..5b6b64eaeb 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -902,6 +902,9 @@ extern void route_map_call_show(struct vty *vty, struct lyd_node *dnode,
extern void route_map_description_show(struct vty *vty,
struct lyd_node *dnode,
bool show_defaults);
+extern void route_map_optimization_disabled_show(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
extern void route_map_cli_init(void);
#ifdef __cplusplus
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 9a53c11a4c..e11b9eea74 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -1396,6 +1396,74 @@ void route_map_description_show(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL));
}
+DEFPY_YANG(
+ route_map_optimization, route_map_optimization_cmd,
+ "[no] route-map WORD$name optimization",
+ NO_STR
+ ROUTE_MAP_CMD_STR
+ "Configure route-map optimization\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-route-map:lib/route-map[name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-route-map:lib/route-map[name='%s']/optimization-disabled",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, no ? "true" : "false");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void route_map_optimization_disabled_show(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *name = yang_dnode_get_string(dnode, "../name");
+ const bool disabled = yang_dnode_get_bool(dnode, NULL);
+
+ vty_out(vty, "%sroute-map %s optimization\n", disabled ? "no " : "",
+ name);
+}
+
+#if CONFDATE > 20220409
+CPP_NOTICE("Time to remove old route-map optimization command")
+#endif
+
+DEFPY_HIDDEN(
+ routemap_optimization, routemap_optimization_cmd,
+ "[no] route-map optimization",
+ NO_STR
+ "route-map\n"
+ "optimization\n")
+{
+ const struct lyd_node *rmi_dnode;
+ const char *rm_name;
+ char xpath[XPATH_MAXLEN];
+
+ vty_out(vty,
+ "%% This command is deprecated. Please, use `route-map NAME optimization` from the config node.\n");
+
+ rmi_dnode =
+ yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
+ if (!rmi_dnode) {
+ vty_out(vty, "%% Failed to get RMI dnode in candidate DB\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rm_name = yang_dnode_get_string(rmi_dnode, "../name");
+
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-route-map:lib/route-map[name='%s']/optimization-disabled",
+ rm_name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, no ? "true" : "false");
+ return nb_cli_apply_changes(vty, NULL);
+}
+
static int route_map_config_write(struct vty *vty)
{
struct lyd_node *dnode;
@@ -1447,6 +1515,7 @@ void route_map_cli_init(void)
install_element(CONFIG_NODE, &route_map_cmd);
install_element(CONFIG_NODE, &no_route_map_cmd);
install_element(CONFIG_NODE, &no_route_map_all_cmd);
+ install_element(CONFIG_NODE, &route_map_optimization_cmd);
/* Install the on-match stuff */
install_element(RMAP_NODE, &rmap_onmatch_next_cmd);
@@ -1513,4 +1582,6 @@ void route_map_cli_init(void)
install_element(RMAP_NODE, &set_srte_color_cmd);
install_element(RMAP_NODE, &no_set_srte_color_cmd);
+
+ install_element(RMAP_NODE, &routemap_optimization_cmd);
}
diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
index 410eb51f5e..db06e9caac 100644
--- a/lib/routemap_northbound.c
+++ b/lib/routemap_northbound.c
@@ -141,6 +141,30 @@ static int lib_route_map_destroy(struct nb_cb_destroy_args *args)
}
/*
+ * XPath: /frr-route-map:lib/route-map/optimization-disabled
+ */
+static int
+lib_route_map_optimization_disabled_modify(struct nb_cb_modify_args *args)
+{
+ struct route_map *rm;
+ bool disabled = yang_dnode_get_bool(args->dnode, NULL);
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ rm = nb_running_get_entry(args->dnode, NULL, true);
+ rm->optimization_disabled = disabled;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-route-map:lib/route-map/entry
*/
static int lib_route_map_entry_create(struct nb_cb_create_args *args)
@@ -1198,6 +1222,13 @@ const struct frr_yang_module_info frr_route_map_info = {
}
},
{
+ .xpath = "/frr-route-map:lib/route-map/optimization-disabled",
+ .cbs = {
+ .modify = lib_route_map_optimization_disabled_modify,
+ .cli_show = route_map_optimization_disabled_show,
+ }
+ },
+ {
.xpath = "/frr-route-map:lib/route-map/entry",
.cbs = {
.create = lib_route_map_entry_create,
diff --git a/lib/zclient.c b/lib/zclient.c
index c78937c1ec..d613906d82 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -3916,6 +3916,21 @@ static int zclient_read(struct thread *thread)
(*zclient->zebra_client_close_notify)(command, zclient,
length, vrf_id);
break;
+ case ZEBRA_NHRP_NEIGH_ADDED:
+ if (zclient->neighbor_added)
+ (*zclient->neighbor_added)(command, zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_NHRP_NEIGH_REMOVED:
+ if (zclient->neighbor_removed)
+ (*zclient->neighbor_removed)(command, zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_NHRP_NEIGH_GET:
+ if (zclient->neighbor_get)
+ (*zclient->neighbor_get)(command, zclient, length,
+ vrf_id);
+ break;
default:
break;
}
@@ -4181,3 +4196,59 @@ char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, size_t len)
return buf;
}
+
+static int zclient_neigh_ip_read_entry(struct stream *s, struct ipaddr *add)
+{
+ uint8_t family;
+
+ STREAM_GETC(s, family);
+ if (family != AF_INET && family != AF_INET6)
+ return -1;
+
+ STREAM_GET(&add->ip.addr, s, family2addrsize(family));
+ add->ipa_type = family;
+ return 0;
+ stream_failure:
+ return -1;
+}
+
+int zclient_neigh_ip_encode(struct stream *s,
+ uint16_t cmd,
+ union sockunion *in,
+ union sockunion *out,
+ struct interface *ifp)
+{
+ int ret = 0;
+
+ zclient_create_header(s, cmd, ifp->vrf_id);
+ stream_putc(s, sockunion_family(in));
+ stream_write(s, sockunion_get_addr(in), sockunion_get_addrlen(in));
+ if (out && sockunion_family(out) != AF_UNSPEC) {
+ stream_putc(s, sockunion_family(out));
+ stream_write(s, sockunion_get_addr(out),
+ sockunion_get_addrlen(out));
+ } else
+ stream_putc(s, AF_UNSPEC);
+ stream_putl(s, ifp->ifindex);
+ if (out)
+ stream_putl(s, ZEBRA_NEIGH_STATE_REACHABLE);
+ else
+ stream_putl(s, ZEBRA_NEIGH_STATE_FAILED);
+ return ret;
+}
+
+int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api)
+{
+ int ret;
+
+ ret = zclient_neigh_ip_read_entry(s, &api->ip_in);
+ if (ret < 0)
+ return -1;
+ zclient_neigh_ip_read_entry(s, &api->ip_out);
+
+ STREAM_GETL(s, api->index);
+ STREAM_GETL(s, api->ndm_state);
+ return 0;
+ stream_failure:
+ return -1;
+}
diff --git a/lib/zclient.h b/lib/zclient.h
index bd952ea1e6..90240e40b2 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -23,6 +23,7 @@
/* For struct zapi_route. */
#include "prefix.h"
+#include "ipaddr.h"
/* For struct interface and struct connected. */
#include "if.h"
@@ -223,6 +224,14 @@ typedef enum {
ZEBRA_NEIGH_DISCOVER,
ZEBRA_ROUTE_NOTIFY_REQUEST,
ZEBRA_CLIENT_CLOSE_NOTIFY,
+ ZEBRA_NHRP_NEIGH_ADDED,
+ ZEBRA_NHRP_NEIGH_REMOVED,
+ ZEBRA_NHRP_NEIGH_GET,
+ ZEBRA_NHRP_NEIGH_REGISTER,
+ ZEBRA_NHRP_NEIGH_UNREGISTER,
+ ZEBRA_NEIGH_IP_ADD,
+ ZEBRA_NEIGH_IP_DEL,
+ ZEBRA_CONFIGURE_ARP,
} zebra_message_types_t;
enum zebra_error_types {
@@ -381,6 +390,9 @@ struct zclient {
int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS);
int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS);
int (*zebra_client_close_notify)(ZAPI_CALLBACK_ARGS);
+ void (*neighbor_added)(ZAPI_CALLBACK_ARGS);
+ void (*neighbor_removed)(ZAPI_CALLBACK_ARGS);
+ void (*neighbor_get)(ZAPI_CALLBACK_ARGS);
};
/* Zebra API message flag. */
@@ -794,6 +806,27 @@ struct zclient_options {
extern struct zclient_options zclient_options_default;
+/* link layer representation for GRE like interfaces
+ * ip_in is the underlay IP, ip_out is the tunnel dest
+ * index stands for the index of the interface
+ * ndm state stands for the NDM value in netlink
+ */
+#define ZEBRA_NEIGH_STATE_REACHABLE (0x02)
+#define ZEBRA_NEIGH_STATE_FAILED (0x20)
+struct zapi_neigh_ip {
+ int cmd;
+ struct ipaddr ip_in;
+ struct ipaddr ip_out;
+ ifindex_t index;
+ uint32_t ndm_state;
+};
+int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api);
+int zclient_neigh_ip_encode(struct stream *s,
+ uint16_t cmd,
+ union sockunion *in,
+ union sockunion *out,
+ struct interface *ifp);
+
/*
* We reserve the top 4 bits for l2-NHG, everything else
* is for zebra/proto l3-NHG.