#include <vnet/ethernet/ethernet.h>
#include <vppinfra/mhash.h>
#include <vppinfra/md5.h>
+#include <vnet/adj/adj.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/ip6_fib.h>
#if DPDK==1
#include <vnet/devices/dpdk/dpdk.h>
typedef struct {
ip6_neighbor_key_t key;
u8 link_layer_address[8];
+ u16 flags;
+#define IP6_NEIGHBOR_FLAG_STATIC (1 << 0)
+#define IP6_NEIGHBOR_FLAG_DYNAMIC (2 << 0)
u64 cpu_time_last_updated;
+ adj_index_t adj_index;
} ip6_neighbor_t;
/* advertised prefix option */
u32 seed;
u64 randomizer;
int ref_count;
- u32 all_nodes_adj_index;
- u32 all_routers_adj_index;
- u32 all_mldv2_routers_adj_index;
+ adj_index_t all_nodes_adj_index;
+ adj_index_t all_routers_adj_index;
+ adj_index_t all_mldv2_routers_adj_index;
/* timing information */
#define DEF_MAX_RADV_INTERVAL 200
uword node_index;
uword type_opaque;
uword data;
+ /* Used for nd event notification only */
+ void * data_callback;
+ u32 pid;
} pending_resolution_t;
mhash_t pending_resolutions_by_address;
pending_resolution_t * pending_resolutions;
+ /* Mac address change notification */
+ mhash_t mac_changes_by_address;
+ pending_resolution_t * mac_changes;
+
u32 * neighbor_input_next_index_by_hw_if_index;
ip6_neighbor_t * neighbor_pool;
} ip6_neighbor_main_t;
static ip6_neighbor_main_t ip6_neighbor_main;
+static ip6_address_t ip6a_zero; /* ip6 address 0 */
static u8 * format_ip6_neighbor_ip6_entry (u8 * s, va_list * va)
{
ip6_neighbor_t * n = va_arg (*va, ip6_neighbor_t *);
vnet_main_t * vnm = vnet_get_main();
vnet_sw_interface_t * si;
+ u8 * flags = 0;
if (! n)
- return format (s, "%=12s%=20s%=20s%=40s", "Time", "Address", "Link layer", "Interface");
+ return format (s, "%=12s%=20s%=6s%=20s%=40s", "Time", "Address", "Flags", "Link layer", "Interface");
+
+ if (n->flags & IP6_NEIGHBOR_FLAG_DYNAMIC)
+ flags = format(flags, "D");
+
+ if (n->flags & IP6_NEIGHBOR_FLAG_STATIC)
+ flags = format(flags, "S");
si = vnet_get_sw_interface (vnm, n->key.sw_if_index);
- s = format (s, "%=12U%=20U%=20U%=40U",
+ s = format (s, "%=12U%=20U%=6s%=20U%=40U",
format_vlib_cpu_time, vm, n->cpu_time_last_updated,
format_ip6_address, &n->key.ip6_address,
+ flags ? (char *)flags : "",
format_ethernet_address, n->link_layer_address,
format_vnet_sw_interface_name, vnm, si);
+ vec_free(flags);
return s;
}
typedef struct {
u8 is_add;
- u8 pad;
+ u8 is_static;
u8 link_layer_address[6];
u32 sw_if_index;
ip6_address_t addr;
} ip6_neighbor_set_unset_rpc_args_t;
+#if DPDK > 0
static void ip6_neighbor_set_unset_rpc_callback
( ip6_neighbor_set_unset_rpc_args_t * a);
-#if DPDK > 0
static void set_unset_ip6_neighbor_rpc
(vlib_main_t * vm,
u32 sw_if_index,
ip6_address_t * a,
u8 *link_layer_addreess,
- int is_add)
+ int is_add, int is_static)
{
ip6_neighbor_set_unset_rpc_args_t args;
+ void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
args.sw_if_index = sw_if_index;
args.is_add = is_add;
- memcpy (&args.addr, a, sizeof (*a));
- memcpy (args.link_layer_address, link_layer_addreess, 6);
+ args.is_static = is_static;
+ clib_memcpy (&args.addr, a, sizeof (*a));
+ clib_memcpy (args.link_layer_address, link_layer_addreess, 6);
vl_api_rpc_call_main_thread (ip6_neighbor_set_unset_rpc_callback,
(u8 *) &args, sizeof (args));
}
#endif
+static void
+ip6_nd_mk_complete (ip6_neighbor_t * nbr)
+{
+ fib_prefix_t pfx = {
+ .fp_len = 128,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_addr = {
+ .ip6 = nbr->key.ip6_address,
+ },
+ };
+ ip6_main_t *im;
+ u32 fib_index;
+
+ im = &ip6_main;
+ fib_index = im->fib_index_by_sw_if_index[nbr->key.sw_if_index];
+
+ /* only once please */
+ if (ADJ_INDEX_INVALID == nbr->adj_index)
+ {
+ nbr->adj_index =
+ adj_nbr_add_or_lock_w_rewrite(FIB_PROTOCOL_IP6,
+ FIB_LINK_IP6,
+ &pfx.fp_addr,
+ nbr->key.sw_if_index,
+ nbr->link_layer_address);
+ ASSERT(ADJ_INDEX_INVALID != nbr->adj_index);
+
+ fib_table_entry_update_one_path(fib_index,
+ &pfx,
+ FIB_SOURCE_ADJ,
+ FIB_ENTRY_FLAG_NONE,
+ FIB_PROTOCOL_IP6,
+ &pfx.fp_addr,
+ nbr->key.sw_if_index,
+ ~0,
+ 1,
+ MPLS_LABEL_INVALID,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ }
+ else
+ {
+ adj_nbr_update_rewrite(nbr->adj_index,
+ nbr->link_layer_address);
+ }
+}
+
int
vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
u32 sw_if_index,
ip6_address_t * a,
u8 * link_layer_address,
- uword n_bytes_link_layer_address)
+ uword n_bytes_link_layer_address,
+ int is_static)
{
- vnet_main_t * vnm = vnet_get_main();
ip6_neighbor_main_t * nm = &ip6_neighbor_main;
ip6_neighbor_key_t k;
- ip6_neighbor_t * n;
- ip6_main_t * im = &ip6_main;
+ ip6_neighbor_t * n = 0;
+ int make_new_nd_cache_entry=1;
uword * p;
u32 next_index;
- pending_resolution_t * pr;
+ pending_resolution_t * pr, * mc;
#if DPDK > 0
if (os_get_cpu_number())
{
set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
- 1 /* set new neighbor */);
+ 1 /* set new neighbor */, is_static);
return 0;
}
#endif
vlib_worker_thread_barrier_sync (vm);
p = mhash_get (&nm->neighbor_index_by_key, &k);
- if (p)
+ if (p) {
n = pool_elt_at_index (nm->neighbor_pool, p[0]);
+ /* Refuse to over-write static neighbor entry. */
+ if (!is_static &&
+ (n->flags & IP6_NEIGHBOR_FLAG_STATIC))
+ return -2;
+ make_new_nd_cache_entry = 0;
+ }
+
+ if (make_new_nd_cache_entry) {
+ pool_get (nm->neighbor_pool, n);
+ mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool,
+ /* old value */ 0);
+ n->key = k;
+ n->adj_index = ADJ_INDEX_INVALID;
+ }
else
- {
- ip6_add_del_route_args_t args;
- ip_adjacency_t adj;
-
- memset (&adj, 0, sizeof(adj));
- adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
- adj.explicit_fib_index = ~0;
-
- vnet_rewrite_for_sw_interface
- (vnm,
- VNET_L3_PACKET_TYPE_IP6,
- sw_if_index,
- ip6_rewrite_node.index,
- link_layer_address,
- &adj.rewrite_header,
- sizeof (adj.rewrite_data));
-
- args.table_index_or_table_id = im->fib_index_by_sw_if_index[sw_if_index];
- args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_ADD | IP6_ROUTE_FLAG_NEIGHBOR;
- args.dst_address = a[0];
- args.dst_address_length = 128;
- args.adj_index = ~0;
- args.add_adj = &adj;
- args.n_add_adj = 1;
-
- ip6_add_del_route (im, &args);
- pool_get (nm->neighbor_pool, n);
- mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool,
- /* old value */ 0);
- n->key = k;
- }
+ {
+ /*
+ * prevent a DoS attack from the data-plane that
+ * spams us with no-op updates to the MAC address
+ */
+ if (0 == memcmp(n->link_layer_address,
+ link_layer_address,
+ n_bytes_link_layer_address))
+ return -1;
+ }
/* Update time stamp and ethernet address. */
- memcpy (n->link_layer_address, link_layer_address, n_bytes_link_layer_address);
+ clib_memcpy (n->link_layer_address,
+ link_layer_address,
+ n_bytes_link_layer_address);
+
n->cpu_time_last_updated = clib_cpu_time_now ();
+ if (is_static)
+ n->flags |= IP6_NEIGHBOR_FLAG_STATIC;
+ else
+ n->flags |= IP6_NEIGHBOR_FLAG_DYNAMIC;
+
+ ip6_nd_mk_complete(n);
/* Customer(s) waiting for this address to be resolved? */
p = mhash_get (&nm->pending_resolutions_by_address, a);
- if (p == 0)
- goto out;
-
- next_index = p[0];
+ if (p)
+ {
+ next_index = p[0];
- while (next_index != (u32)~0)
+ while (next_index != (u32)~0)
+ {
+ pr = pool_elt_at_index (nm->pending_resolutions, next_index);
+ vlib_process_signal_event (vm, pr->node_index,
+ pr->type_opaque,
+ pr->data);
+ next_index = pr->next_index;
+ pool_put (nm->pending_resolutions, pr);
+ }
+
+ mhash_unset (&nm->pending_resolutions_by_address, a, 0);
+ }
+
+ /* Customer(s) requesting ND event for this address? */
+ p = mhash_get (&nm->mac_changes_by_address, a);
+ if (p)
{
- pr = pool_elt_at_index (nm->pending_resolutions, next_index);
- vlib_process_signal_event (vm, pr->node_index,
- pr->type_opaque,
- pr->data);
- next_index = pr->next_index;
- pool_put (nm->pending_resolutions, pr);
+ next_index = p[0];
+
+ while (next_index != (u32)~0)
+ {
+ int (*fp)(u32, u8 *, u32, ip6_address_t *);
+ int rv = 1;
+ mc = pool_elt_at_index (nm->mac_changes, next_index);
+ fp = mc->data_callback;
+
+ /* Call the user's data callback, return 1 to suppress dup events */
+ if (fp)
+ rv = (*fp)(mc->data, link_layer_address, sw_if_index, &ip6a_zero);
+ /*
+ * Signal the resolver process, as long as the user
+ * says they want to be notified
+ */
+ if (rv == 0)
+ vlib_process_signal_event (vm, mc->node_index,
+ mc->type_opaque,
+ mc->data);
+ next_index = mc->next_index;
+ }
}
- mhash_unset (&nm->pending_resolutions_by_address, a, 0);
-
-out:
vlib_worker_thread_barrier_release(vm);
return 0;
}
+static void
+ip6_nd_mk_incomplete (ip6_neighbor_t *nbr)
+{
+ fib_prefix_t pfx = {
+ .fp_len = 128,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_addr = {
+ .ip6 = nbr->key.ip6_address,
+ },
+ };
+ u32 fib_index;
+ ip6_main_t *im;
+
+ im = &ip6_main;
+ fib_index = im->fib_index_by_sw_if_index[nbr->key.sw_if_index];
+
+ /*
+ * revert the adj this ND entry sourced to incomplete
+ */
+ adj_nbr_update_rewrite(nbr->adj_index,
+ NULL);
+
+ /*
+ * remove the FIB entry the ND entry sourced
+ */
+ fib_table_entry_delete(fib_index, &pfx, FIB_SOURCE_ADJ);
+
+ /*
+ * Unlock the adj now that the ARP entry is no longer a source
+ */
+ adj_unlock(nbr->adj_index);
+ nbr->adj_index = ADJ_INDEX_INVALID;
+}
+
int
vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
u32 sw_if_index,
ip6_neighbor_main_t * nm = &ip6_neighbor_main;
ip6_neighbor_key_t k;
ip6_neighbor_t * n;
- ip6_main_t * im = &ip6_main;
- ip6_add_del_route_args_t args;
uword * p;
int rv = 0;
if (os_get_cpu_number())
{
set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
- 0 /* unset */);
+ 0 /* unset */, 0);
return 0;
}
#endif
}
n = pool_elt_at_index (nm->neighbor_pool, p[0]);
+
+ ip6_nd_mk_incomplete(n);
mhash_unset (&nm->neighbor_index_by_key, &n->key, 0);
pool_put (nm->neighbor_pool, n);
- args.table_index_or_table_id = im->fib_index_by_sw_if_index[sw_if_index];
- args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_DEL
- | IP6_ROUTE_FLAG_NEIGHBOR;
- args.dst_address = a[0];
- args.dst_address_length = 128;
- args.adj_index = ~0;
- args.add_adj = NULL;
- args.n_add_adj = 0;
- ip6_add_del_route (im, &args);
out:
vlib_worker_thread_barrier_release(vm);
return rv;
}
+#if DPDK > 0
static void ip6_neighbor_set_unset_rpc_callback
( ip6_neighbor_set_unset_rpc_args_t * a)
{
vlib_main_t * vm = vlib_get_main();
if (a->is_add)
vnet_set_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr,
- a->link_layer_address, 6);
+ a->link_layer_address, 6, a->is_static);
else
vnet_unset_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr,
a->link_layer_address, 6);
}
+#endif
static int
ip6_neighbor_sort (void *a1, void *a2)
u8 mac_address[6];
int addr_valid = 0;
int is_del = 0;
+ int is_static = 0;
u32 sw_if_index;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
else if (unformat (input, "delete") || unformat (input, "del"))
is_del = 1;
+ else if (unformat (input, "static"))
+ is_static = 1;
else
break;
}
if (!is_del)
vnet_set_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
- mac_address, sizeof(mac_address));
+ mac_address, sizeof(mac_address), is_static);
else
vnet_unset_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
mac_address, sizeof(mac_address));
VLIB_CLI_COMMAND (set_ip6_neighbor_command, static) = {
.path = "set ip6 neighbor",
.function = set_ip6_neighbor,
- .short_help = "set ip6 neighbor [del] <intfc> <ip6-address> <mac-address>",
+ .short_help = "set ip6 neighbor [del] <intfc> <ip6-address> <mac-address> [static]",
};
typedef enum {
{
vnet_main_t * vnm = vnet_get_main();
ip6_main_t * im = &ip6_main;
- ip_lookup_main_t * lm = &im->lookup_main;
uword n_packets = frame->n_vectors;
u32 * from, * to_next;
u32 n_left_from, n_left_to_next, next_index, n_advertisements_sent;
if (!ip6_sadd_unspecified && !ip6_sadd_link_local)
{
u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
- ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main, src_adj_index0);
-
- /* Allow all realistic-looking rewrite adjacencies to pass */
- ni0 = adj0->lookup_next_index;
- is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) &&
- (ni0 < IP_LOOKUP_N_NEXT);
- error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0
- || ! is_rewrite0)
- ? ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK
- : error0);
+ if (ADJ_INDEX_INVALID != src_adj_index0)
+ {
+ ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main, src_adj_index0);
+
+ /* Allow all realistic-looking rewrite adjacencies to pass */
+ ni0 = adj0->lookup_next_index;
+ is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) &&
+ (ni0 < IP6_LOOKUP_N_NEXT);
+
+ error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0
+ || ! is_rewrite0)
+ ? ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK
+ : error0);
+ }
+ else
+ {
+ error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK;
+ }
}
o0 = (void *) (h0 + 1);
vnet_set_ip6_ethernet_neighbor (
vm, sw_if_index0,
is_solicitation ? &ip0->src_address : &h0->target_address,
- o0->ethernet_address, sizeof (o0->ethernet_address));
+ o0->ethernet_address, sizeof (o0->ethernet_address), 0);
}
if (is_solicitation && error0 == ICMP6_ERROR_NONE)
{
- /* Check that target address is one that we know about. */
- ip_interface_address_t * ia0;
- ip6_address_fib_t ip6_af0;
- void * oldheap;
-
- ip6_addr_fib_init (&ip6_af0, &h0->target_address,
- vec_elt (im->fib_index_by_sw_if_index,
- sw_if_index0));
-
- /* Gross kludge, "thank you" MJ, don't even ask */
- oldheap = clib_mem_set_heap (clib_per_cpu_mheaps[0]);
- ia0 = ip_get_interface_address (lm, &ip6_af0);
- clib_mem_set_heap (oldheap);
- error0 = ia0 == 0 ?
- ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN : error0;
+ /* Check that target address is local to this router. */
+ fib_node_index_t fei;
+ u32 fib_index;
+
+ fib_index = ip6_fib_table_get_index_for_sw_if_index(sw_if_index0);
+
+ if (~0 == fib_index)
+ {
+ error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
+ }
+ else
+ {
+ fei = ip6_fib_table_lookup_exact_match(fib_index,
+ &h0->target_address,
+ 128);
+
+ if (FIB_NODE_INDEX_INVALID == fei ||
+ !(FIB_ENTRY_FLAG_LOCAL &
+ fib_entry_get_flags_for_source(fei, FIB_SOURCE_INTERFACE)))
+ {
+ error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
+ }
+ }
}
if (is_solicitation)
eth_if0 = ethernet_get_interface (ðernet_main, sw_if0->hw_if_index);
if (eth_if0 && o0)
{
- memcpy (o0->ethernet_address, eth_if0->address, 6);
+ clib_memcpy (o0->ethernet_address, eth_if0->address, 6);
o0->header.type =
ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address;
}
/* Reuse current MAC header, copy SMAC to DMAC and
* interface MAC to SMAC */
- vlib_buffer_reset (p0);
+ vlib_buffer_advance(p0, - ethernet_buffer_header_size(p0));
eth0 = vlib_buffer_get_current(p0);
- memcpy(eth0->dst_address, eth0->src_address, 6);
- memcpy(eth0->src_address, eth_if0->address, 6);
+ clib_memcpy(eth0->dst_address, eth0->src_address, 6);
+ clib_memcpy(eth0->src_address, eth_if0->address, 6);
/* Setup input and output sw_if_index for packet */
ASSERT(vnet_buffer(p0)->sw_if_index[VLIB_RX] == sw_if_index0);
if (!is_unspecified && !is_link_local)
{
u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
- ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main, src_adj_index0);
- error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0
- || (adj0->lookup_next_index != IP_LOOKUP_NEXT_ARP
- && adj0->lookup_next_index != IP_LOOKUP_NEXT_REWRITE))
- ? ICMP6_ERROR_ROUTER_SOLICITATION_SOURCE_NOT_ON_LINK
- : error0);
+ if (ADJ_INDEX_INVALID != src_adj_index0)
+ {
+ ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main,
+ src_adj_index0);
+
+ error0 = (adj0->rewrite_header.sw_if_index != sw_if_index0
+ ? ICMP6_ERROR_ROUTER_SOLICITATION_SOURCE_NOT_ON_LINK
+ : error0);
+ }
+ else
+ {
+ error0 = ICMP6_ERROR_ROUTER_SOLICITATION_SOURCE_NOT_ON_LINK;
+ }
}
/* check for source LL option and process */
vnet_set_ip6_ethernet_neighbor (vm, sw_if_index0,
&ip0->src_address,
o0->ethernet_address,
- sizeof (o0->ethernet_address));
+ sizeof (o0->ethernet_address), 0);
}
/* default is to drop */
h.header.n_data_u64s = 1;
/* copy ll address */
- memcpy(&h.ethernet_address[0], eth_if0->address, 6);
+ clib_memcpy(&h.ethernet_address[0], eth_if0->address, 6);
vlib_buffer_add_data (vm,
p0->free_list_index,
}
h.unused = 0;
- memcpy(&h.dst_address, &pr_info->prefix, sizeof(ip6_address_t));
+ clib_memcpy(&h.dst_address, &pr_info->prefix, sizeof(ip6_address_t));
payload_length += sizeof( icmp6_neighbor_discovery_prefix_information_option_t);
* interface MAC to SMAC */
vlib_buffer_reset (p0);
eth0 = vlib_buffer_get_current(p0);
- memcpy(eth0->dst_address, eth0->src_address, 6);
- memcpy(eth0->src_address, eth_if0->address, 6);
+ clib_memcpy(eth0->dst_address, eth0->src_address, 6);
+ clib_memcpy(eth0->src_address, eth_if0->address, 6);
next0 = is_dropped ?
next0 : ICMP6_ROUTER_SOLICITATION_NEXT_REPLY_TX;
vnet_buffer(p0)->sw_if_index[VLIB_TX] = sw_if_index0;
u32 sw_if_index,
u32 is_add)
{
- ip6_main_t * im = &ip6_main;
ip6_neighbor_main_t * nm = &ip6_neighbor_main;
- ip_lookup_main_t * lm = &im->lookup_main;
ip6_radv_t * a= 0;
- u32 ri = ~0;;
+ u32 ri = ~0;
vnet_sw_interface_t * sw_if0;
ethernet_interface_t * eth_if0 = 0;
ip6_mldp_group_t *m;
/* remove adjacencies */
- ip_del_adjacency (lm, a->all_nodes_adj_index);
- ip_del_adjacency (lm, a->all_routers_adj_index);
- ip_del_adjacency (lm, a->all_mldv2_routers_adj_index);
+ adj_unlock(a->all_nodes_adj_index);
+ adj_unlock(a->all_routers_adj_index);
+ adj_unlock(a->all_mldv2_routers_adj_index);
/* clean up prefix_pool */
pool_foreach (p, a->adv_prefixes_pool, ({
pool_put (nm->if_radv_pool, a);
nm->if_radv_pool_index_by_sw_if_index[sw_if_index] = ~0;
ri = ~0;
+ ip6_sw_interface_enable_disable(sw_if_index, 0);
}
}
else
{
vnet_hw_interface_t * hw_if0;
+ ip6_sw_interface_enable_disable(sw_if_index, 1);
hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index);
pool_get (nm->if_radv_pool, a);
a->seed = random_default_seed();
/* for generating random interface ids */
- a->randomizer = 0x1119194911191949;
+ a->randomizer = 0x1119194911191949ULL;
a->randomizer = random_u64 ((u32 *)&a->randomizer);
a->initial_adverts_count = MAX_INITIAL_RTR_ADVERTISEMENTS ;
/* fill in radv_info for this interface that will be needed later */
a->adv_link_mtu = hw_if0->max_l3_packet_bytes[VLIB_RX];
- memcpy (a->link_layer_address, eth_if0->address, 6);
+ clib_memcpy (a->link_layer_address, eth_if0->address, 6);
/* fill in default link-local address (this may be overridden) */
ip6_link_local_address_from_ethernet_address (&a->link_local_address, eth_if0->address);
mhash_init (&a->address_to_mldp_index, sizeof (uword), sizeof (ip6_address_t));
{
- ip_adjacency_t *adj;
u8 link_layer_address[6] =
{0x33, 0x33, 0x00, 0x00, 0x00, IP6_MULTICAST_GROUP_ID_all_hosts};
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &a->all_nodes_adj_index);
-
- adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
- adj->if_address_index = ~0;
-
- vnet_rewrite_for_sw_interface
- (vnm,
- VNET_L3_PACKET_TYPE_IP6,
- sw_if_index,
- ip6_rewrite_node.index,
- link_layer_address,
- &adj->rewrite_header,
- sizeof (adj->rewrite_data));
+ a->all_nodes_adj_index = adj_rewrite_add_and_lock(FIB_PROTOCOL_IP6,
+ FIB_LINK_IP6,
+ sw_if_index,
+ link_layer_address);
}
{
- ip_adjacency_t *adj;
u8 link_layer_address[6] =
{0x33, 0x33, 0x00, 0x00, 0x00, IP6_MULTICAST_GROUP_ID_all_routers};
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &a->all_routers_adj_index);
-
- adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
- adj->if_address_index = ~0;
-
- vnet_rewrite_for_sw_interface
- (vnm,
- VNET_L3_PACKET_TYPE_IP6,
- sw_if_index,
- ip6_rewrite_node.index,
- link_layer_address,
- &adj->rewrite_header,
- sizeof (adj->rewrite_data));
+ a->all_routers_adj_index = adj_rewrite_add_and_lock(FIB_PROTOCOL_IP6,
+ FIB_LINK_IP6,
+ sw_if_index,
+ link_layer_address);
}
{
- ip_adjacency_t *adj;
u8 link_layer_address[6] =
{0x33, 0x33, 0x00, 0x00, 0x00, IP6_MULTICAST_GROUP_ID_mldv2_routers};
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &a->all_mldv2_routers_adj_index);
-
- adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
- adj->if_address_index = ~0;
-
- vnet_rewrite_for_sw_interface
- (vnm,
- VNET_L3_PACKET_TYPE_IP6,
- sw_if_index,
- ip6_rewrite_node.index,
- link_layer_address,
- &adj->rewrite_header,
- sizeof (adj->rewrite_data));
+ a->all_mldv2_routers_adj_index =
+ adj_rewrite_add_and_lock(FIB_PROTOCOL_IP6,
+ FIB_LINK_IP6,
+ sw_if_index,
+ link_layer_address);
}
/* add multicast groups we will always be reporting */
mcast_group_info->type = 4;
mcast_group_info->mcast_source_address_pool = 0;
mcast_group_info->num_sources = 0;
- memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
+ clib_memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
}
ip6_set_reserved_multicast_address (&addr,
mcast_group_info->type = 4;
mcast_group_info->mcast_source_address_pool = 0;
mcast_group_info->num_sources = 0;
- memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
+ clib_memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
}
ip6_set_reserved_multicast_address (&addr,
mcast_group_info->type = 4;
mcast_group_info->mcast_source_address_pool = 0;
mcast_group_info->num_sources = 0;
- memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
+ clib_memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
}
}
}
rr.type = m->type;
rr.aux_data_len_u32s = 0;
rr.num_sources = clib_host_to_net_u16 (m->num_sources);
- memcpy(&rr.mcast_addr, &m->mcast_address, sizeof(ip6_address_t));
+ clib_memcpy(&rr.mcast_addr, &m->mcast_address, sizeof(ip6_address_t));
num_addr_records++;
ip6_radv_t *radv_info;
vlib_frame_t * f = 0;
u32 n_this_frame = 0;
- u32 n_left_to_next;
- u32 * to_next;
+ u32 n_left_to_next = 0;
+ u32 * to_next = 0;
u32 bo0;
icmp6_router_solicitation_header_t * h0;
vlib_buffer_t * b0;
/* API support functions */
int
ip6_neighbor_ra_config(vlib_main_t * vm, u32 sw_if_index,
- u8 surpress, u8 managed, u8 other,
+ u8 suppress, u8 managed, u8 other,
u8 ll_option, u8 send_unicast, u8 cease,
u8 use_lifetime, u32 lifetime,
u32 initial_count, u32 initial_interval,
if "flag" is set and is_no is true then restore default value else set value corresponding to "flag"
if "flag" is clear don't change corresponding value
*/
- radv_info->send_radv = (surpress != 0) ? ( (is_no != 0) ? 1 : 0 ) : radv_info->send_radv;
+ radv_info->send_radv = (suppress != 0) ? ( (is_no != 0) ? 1 : 0 ) : radv_info->send_radv;
radv_info->adv_managed_flag = ( managed != 0) ? ( (is_no) ? 0 : 1) : radv_info->adv_managed_flag;
radv_info->adv_other_flag = (other != 0) ? ( (is_no) ? 0: 1) : radv_info->adv_other_flag;
radv_info->adv_link_layer_address = ( ll_option != 0) ? ( (is_no) ? 1 : 0) : radv_info->adv_link_layer_address;
memset(prefix, 0x0, sizeof(ip6_radv_prefix_t));
prefix->prefix_len = prefix_len;
- memcpy(&prefix->prefix, prefix_addr, sizeof(ip6_address_t));
+ clib_memcpy(&prefix->prefix, prefix_addr, sizeof(ip6_address_t));
/* initialize default values */
prefix->adv_on_link_flag = 1; /* L bit set */
ip6_neighbor_main_t * nm = &ip6_neighbor_main;
clib_error_t * error = 0;
u8 is_no = 0;
- u8 surpress = 0, managed = 0, other = 0;
- u8 surpress_ll_option = 0, send_unicast = 0, cease= 0;
+ u8 suppress = 0, managed = 0, other = 0;
+ u8 suppress_ll_option = 0, send_unicast = 0, cease= 0;
u8 use_lifetime = 0;
u32 sw_if_index, ra_lifetime = 0, ra_initial_count = 0, ra_initial_interval = 0;
u32 ra_max_interval = 0 , ra_min_interval = 0;
other = 1;
break;
}
- else if (unformat (line_input, "ra-surpress"))
+ else if (unformat (line_input, "ra-suppress") ||
+ unformat (line_input, "ra-surpress"))
{
- surpress = 1;
+ suppress = 1;
break;
}
- else if (unformat (line_input, "ra-surpress-link-layer"))
+ else if (unformat (line_input, "ra-suppress-link-layer") ||
+ unformat (line_input, "ra-surpress-link-layer"))
{
- surpress_ll_option = 1;
+ suppress_ll_option = 1;
break;
}
else if (unformat (line_input, "ra-send-unicast"))
if(add_radv_info)
{
ip6_neighbor_ra_config(vm, sw_if_index,
- surpress, managed, other,
- surpress_ll_option, send_unicast, cease,
+ suppress, managed, other,
+ suppress_ll_option, send_unicast, cease,
use_lifetime, ra_lifetime,
ra_initial_count, ra_initial_interval,
ra_max_interval, ra_min_interval,
return error;
}
+static void
+ip6_print_addrs(vlib_main_t * vm,
+ u32 *addrs)
+{
+ ip_lookup_main_t * lm = &ip6_main.lookup_main;
+ u32 i;
+
+ for (i = 0; i < vec_len (addrs); i++)
+ {
+ ip_interface_address_t * a = pool_elt_at_index(lm->if_address_pool, addrs[i]);
+ ip6_address_t * address = ip_interface_address_get_address (lm, a);
+
+ vlib_cli_output (vm, "\t\t%U/%d",
+ format_ip6_address, address,
+ a->address_length);
+ }
+}
+
static clib_error_t *
show_ip6_interface_cmd (vlib_main_t * vm,
unformat_input_t * input,
vnet_get_sw_interface (vnm, sw_if_index),
(vnet_sw_interface_is_admin_up (vnm, sw_if_index) ? "up" : "down"));
- u32 ai;
- u32 *global_scope = 0,i;
+ u32 ai;
+ u32 *link_scope = 0, *global_scope = 0;
+ u32 *local_scope = 0, *unknown_scope = 0;
ip_interface_address_t * a;
vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index, sw_if_index, ~0);
a = pool_elt_at_index(lm->if_address_pool, ai);
ip6_address_t * address = ip_interface_address_get_address (lm, a);
- if( ip6_address_is_link_local_unicast (address))
- vlib_cli_output (vm, "\tIPv6 is enabled, link-local address is %U\n", format_ip6_address,
- address);
-
- if((address->as_u8[0] & 0xe0) == 0x20)
+ if (ip6_address_is_link_local_unicast (address))
+ vec_add1 (link_scope, ai);
+ else if(ip6_address_is_global_unicast (address))
vec_add1 (global_scope, ai);
+ else if(ip6_address_is_local_unicast (address))
+ vec_add1 (local_scope, ai);
+ else
+ vec_add1 (unknown_scope, ai);
ai = a->next_this_sw_interface;
}
- vlib_cli_output (vm, "\tGlobal unicast address(es):\n");
- for (i = 0; i < vec_len (global_scope); i++)
- {
- a = pool_elt_at_index(lm->if_address_pool, global_scope[i]);
- ip6_address_t * address = ip_interface_address_get_address (lm, a);
- ip6_address_t mask, subnet;
+ if (vec_len (link_scope))
+ {
+ vlib_cli_output (vm, "\tLink-local address(es):\n");
+ ip6_print_addrs (vm, link_scope);
+ vec_free (link_scope);
+ }
- subnet = *address;
- ip6_address_mask_from_width(&mask, a->address_length);
- ip6_address_mask(&subnet, &mask);
+ if (vec_len (local_scope))
+ {
+ vlib_cli_output (vm, "\tLocal unicast address(es):\n");
+ ip6_print_addrs (vm, local_scope);
+ vec_free (local_scope);
+ }
+
+ if (vec_len (global_scope))
+ {
+ vlib_cli_output (vm, "\tGlobal unicast address(es):\n");
+ ip6_print_addrs (vm, global_scope);
+ vec_free (global_scope);
+ }
+
+ if (vec_len (unknown_scope))
+ {
+ vlib_cli_output (vm, "\tOther-scope address(es):\n");
+ ip6_print_addrs (vm, unknown_scope);
+ vec_free (unknown_scope);
+ }
- vlib_cli_output (vm, "\t\t%U, subnet is %U/%d",
- format_ip6_address, address,
- format_ip6_address,&subnet,
- a->address_length);
- }
- vec_free (global_scope);
vlib_cli_output (vm, "\tJoined group address(es):\n");
ip6_mldp_group_t *m;
pool_foreach (m, radv_info->mldp_group_pool, ({
}
else
{
- error = clib_error_return (0, "Ipv6 not enabled on interface",
+ error = clib_error_return (0, "IPv6 not enabled on interface",
format_unformat_error, input);
}
VLIB_CLI_COMMAND (show_ip6_interface_command, static) = {
.path = "show ip6 interface",
.function = show_ip6_interface_cmd,
- .short_help = "Show ip6 interface <iface name>",
+ .short_help = "show ip6 interface <iface name>",
};
clib_error_t *
md5_add (&m, &link_local_address, 16);
md5_finish (&m, digest);
- memcpy(&link_local_address, digest, 16);
+ clib_memcpy(&link_local_address, digest, 16);
radv_info->randomizer = link_local_address.as_u64[0];
/* essentially "enables" ipv6 on this interface */
error = ip6_add_del_interface_address (vm, sw_if_index,
- &link_local_address, 64 /* address width */,
+ &link_local_address,
+ 128 /* address width */,
0 /* is_del */);
if(error)
mcast_group_info->type = 4;
mcast_group_info->mcast_source_address_pool = 0;
mcast_group_info->num_sources = 0;
- memcpy(&mcast_group_info->mcast_address, &a, sizeof(ip6_address_t));
+ clib_memcpy(&mcast_group_info->mcast_address, &a, sizeof(ip6_address_t));
}
}
}
/* value size */ sizeof (uword),
/* key size */ sizeof (ip6_address_t));
+ mhash_init (&nm->mac_changes_by_address,
+ /* value size */ sizeof (uword),
+ /* key size */ sizeof (ip6_address_t));
+
/* default, configurable */
nm->limit_neighbor_cache_size = 50000;
pr - nm->pending_resolutions, 0 /* old value */);
}
+int vnet_add_del_ip6_nd_change_event (vnet_main_t * vnm,
+ void * data_callback,
+ u32 pid,
+ void * address_arg,
+ uword node_index,
+ uword type_opaque,
+ uword data,
+ int is_add)
+{
+ ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+ ip6_address_t * address = address_arg;
+ uword * p;
+ pending_resolution_t * mc;
+ void (*fp)(u32, u8 *) = data_callback;
+
+ if (is_add)
+ {
+ pool_get (nm->mac_changes, mc);
+
+ mc->next_index = ~0;
+ mc->node_index = node_index;
+ mc->type_opaque = type_opaque;
+ mc->data = data;
+ mc->data_callback = data_callback;
+ mc->pid = pid;
+
+ p = mhash_get (&nm->mac_changes_by_address, address);
+ if (p)
+ {
+ /* Insert new resolution at the head of the list */
+ mc->next_index = p[0];
+ mhash_unset (&nm->mac_changes_by_address, address, 0);
+ }
+
+ mhash_set (&nm->mac_changes_by_address, address,
+ mc - nm->mac_changes, 0);
+ return 0;
+ }
+ else
+ {
+ u32 index;
+ pending_resolution_t * mc_last = 0;
+
+ p = mhash_get (&nm->mac_changes_by_address, address);
+ if (p == 0)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ index = p[0];
+
+ while (index != (u32)~0)
+ {
+ mc = pool_elt_at_index (nm->mac_changes, index);
+ if (mc->node_index == node_index &&
+ mc->type_opaque == type_opaque &&
+ mc->pid == pid)
+ {
+ /* Clients may need to clean up pool entries, too */
+ if (fp)
+ (*fp)(mc->data, 0 /* no new mac addrs */);
+ if (index == p[0])
+ {
+ mhash_unset (&nm->mac_changes_by_address, address, 0);
+ if (mc->next_index != ~0)
+ mhash_set (&nm->mac_changes_by_address, address,
+ mc->next_index, 0);
+ pool_put (nm->mac_changes, mc);
+ return 0;
+ }
+ else
+ {
+ ASSERT(mc_last);
+ mc_last->next_index = mc->next_index;
+ pool_put (nm->mac_changes, mc);
+ return 0;
+ }
+ }
+ mc_last = mc;
+ index = mc->next_index;
+ }
+
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+ }
+}
+
+int vnet_ip6_nd_term (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_buffer_t * p0,
+ ethernet_header_t * eth,
+ ip6_header_t * ip,
+ u32 sw_if_index,
+ u16 bd_index,
+ u8 shg)
+{
+ ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+ icmp6_neighbor_solicitation_or_advertisement_header_t * ndh;
+ pending_resolution_t * mc;
+ uword *p;
+
+ ndh = ip6_next_header (ip);
+ if (ndh->icmp.type != ICMP6_neighbor_solicitation &&
+ ndh->icmp.type != ICMP6_neighbor_advertisement)
+ return 0;
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (p0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ u8 *t0 = vlib_add_trace (vm, node, p0,
+ sizeof (icmp6_input_trace_t));
+ clib_memcpy (t0, ip, sizeof (icmp6_input_trace_t));
+ }
+
+ /* Check if anyone want ND events for L2 BDs */
+ p = mhash_get (&nm->mac_changes_by_address, &ip6a_zero);
+ if (p && shg == 0 && /* Only SHG 0 interface which is more likely local */
+ !ip6_address_is_link_local_unicast (&ip->src_address))
+ {
+ u32 next_index = p[0];
+ while (next_index != (u32)~0)
+ {
+ int (*fp)(u32, u8 *, u32, ip6_address_t *);
+ int rv = 1;
+ mc = pool_elt_at_index (nm->mac_changes, next_index);
+ fp = mc->data_callback;
+ /* Call the callback, return 1 to suppress dup events */
+ if (fp) rv = (*fp)(mc->data,
+ eth->src_address,
+ sw_if_index,
+ &ip->src_address);
+ /* Signal the resolver process */
+ if (rv == 0)
+ vlib_process_signal_event (vm, mc->node_index,
+ mc->type_opaque,
+ mc->data);
+ next_index = mc->next_index;
+ }
+ }
+
+ /* Check if MAC entry exsist for solicited target IP */
+ if (ndh->icmp.type == ICMP6_neighbor_solicitation)
+ {
+ icmp6_neighbor_discovery_ethernet_link_layer_address_option_t * opt;
+ l2_bridge_domain_t *bd_config;
+ u8 * macp;
+
+ opt = (void *) (ndh + 1);
+ if ((opt->header.type !=
+ ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address) ||
+ (opt->header.n_data_u64s != 1))
+ return 0; /* source link layer address option not present */
+
+ bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
+ macp = (u8 *) hash_get_mem (bd_config->mac_by_ip6, &ndh->target_address);
+ if (macp)
+ { /* found ip-mac entry, generate eighbor advertisement response */
+ int bogus_length;
+ vlib_node_runtime_t * error_node =
+ vlib_node_get_runtime (vm, ip6_icmp_input_node.index);
+ ip->dst_address = ip->src_address;
+ ip->src_address = ndh->target_address;
+ ip->hop_limit = 255;
+ opt->header.type =
+ ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address;
+ clib_memcpy (opt->ethernet_address, macp, 6);
+ ndh->icmp.type = ICMP6_neighbor_advertisement;
+ ndh->advertisement_flags = clib_host_to_net_u32
+ (ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED |
+ ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE);
+ ndh->icmp.checksum = 0;
+ ndh->icmp.checksum = ip6_tcp_udp_icmp_compute_checksum(vm, p0, ip,
+ &bogus_length);
+ clib_memcpy(eth->dst_address, eth->src_address, 6);
+ clib_memcpy(eth->src_address, macp, 6);
+ vlib_error_count (vm, error_node->node_index,
+ ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_TX, 1);
+ return 1;
+ }
+ }
+
+ return 0;
+
+}
+
+void
+ethernet_ndp_change_mac (vlib_main_t * vm, u32 sw_if_index)
+{
+ ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+ ip6_neighbor_t * n;
+
+ /* *INDENT-OFF* */
+ pool_foreach (n, nm->neighbor_pool, ({
+ if (n->key.sw_if_index == sw_if_index)
+ {
+ if (ADJ_INDEX_INVALID != n->adj_index)
+ {
+ adj_nbr_update_rewrite(n->adj_index,
+ n->link_layer_address);
+ }
+ }
+ }));
+ /* *INDENT-ON* */
+}