diff options
author | Jeff Shaw <jeffrey.b.shaw@intel.com> | 2016-09-21 19:12:46 -0400 |
---|---|---|
committer | Jeff Shaw <jeffrey.b.shaw@intel.com> | 2016-10-04 15:58:00 -0400 |
commit | dfae7756baf895957a43944f63bfe0c850b16467 (patch) | |
tree | cb8b0c5ae0a09835f9817a8956bd86dbf9a1de57 /router/router/router.c | |
parent | 961580e47e58e9cb7175ec89703bc951c7ce71b2 (diff) |
[router] IPv6 support and refactoring.
This change adds support for IPv6 while refactoring most of the
original plugin code in the following ways.
- Adhere to vpp style guidelines.
- Split the netlink, node, and tap processing into separate
files named with a "tap_inject" prefix which more accurately
represents the functionality.
- Implement our own tap management and rx/tx. This is to reduce
the overhead of passing packets in and out of vnet tap
devices, in favor of directly reading/writing from the tap.
- Change how nodes work. Now we have neighbor, rx, and tx nodes.
The neighbor node sends ARP replies and ICMP6 neighbor
advertisements to the arp-input and icmp6-neighbor-solicitation
nodes, respectively, before also injecting the packet to the
host, making it possible for both vpp and the host network stack
to resolve the next hop. The tx node injects packets into the
host by writing to the tap. The rx node reads packets from the
tap and sends them on its associated data plane interface.
- Simplify the CLI. Instead of creating taps specifically for
a given interface we create a tap for all of the Ethernet
interfaces with the "enable tap-inject" CLI command. The
interfaces are named with a "vpp" prefix, i.e. "vpp0". Also
add a "disable tap-inject" option.
- Provide ability to enable at configuration time with the
tap-inject { enable } stanza.
Change-Id: I6b56da606e2da1d793ce6aca222fe4eb5a4e070d
Signed-off-by: Jeff Shaw <jeffrey.b.shaw@intel.com>
Diffstat (limited to 'router/router/router.c')
-rw-r--r-- | router/router/router.c | 691 |
1 files changed, 0 insertions, 691 deletions
diff --git a/router/router/router.c b/router/router/router.c deleted file mode 100644 index 741f34c..0000000 --- a/router/router/router.c +++ /dev/null @@ -1,691 +0,0 @@ -/* - * Copyright 2016 Intel Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include <net/ethernet.h> -#include <net/if_arp.h> -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <vnet/vnet.h> -#include <vnet/plugin/plugin.h> -#include <vnet/ip/ip.h> -#include <vnet/unix/tuntap.h> -#include <librtnl/mapper.h> -#include <vnet/ethernet/arp_packet.h> -#include <vlibmemory/api.h> - -enum { - NEXT_UNTAPPED = 0, - NEXT_INJECT, -}; - -enum { - ERROR_INJECT_ARP, - ERROR_INJECT_ICMP, - ERROR_INJECT_CLASSIFIED, -}; - -static char *error_strings[] = { - [ERROR_INJECT_ARP] = "Inject ARP", - [ERROR_INJECT_ICMP] = "Inject ICMP", - [ERROR_INJECT_CLASSIFIED] = "Inject Classified", -}; - -struct tap_to_iface { - u32 tap; - u32 iface; -}; - -struct router_main { - vnet_main_t *vnet_main; - u32 *iface_to_tap; - u32 *iface_to_protos; - struct tap_to_iface *tap_to_iface; - u32 ns_index; -}; - -static struct router_main rm; - -enum { - PROTO_ARP = 0, - PROTO_ICMP4, - PROTO_IGMP4, - PROTO_OSPF2, - PROTO_TCP, - PROTO_UDP, - PROTO_N_TOTAL, -}; - -enum { - PROTO_BIT_ARP = 1 << PROTO_ARP, - PROTO_BIT_ICMP4 = 1 << PROTO_ICMP4, - PROTO_BIT_IGMP4 = 1 << PROTO_IGMP4, - PROTO_BIT_OSPF2 = 1 << PROTO_OSPF2, - PROTO_BIT_TCP = 1 << PROTO_TCP, - PROTO_BIT_UDP = 1 << PROTO_UDP, -}; - -static char *proto_strings[PROTO_N_TOTAL] = { - [PROTO_ARP] = "arp", - [PROTO_ICMP4] = "icmp4", - [PROTO_IGMP4] = "igmp4", - [PROTO_OSPF2] = "ospf2", - [PROTO_TCP] = "tcp", - [PROTO_UDP] = "udp", -}; - -static inline u32 parse_protos(char *proto_string) -{ - u32 protos = 0; - char *tok, **proto; - - for (tok = strtok(proto_string, ","); tok; tok = strtok(NULL, ",")) - for (proto = proto_strings; proto && *proto; ++proto) - if (!strncmp(tok, *proto, 16)) - protos |= 1 << (proto - proto_strings); - return protos; -} - -static uword unformat_protos(unformat_input_t *input, va_list *args) -{ - u32 *protos = va_arg(*args, u32 *); - u8 *proto_string; - - if (unformat(input, "%s", &proto_string)) - *protos = parse_protos((char *)proto_string); - return 1; -} - -vlib_node_registration_t tap_inject_arp_node; -vlib_node_registration_t tap_inject_icmp_node; -vlib_node_registration_t tap_inject_classified_node; - -static inline void -update_arp_entry(vlib_buffer_t *b0, ethernet_arp_header_t *arp, u32 vlib_rx) -{ - ethernet_header_t *eth; - ip4_address_t *if_addr; - ip_interface_address_t *ifa; - - if (arp->l2_type != ntohs(ETHERNET_ARP_HARDWARE_TYPE_ethernet) || - arp->l3_type != ntohs(ETHERNET_TYPE_IP4)) - return; - - /* Check that IP address is local and matches incoming interface. */ - if_addr = ip4_interface_address_matching_destination(&ip4_main, - &arp->ip4_over_ethernet[1].ip4, - vlib_rx, &ifa); - if (!if_addr) - return; - - /* Source must also be local to subnet of matching interface address. */ - if (!ip4_destination_matches_interface(&ip4_main, - &arp->ip4_over_ethernet[0].ip4, ifa)) - return; - - /* Reject replies with our local interface address. */ - if (if_addr->as_u32 == arp->ip4_over_ethernet[0].ip4.as_u32) - return; - - if (if_addr->as_u32 != arp->ip4_over_ethernet[1].ip4.as_u32) - return; - - eth = ethernet_buffer_get_header(b0); - - /* Trash ARP packets whose ARP-level source addresses do not - * match their L2-frame-level source addresses */ - if (memcmp(eth->src_address, arp->ip4_over_ethernet[0].ethernet, - sizeof(eth->src_address))) - return; - - if (arp->ip4_over_ethernet[0].ip4.as_u32 == 0 || - (arp->ip4_over_ethernet[0].ip4.as_u32 == - arp->ip4_over_ethernet[1].ip4.as_u32)) - return; - - /* Learn or update sender's mapping only for requests or unicasts - * that don't match local interface address. */ - if (ethernet_address_cast(eth->dst_address) != ETHERNET_ADDRESS_UNICAST) - return; - - vnet_arp_set_ip4_over_ethernet(rm.vnet_main, vlib_rx, ~0, - &arp->ip4_over_ethernet[0], 0); -} - -static uword -tap_inject_func(vlib_main_t *m, vlib_node_runtime_t *node, vlib_frame_t *f, - int mode) -{ - u32 n_left_from = f->n_vectors; - u32 *from = vlib_frame_vector_args(f); - u32 next_index = node->cached_next_index; - u32 *to_next; - u32 counter, count = 0; - - while (n_left_from) { - vlib_buffer_t *b0; - u32 next0, bi0, n_left; - u32 vlib_rx, vlib_tx; - u32 protos, proto_bit = 0; - - vlib_get_next_frame(m, node, next_index, to_next, n_left); - - *(to_next++) = bi0 = *(from++); - --n_left_from; - --n_left; - - b0 = vlib_get_buffer(m, bi0); - - vlib_rx = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - vlib_tx = rm.iface_to_tap[vlib_rx]; - protos = rm.iface_to_protos[vlib_rx]; - - next0 = NEXT_UNTAPPED; - - if (vlib_tx == 0 || vlib_tx == ~0 || protos == 0) - goto untapped; - - if (mode == ERROR_INJECT_CLASSIFIED) { - ip4_header_t *iphdr; - - iphdr = vlib_buffer_get_current(b0); - if (iphdr->protocol == IP_PROTOCOL_TCP) - proto_bit = PROTO_BIT_TCP; - else if (iphdr->protocol == IP_PROTOCOL_UDP) - proto_bit = PROTO_BIT_UDP; - else if (iphdr->protocol == IP_PROTOCOL_OSPF) - proto_bit = PROTO_BIT_OSPF2; - else if (iphdr->protocol == IP_PROTOCOL_IGMP) - proto_bit = PROTO_BIT_IGMP4; - } else if (mode == ERROR_INJECT_ARP) { - proto_bit = PROTO_BIT_ARP; - } else if (mode == ERROR_INJECT_ICMP) { - proto_bit = PROTO_BIT_ICMP4; - } - - if (!(protos & proto_bit)) - goto untapped; - - next0 = NEXT_INJECT; - - vnet_buffer(b0)->sw_if_index[VLIB_TX] = vlib_tx; - ++count; - - if (mode == ERROR_INJECT_ARP) { - ethernet_arp_header_t *arphdr; - - arphdr = vlib_buffer_get_current(b0); - if (arphdr->opcode == ntohs(ETHERNET_ARP_OPCODE_reply)) - update_arp_entry(b0, arphdr, vlib_rx); - } - - /* FIXME: What about VLAN? */ - b0->current_data -= sizeof(ethernet_header_t); - b0->current_length += sizeof(ethernet_header_t); - -untapped: - vlib_validate_buffer_enqueue_x1(m, node, next_index, to_next, - n_left, bi0, next0); - vlib_put_next_frame(m, node, next_index, n_left); - } - - switch (mode) { - case ERROR_INJECT_ARP: - counter = ERROR_INJECT_ARP; - break; - case ERROR_INJECT_ICMP: - counter = ERROR_INJECT_ICMP; - break; - default: - counter = ERROR_INJECT_CLASSIFIED; - } - - vlib_node_increment_counter(m, node->node_index, counter, count); - return f->n_vectors; -} - -static uword -tap_inject_arp(vlib_main_t *m, vlib_node_runtime_t *node, vlib_frame_t *f) -{ - return tap_inject_func(m, node, f, ERROR_INJECT_ARP); -} - -VLIB_REGISTER_NODE(tap_inject_arp_node) = { - .function = tap_inject_arp, - .name = "tap-inject-arp", - .vector_size = sizeof(u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(error_strings), - .error_strings = error_strings, - .n_next_nodes = 2, - .next_nodes = { - [NEXT_UNTAPPED] = "arp-input", - [NEXT_INJECT] = "interface-output", - }, -}; - -static uword -tap_inject_icmp(vlib_main_t *m, vlib_node_runtime_t *node, vlib_frame_t *f) -{ - return tap_inject_func(m, node, f, ERROR_INJECT_ICMP); -} - -VLIB_REGISTER_NODE(tap_inject_icmp_node) = { - .function = tap_inject_icmp, - .name = "tap-inject-icmp", - .vector_size = sizeof(u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(error_strings), - .error_strings = error_strings, - .n_next_nodes = 2, - .next_nodes = { - [NEXT_UNTAPPED] = "ip4-icmp-input", - [NEXT_INJECT] = "interface-output", - }, -}; - -static uword -tap_inject_classified(vlib_main_t *m, vlib_node_runtime_t *node, - vlib_frame_t *f) -{ - return tap_inject_func(m, node, f, ERROR_INJECT_CLASSIFIED); -} - -VLIB_REGISTER_NODE(tap_inject_classified_node) = { - .function = tap_inject_classified, - .name = "tap-inject-classified", - .vector_size = sizeof(u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(error_strings), - .error_strings = error_strings, - .n_next_nodes = 2, - .next_nodes = { - [NEXT_UNTAPPED] = "error-drop", - [NEXT_INJECT] = "interface-output", - }, -}; - -static int -set_tap_hwaddr(vlib_main_t *m, char *name, u8 *hwaddr) -{ - int fd, rc; - struct ifreq ifr; - - fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (fd < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, (char *)name, sizeof(ifr.ifr_name) - 1); - memcpy(ifr.ifr_hwaddr.sa_data, hwaddr, ETHER_ADDR_LEN); - ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; - rc = ioctl(fd, SIOCSIFHWADDR, &ifr) < 0 ? -1 : 0; - close(fd); - return rc; -} - -static int -set_tap_link_state(vlib_main_t *m, char *name, u16 flags) -{ - int fd, rc; - struct ifreq ifr; - - fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (fd < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, (char *)name, sizeof(ifr.ifr_name) - 1); - - rc = ioctl(fd, SIOCGIFFLAGS, &ifr); - if (rc < 0) - goto out; - - if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) - ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); - else - ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING); - - rc = ioctl(fd, SIOCSIFFLAGS, &ifr) < 0 ? -1 : 0; -out: - close(fd); - return rc; -} - -static clib_error_t * -do_tap_connect(vlib_main_t *m, char *name, u32 iface, u32 *tap) -{ - vnet_hw_interface_t *hw = vnet_get_hw_interface(rm.vnet_main, iface); - vnet_sw_interface_t *sw = vnet_get_sw_interface(rm.vnet_main, iface); - u64 hw_address = 0; - - *tap = ~0; - if (!hw) - return clib_error_return(0, "invalid interface"); - else if (hw->hw_address) - memcpy(&hw_address, hw->hw_address, 6); - - if (vnet_tap_connect(m, (u8 *)name, (u8 *)&hw_address, tap)) - return clib_error_return(0, "failed to connect tap"); - - if (set_tap_hwaddr(m, name, (u8 *)&hw_address)) - return clib_error_return(0, "failed to set tap hw address"); - - if (set_tap_link_state(m, name, sw->flags)) - return clib_error_return(0, "failed to set tap link state"); - - if (set_int_l2_mode(m, rm.vnet_main, MODE_L2_XC, *tap, 0, 0, 0, iface)) - return clib_error_return(0, "failed to xconnect to interface"); - - return vnet_sw_interface_set_flags(rm.vnet_main, *tap, - VNET_SW_INTERFACE_FLAG_ADMIN_UP); -} - -static void add_del_addr(ns_addr_t *a, int is_del) -{ - struct tap_to_iface *map = NULL; - u32 sw_if_index = ~0; - - vec_foreach(map, rm.tap_to_iface) { - if (a->ifaddr.ifa_index == map->tap) { - sw_if_index = map->iface; - break; - } - } - - if (sw_if_index == ~0) - return; - - ip4_add_del_interface_address(vlib_get_main(), - sw_if_index, (ip4_address_t *)a->local, - a->ifaddr.ifa_prefixlen, is_del); -} - -static void add_del_route(ns_route_t *r, int is_del) -{ - struct tap_to_iface *map = NULL; - u32 sw_if_index = ~0; - - vec_foreach(map, rm.tap_to_iface) { - if (r->oif == map->tap) { - sw_if_index = map->iface; - break; - } - } - - if (sw_if_index == ~0 || r->table != 254) - return; - - ip4_add_del_route_next_hop(&ip4_main, - is_del ? IP4_ROUTE_FLAG_DEL : IP4_ROUTE_FLAG_ADD, - (ip4_address_t *)r->dst, r->rtm.rtm_dst_len, - (ip4_address_t *)r->gateway, sw_if_index, 0, ~0, 0); -} - -struct set_flags_args { - u32 sw_if_index; - u8 flags; -}; - -static void set_interface_flags_callback(struct set_flags_args *a) -{ - vnet_sw_interface_set_flags(rm.vnet_main, a->sw_if_index, - a->flags); -} - -static void add_del_link(ns_link_t *l, int is_del) -{ - struct tap_to_iface *map = NULL; - u32 sw_if_index = ~0; - u8 flags = 0; - struct set_flags_args args; - vnet_sw_interface_t *sw = NULL; - - vec_foreach(map, rm.tap_to_iface) { - if (l->ifi.ifi_index == map->tap) { - sw_if_index = map->iface; - break; - } - } - - if (sw_if_index == ~0) - return; - - sw = vnet_get_sw_interface(rm.vnet_main, sw_if_index); - flags = sw->flags; - - if (l->ifi.ifi_flags & IFF_UP) - flags |= VNET_SW_INTERFACE_FLAG_ADMIN_UP; - else - flags &= ~VNET_SW_INTERFACE_FLAG_ADMIN_UP; - - args.sw_if_index = sw_if_index; - args.flags = flags; - - vl_api_rpc_call_main_thread(set_interface_flags_callback, - (u8 *) &args, sizeof(args)); -} - -static void -netns_notify_cb(void *obj, netns_type_t type, u32 flags, uword opaque) -{ - if (type == NETNS_TYPE_ADDR) - add_del_addr((ns_addr_t *)obj, flags & NETNS_F_DEL); - else if (type == NETNS_TYPE_ROUTE) - add_del_route((ns_route_t *)obj, flags & NETNS_F_DEL); - else if (type == NETNS_TYPE_LINK) - add_del_link((ns_link_t *)obj, flags & NETNS_F_DEL); -} - -static void insert_tap_to_iface(u32 tap, u32 iface) -{ - struct tap_to_iface map = { - .tap = tap, - .iface = iface, - }; - - vec_add1(rm.tap_to_iface, map); -} - - -static u32 ip4_next_index = ~0; - -static u32 -ip4_lookup_next_index(void) -{ - if (ip4_next_index == ~0) { - ip4_next_index = vlib_node_add_next(vlib_get_main(), - ip4_lookup_node.index, - tap_inject_classified_node.index); - } - - return ip4_next_index; -} - -static u32 ip4_multicast_arc_added; - -static void -add_ip4_multicast_arc(void) -{ - ip4_add_del_route_args_t a; - ip_adjacency_t add_adj; - - if (ip4_multicast_arc_added) - return; - - memset(&a, 0, sizeof(a)); - memset(&add_adj, 0, sizeof(add_adj)); - - a.add_adj = &add_adj; - a.n_add_adj = 1; - - a.flags = IP4_ROUTE_FLAG_TABLE_ID | IP4_ROUTE_FLAG_ADD; - a.table_index_or_table_id = 0; - a.dst_address.as_u32 = 0x000000E0; /* 224.0.0.0 */ - a.dst_address_length = 24; - a.adj_index = ~0; - - add_adj.explicit_fib_index = ~0; - add_adj.rewrite_header.node_index = ip4_rewrite_node.index; - add_adj.lookup_next_index = ip4_lookup_next_index(); - add_adj.if_address_index = ~0; - - ip4_add_del_route(&ip4_main, &a); - ip4_multicast_arc_added = 1; -} - -static clib_error_t * -tap_inject(vlib_main_t *m, unformat_input_t *input, vlib_cli_command_t *cmd) -{ - char *name = NULL; - u32 iface = ~0, tap = ~0, protos = 0; - clib_error_t *err; - - while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) { - if (unformat(input, "from %U", unformat_vnet_sw_interface, - rm.vnet_main, &iface)) - ; - else if (unformat(input, "as %s", &name)) - ; - else if (unformat(input, "%U", unformat_protos, &protos)) - ; - else - break; - } - - if (!protos) - return clib_error_return(0, - "no protocols specified"); - else if (iface == ~0) - return clib_error_return(0, - "interface name is missing or invalid"); - else if (!name) - return clib_error_return(0, - "host interface name is missing or invalid"); - - if (protos & PROTO_BIT_OSPF2) { - /* Require arp, icmp4, and igmp4 for ospf2. */ - if (!(protos & PROTO_BIT_ARP) || - !(protos & PROTO_BIT_ICMP4) || - !(protos & PROTO_BIT_IGMP4)) - return clib_error_return(0, - "ospf2 requires arp, icmp4, and igmp4"); - } - - if (protos & PROTO_BIT_TCP) /* Require arp and icmp4 for tcp. */ - if (!(protos & PROTO_BIT_ARP) || !(protos & PROTO_BIT_ICMP4)) - return clib_error_return(0, - "tcp requires arp and icmp4"); - - if (protos & PROTO_BIT_UDP) { - /* Require arp, icmp4, and igmp4 for udp. */ - if (!(protos & PROTO_BIT_ARP) || - !(protos & PROTO_BIT_ICMP4) || - !(protos & PROTO_BIT_IGMP4)) - return clib_error_return(0, - "udp requires arp, icmp4, and igmp4"); - } - - err = do_tap_connect(m, name, iface, &tap); - if (err) { - if (tap != ~0) - vnet_tap_delete(m, tap); - return err; - } - - if ((protos & PROTO_BIT_ARP) || (protos & PROTO_BIT_ICMP4)) { - if (rm.ns_index == ~0) { - char nsname = 0; - netns_sub_t sub = { - .notify = netns_notify_cb, - .opaque = 0, - }; - - rm.ns_index = netns_open(&nsname, &sub); - if (rm.ns_index == ~0) { - vnet_tap_delete(m, tap); - clib_error_return(0, - "failed to open namespace"); - } - } - } - - if (protos & PROTO_BIT_IGMP4) - add_ip4_multicast_arc(); - - if (protos & PROTO_BIT_ARP) - ethernet_register_input_type(m, ETHERNET_TYPE_ARP, - tap_inject_arp_node.index); - - if (protos & PROTO_BIT_ICMP4) - ip4_register_protocol(IP_PROTOCOL_ICMP, - tap_inject_icmp_node.index); - - if (protos & PROTO_BIT_OSPF2) - ip4_register_protocol(IP_PROTOCOL_OSPF, - tap_inject_classified_node.index); - - if (protos & PROTO_BIT_TCP) - ip4_register_protocol(IP_PROTOCOL_TCP, - tap_inject_classified_node.index); - - if (protos & PROTO_BIT_UDP) - ip4_register_protocol(IP_PROTOCOL_UDP, - tap_inject_classified_node.index); - - /* Find sw_if_index of tap associated with data plane interface. */ - rm.iface_to_tap[iface] = tap; - rm.iface_to_protos[iface] = protos; - - /* Find data plane interface associated with host tap ifindex. */ - insert_tap_to_iface(if_nametoindex(name), iface); - - return 0; -} - -VLIB_CLI_COMMAND(tap_inject_command, static) = { - .path = "tap inject", - .short_help = "tap inject <protocol[,protocol...]> from <intfc-name> as <host-intfc-name>", - .function = tap_inject, -}; - -static clib_error_t * -interface_add_del(struct vnet_main_t *m, u32 hw_if_index, u32 add) -{ - vnet_hw_interface_t *hw = vnet_get_hw_interface(m, hw_if_index); - vnet_sw_interface_t *sw = vnet_get_sw_interface(m, hw->sw_if_index); - ASSERT(hw->sw_if_index == sw->sw_if_index); - - vec_validate(rm.iface_to_tap, sw->sw_if_index); - vec_validate(rm.iface_to_protos, sw->sw_if_index); - rm.iface_to_tap[sw->sw_if_index] = ~0; - rm.iface_to_protos[sw->sw_if_index] = 0; - return 0; -} -VNET_HW_INTERFACE_ADD_DEL_FUNCTION(interface_add_del); - -clib_error_t * -vlib_plugin_register(vlib_main_t *m, vnet_plugin_handoff_t *h, int f) -{ - rm.vnet_main = h->vnet_main; - rm.ns_index = ~0; - return 0; -} - -static clib_error_t *router_init(vlib_main_t *m) -{ - return 0; -} -VLIB_INIT_FUNCTION(router_init); |