ip6-nd: support dump/details for IPv6 RA

Type: improvement

With this change, add support for dumping IPv6 Router Advertisements
details on a per-interface basis (or all). Also, cover that with a test.

Signed-off-by: Alexander Chernavin <achernavin@netgate.com>
Change-Id: I89fa93439d33cc36252377f27187b18b3d30a1d4
This commit is contained in:
Alexander Chernavin
2023-02-02 14:22:56 +00:00
committed by Neale Ranns
parent 02dfd29634
commit 3b28fd7306
6 changed files with 549 additions and 98 deletions

View File

@ -20,7 +20,7 @@
called through a shared memory interface.
*/
option version = "1.0.0";
option version = "1.1.0";
import "vnet/ip/ip_types.api";
import "vnet/interface_types.api";
@ -106,6 +106,134 @@ autoreply define sw_interface_ip6nd_ra_prefix
u32 pref_lifetime;
};
/** \brief IPv6 Router Advertisements prefix entry
@param prefix - prefix to advertise
@param onlink_flag - if true, the prefix can be used for on-link
determination
@param autonomous_flag - if true, the prefix can be used for stateless
address configuration
@param val_lifetime - valid lifetime in seconds (0xffffffff represents
infinity)
@param pref_lifetime - preferred lifetime in seconds (0xffffffff represents
infinity)
@param valid_lifetime_expires - number of seconds in which valid lifetime
expires (zero means never, negative value
means expired this number of seconds ago)
@param pref_lifetime_expires - number of seconds in which preferred
lifetime expires (zero means never, negative
value means expired this number of seconds
ago)
@param decrement_lifetime_flag - if true, decrement valid lifetime and
preferred lifetime
@param no_advertise - if true, the prefix will not be advertised
*/
typedef ip6nd_ra_prefix
{
vl_api_prefix_t prefix;
bool onlink_flag;
bool autonomous_flag;
u32 val_lifetime;
u32 pref_lifetime;
f64 valid_lifetime_expires;
f64 pref_lifetime_expires;
bool decrement_lifetime_flag;
bool no_advertise;
};
/** \brief Dump IPv6 Router Advertisements details on a per-interface basis
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sw_if_index - interface index to use as a filter (0xffffffff
represents all interfaces)
*/
define sw_interface_ip6nd_ra_dump
{
option in_progress;
u32 client_index;
u32 context;
vl_api_interface_index_t sw_if_index;
option vat_help = "[(<if-name>|sw_if_index <if-idx>)]";
};
/** \brief Details on IPv6 Router Advertisements for a single interface
@param context - returned sender context, to match reply w/ request
@param sw_if_index - interface index the details are belong to
@param cur_hop_limit - current hop limit
@param adv_managed_flag - if true, enable DHCP for address
@param adv_other_flag - if true, Enable DHCP for other information
@param adv_router_lifetime - lifetime associated with the default router in
seconds (zero indicates that the router is not
a default router)
@param adv_neighbor_reachable_time - number of milliseconds within which a
neighbor is assumed to be reachable
(zero means unspecified)
@param adv_retransmit_interval - number of milliseconds between
retransmitted Neighbor Solicitation
messages (zero means unspecified)
@param adv_link_mtu - MTU that all the nodes on a link use
@param send_radv - if true, send periodic Router Advertisements
@param cease_radv - if true, cease to send periodic Router Advertisements
@param send_unicast - if true, destination address of a Router
Advertisement message will use the source address of
the Router Solicitation message (when available).
Otherwise, multicast address will be used
@param adv_link_layer_address - if true, add link layer address option
@param max_radv_interval - maximum time in seconds allowed between sending
unsolicited multicast Router Advertisements
@param min_radv_interval - minimum time in seconds allowed between sending
unsolicited multicast Router Advertisements
@param last_radv_time - number of seconds since the last time a solicited
Router Advertisement message was sent (zero means
never)
@param last_multicast_time - number of seconds since the last time a
multicast Router Advertisements message was
sent (zero means never)
@param next_multicast_time - number of seconds within which next time a
multicast Router Advertisement message will be
sent (zero means never)
@param initial_adverts_count - number of initial Router Advertisement
messages to send
@param initial_adverts_interval - number of seconds between initial Router
Advertisement messages
@param initial_adverts_sent - if true, all initial Router Advertisement
messages were sent
@param n_advertisements_sent - number of Router Advertisements sent
@param n_solicitations_rcvd - number of Router Solicitations received
@param n_solicitations_dropped - number of Router Solicitations dropped
@param n_prefixes - number of prefix entries
@param prefixes - array of prefix entries
*/
define sw_interface_ip6nd_ra_details
{
option in_progress;
u32 context;
vl_api_interface_index_t sw_if_index;
u8 cur_hop_limit;
bool adv_managed_flag;
bool adv_other_flag;
u16 adv_router_lifetime;
u32 adv_neighbor_reachable_time;
u32 adv_retransmit_interval;
u32 adv_link_mtu;
bool send_radv;
bool cease_radv;
bool send_unicast;
bool adv_link_layer_address;
f64 max_radv_interval;
f64 min_radv_interval;
f64 last_radv_time;
f64 last_multicast_time;
f64 next_multicast_time;
u32 initial_adverts_count;
f64 initial_adverts_interval;
bool initial_adverts_sent;
u32 n_advertisements_sent;
u32 n_solicitations_rcvd;
u32 n_solicitations_dropped;
u32 n_prefixes;
vl_api_ip6nd_ra_prefix_t prefixes[n_prefixes];
};
/** \brief IPv6 ND (mirror) proxy
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request

View File

@ -221,6 +221,175 @@ static void
REPLY_MACRO (VL_API_SW_INTERFACE_IP6ND_RA_PREFIX_REPLY);
}
static void
ip6_radv_prefix_encode (f64 now, const ip6_radv_prefix_t *in,
vl_api_ip6nd_ra_prefix_t *out)
{
fib_prefix_t in_ip6_pfx = {
.fp_addr = {
.ip6 = in->prefix,
},
.fp_len = in->prefix_len,
.fp_proto = FIB_PROTOCOL_IP6,
};
ip_prefix_encode (&in_ip6_pfx, &out->prefix);
out->onlink_flag = in->adv_on_link_flag;
out->autonomous_flag = in->adv_autonomous_flag;
out->val_lifetime = htonl (in->adv_valid_lifetime_in_secs);
out->pref_lifetime = htonl (in->adv_pref_lifetime_in_secs);
if (in->adv_valid_lifetime_in_secs != ~0)
{
out->valid_lifetime_expires =
clib_host_to_net_f64 (in->valid_lifetime_expires - now);
}
if (in->adv_pref_lifetime_in_secs != ~0)
{
out->pref_lifetime_expires =
clib_host_to_net_f64 (in->pref_lifetime_expires - now);
}
out->decrement_lifetime_flag = in->decrement_lifetime_flag;
out->no_advertise = (in->enabled == 0);
}
static void
send_sw_interface_ip6nd_ra_details (vl_api_registration_t *reg, u32 context,
ip6_ra_t *radv_info)
{
vl_api_sw_interface_ip6nd_ra_details_t *rmp = 0;
vl_api_ip6nd_ra_prefix_t *api_radv_pfx;
u32 n_prefixes = pool_elts (radv_info->adv_prefixes_pool);
ip6_radv_prefix_t *radv_pfx;
u32 msg_size = sizeof (*rmp) + n_prefixes * sizeof (*api_radv_pfx);
vlib_main_t *vm = vlib_get_main ();
f64 now = vlib_time_now (vm);
rmp = vl_msg_api_alloc (msg_size);
if (!rmp)
return;
clib_memset (rmp, 0, msg_size);
rmp->_vl_msg_id =
ntohs (VL_API_SW_INTERFACE_IP6ND_RA_DETAILS + REPLY_MSG_ID_BASE);
rmp->context = context;
rmp->sw_if_index = htonl (radv_info->sw_if_index);
rmp->cur_hop_limit = radv_info->curr_hop_limit;
rmp->adv_managed_flag = radv_info->adv_managed_flag;
rmp->adv_other_flag = radv_info->adv_other_flag;
rmp->adv_router_lifetime = htons (radv_info->adv_router_lifetime_in_sec);
rmp->adv_neighbor_reachable_time =
htonl (radv_info->adv_neighbor_reachable_time_in_msec);
rmp->adv_retransmit_interval = htonl (
radv_info->adv_time_in_msec_between_retransmitted_neighbor_solicitations);
rmp->adv_link_mtu = htonl (radv_info->adv_link_mtu);
rmp->send_radv = radv_info->send_radv;
rmp->cease_radv = radv_info->cease_radv;
rmp->send_unicast = radv_info->send_unicast;
rmp->adv_link_layer_address = radv_info->adv_link_layer_address;
rmp->max_radv_interval = clib_host_to_net_f64 (radv_info->max_radv_interval);
rmp->min_radv_interval = clib_host_to_net_f64 (radv_info->min_radv_interval);
if (radv_info->last_radv_time > 0.0)
{
rmp->last_radv_time =
clib_host_to_net_f64 (now - radv_info->last_radv_time);
}
if ((radv_info->next_multicast_time - radv_info->last_multicast_time) > 0.0)
{
rmp->last_multicast_time =
clib_host_to_net_f64 (now - radv_info->last_multicast_time);
rmp->next_multicast_time =
clib_host_to_net_f64 (radv_info->next_multicast_time - now);
}
rmp->initial_adverts_count = htonl (radv_info->initial_adverts_count);
rmp->initial_adverts_interval =
clib_host_to_net_f64 (radv_info->initial_adverts_interval);
rmp->initial_adverts_sent = (radv_info->initial_adverts_sent == 0);
rmp->n_advertisements_sent = htonl (radv_info->n_advertisements_sent);
rmp->n_solicitations_rcvd = htonl (radv_info->n_solicitations_rcvd);
rmp->n_solicitations_dropped = htonl (radv_info->n_solicitations_dropped);
rmp->n_prefixes = htonl (n_prefixes);
api_radv_pfx = rmp->prefixes;
pool_foreach (radv_pfx, radv_info->adv_prefixes_pool)
{
ip6_radv_prefix_encode (now, radv_pfx, api_radv_pfx);
api_radv_pfx++;
}
vl_api_send_msg (reg, (u8 *) rmp);
}
typedef struct
{
u32 *sw_if_indices;
} api_dump_ip6_ra_itf_walk_ctx_t;
static walk_rc_t
api_dump_ip6_ra_itf_walk_fn (u32 sw_if_index, void *arg)
{
api_dump_ip6_ra_itf_walk_ctx_t *ctx = arg;
vec_add1 (ctx->sw_if_indices, sw_if_index);
return (WALK_CONTINUE);
}
static void
vl_api_sw_interface_ip6nd_ra_dump_t_handler (
vl_api_sw_interface_ip6nd_ra_dump_t *mp)
{
vl_api_registration_t *reg;
u32 sw_if_index;
ip6_ra_t *radv_info;
reg = vl_api_client_index_to_registration (mp->client_index);
if (!reg)
return;
sw_if_index = ntohl (mp->sw_if_index);
if (sw_if_index == INDEX_INVALID)
{
/* dump all interfaces */
api_dump_ip6_ra_itf_walk_ctx_t ctx = {
.sw_if_indices = NULL,
};
u32 *sw_if_i;
ip6_ra_itf_walk (api_dump_ip6_ra_itf_walk_fn, &ctx);
vec_foreach (sw_if_i, ctx.sw_if_indices)
{
radv_info = ip6_ra_get_itf (*sw_if_i);
if (radv_info != NULL)
{
send_sw_interface_ip6nd_ra_details (reg, mp->context, radv_info);
}
}
vec_free (ctx.sw_if_indices);
}
else
{
/* dump a single interface */
radv_info = ip6_ra_get_itf (sw_if_index);
if (radv_info != NULL)
{
send_sw_interface_ip6nd_ra_details (reg, mp->context, radv_info);
}
}
}
static void
vl_api_ip6nd_send_router_solicitation_t_handler
(vl_api_ip6nd_send_router_solicitation_t * mp)

View File

@ -325,6 +325,63 @@ api_ip6nd_proxy_enable_disable (vat_main_t *vam)
return -1;
}
static int
api_sw_interface_ip6nd_ra_dump (vat_main_t *vam)
{
unformat_input_t *i = vam->input;
vl_api_sw_interface_ip6nd_ra_dump_t *mp;
vl_api_control_ping_t *mp_ping;
u32 sw_if_index = ~0;
int ret;
/* Parse args required to build the message */
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
;
else if (unformat (i, "sw_if_index %u", &sw_if_index))
;
else
{
clib_warning ("parse error '%U'", format_unformat_error, i);
return -99;
}
}
/* Construct the API message */
M (SW_INTERFACE_IP6ND_RA_DUMP, mp);
mp->sw_if_index = ntohl (sw_if_index);
/* Send it */
S (mp);
/* Use control ping for synchronization */
PING (&ip6_nd_test_main, mp_ping);
S (mp_ping);
/* Wait for a reply... */
W (ret);
return ret;
}
static void
vl_api_sw_interface_ip6nd_ra_details_t_handler (
vl_api_sw_interface_ip6nd_ra_details_t *mp)
{
vat_main_t *vam = ip6_nd_test_main.vat_main;
u32 sw_if_index;
u8 send_radv;
/* Read the message */
sw_if_index = ntohl (mp->sw_if_index);
send_radv = mp->send_radv;
/* Print it */
print (vam->ofp, "sw_if_index: %u, send_radv: %s", sw_if_index,
(send_radv ? "on" : "off"));
}
#include <ip6-nd/ip6_nd.api_test.c>
/*

View File

@ -65,95 +65,6 @@ typedef CLIB_PACKED (struct
#define MAX_DELAY_BETWEEN_RAS 1800 /* seconds */
#define MAX_RA_DELAY_TIME .5 /* seconds */
/* advertised prefix option */
typedef struct
{
/* basic advertised information */
ip6_address_t prefix;
u8 prefix_len;
int adv_on_link_flag;
int adv_autonomous_flag;
u32 adv_valid_lifetime_in_secs;
u32 adv_pref_lifetime_in_secs;
/* advertised values are computed from these times if decrementing */
f64 valid_lifetime_expires;
f64 pref_lifetime_expires;
/* local information */
int enabled;
int deprecated_prefix_flag;
int decrement_lifetime_flag;
#define MIN_ADV_VALID_LIFETIME 7203 /* seconds */
#define DEF_ADV_VALID_LIFETIME 2592000
#define DEF_ADV_PREF_LIFETIME 604800
/* extensions are added here, mobile, DNS etc.. */
} ip6_radv_prefix_t;
typedef struct ip6_ra_t_
{
/* advertised config information, zero means unspecified */
u8 curr_hop_limit;
int adv_managed_flag;
int adv_other_flag;
u16 adv_router_lifetime_in_sec;
u32 adv_neighbor_reachable_time_in_msec;
u32 adv_time_in_msec_between_retransmitted_neighbor_solicitations;
/* mtu option */
u32 adv_link_mtu;
/* local information */
u32 sw_if_index;
int send_radv; /* radv on/off on this interface - set by config */
int cease_radv; /* we are ceasing to send - set byf config */
int send_unicast;
int adv_link_layer_address;
int prefix_option;
int failed_device_check;
int ref_count;
/* prefix option */
ip6_radv_prefix_t *adv_prefixes_pool;
/* Hash table mapping address to index in interface advertised prefix pool. */
mhash_t address_to_prefix_index;
f64 max_radv_interval;
f64 min_radv_interval;
f64 min_delay_between_radv;
f64 max_delay_between_radv;
f64 max_rtr_default_lifetime;
f64 last_radv_time;
f64 last_multicast_time;
f64 next_multicast_time;
u32 initial_adverts_count;
f64 initial_adverts_interval;
u32 initial_adverts_sent;
/* stats */
u32 n_advertisements_sent;
u32 n_solicitations_rcvd;
u32 n_solicitations_dropped;
/* router solicitations sending state */
u8 keep_sending_rs; /* when true then next fields are valid */
icmp6_send_router_solicitation_params_t params;
f64 sleep_interval;
f64 due_time;
u32 n_left;
f64 start_time;
vlib_buffer_t *buffer;
u32 seed;
} ip6_ra_t;
static ip6_link_delegate_id_t ip6_ra_delegate_id;
static ip6_ra_t *ip6_ra_pool;
@ -191,7 +102,7 @@ ip6_ra_report_unregister (ip6_ra_report_notify_t fn)
}
}
static inline ip6_ra_t *
ip6_ra_t *
ip6_ra_get_itf (u32 sw_if_index)
{
index_t rai;
@ -214,6 +125,18 @@ ip6_ra_adv_enabled (u32 sw_if_index)
return ((ra != NULL) && (ra->send_radv != 0));
}
void
ip6_ra_itf_walk (ip6_ra_itf_walk_fn_t fn, void *ctx)
{
ip6_ra_t *radv_info;
pool_foreach (radv_info, ip6_ra_pool)
{
if (WALK_STOP == fn (radv_info->sw_if_index, ctx))
break;
}
}
/* for "syslogging" - use elog for now */
#define foreach_log_level \
_ (DEBUG, "DEBUG") \

View File

@ -21,6 +21,105 @@
#include <vnet/fib/fib_types.h>
/* advertised prefix option */
typedef struct
{
/* basic advertised information */
ip6_address_t prefix;
u8 prefix_len;
int adv_on_link_flag;
int adv_autonomous_flag;
u32 adv_valid_lifetime_in_secs;
u32 adv_pref_lifetime_in_secs;
/* advertised values are computed from these times if decrementing */
f64 valid_lifetime_expires;
f64 pref_lifetime_expires;
/* local information */
int enabled;
int deprecated_prefix_flag;
int decrement_lifetime_flag;
#define MIN_ADV_VALID_LIFETIME 7203 /* seconds */
#define DEF_ADV_VALID_LIFETIME 2592000
#define DEF_ADV_PREF_LIFETIME 604800
/* extensions are added here, mobile, DNS etc.. */
} ip6_radv_prefix_t;
typedef struct
{
u32 irt;
u32 mrt;
u32 mrc;
u32 mrd;
} icmp6_send_router_solicitation_params_t;
typedef struct ip6_ra_t_
{
/* advertised config information, zero means unspecified */
u8 curr_hop_limit;
int adv_managed_flag;
int adv_other_flag;
u16 adv_router_lifetime_in_sec;
u32 adv_neighbor_reachable_time_in_msec;
u32 adv_time_in_msec_between_retransmitted_neighbor_solicitations;
/* mtu option */
u32 adv_link_mtu;
/* local information */
u32 sw_if_index;
int send_radv; /* radv on/off on this interface - set by config */
int cease_radv; /* we are ceasing to send - set byf config */
int send_unicast;
int adv_link_layer_address;
int prefix_option;
int failed_device_check;
int ref_count;
/* prefix option */
ip6_radv_prefix_t *adv_prefixes_pool;
/* Hash table mapping address to index in interface advertised prefix pool.
*/
mhash_t address_to_prefix_index;
f64 max_radv_interval;
f64 min_radv_interval;
f64 min_delay_between_radv;
f64 max_delay_between_radv;
f64 max_rtr_default_lifetime;
f64 last_radv_time;
f64 last_multicast_time;
f64 next_multicast_time;
u32 initial_adverts_count;
f64 initial_adverts_interval;
u32 initial_adverts_sent;
/* stats */
u32 n_advertisements_sent;
u32 n_solicitations_rcvd;
u32 n_solicitations_dropped;
/* router solicitations sending state */
u8 keep_sending_rs; /* when true then next fields are valid */
icmp6_send_router_solicitation_params_t params;
f64 sleep_interval;
f64 due_time;
u32 n_left;
f64 start_time;
vlib_buffer_t *buffer;
u32 seed;
} ip6_ra_t;
extern ip6_ra_t *ip6_ra_get_itf (u32 sw_if_index);
extern int ip6_ra_config (vlib_main_t * vm, u32 sw_if_index,
u8 suppress, u8 managed, u8 other,
u8 ll_option, u8 send_unicast, u8 cease,
@ -35,13 +134,9 @@ extern int ip6_ra_prefix (vlib_main_t * vm, u32 sw_if_index,
u8 off_link, u8 no_autoconfig,
u8 no_onlink, u8 is_no);
typedef struct
{
u32 irt;
u32 mrt;
u32 mrc;
u32 mrd;
} icmp6_send_router_solicitation_params_t;
typedef walk_rc_t (*ip6_ra_itf_walk_fn_t) (u32 sw_if_index, void *ctx);
extern void ip6_ra_itf_walk (ip6_ra_itf_walk_fn_t fn, void *ctx);
extern void icmp6_send_router_solicitation (vlib_main_t * vm,
u32 sw_if_index,

View File

@ -779,6 +779,85 @@ class TestIPv6(TestIPv6ND):
rx = rx[0]
self.validate_ra(intf, rx, dst_ip, src_ip=src_ip, pi_opt=opt)
def test_ip6_ra_dump(self):
"""IPv6 RA dump"""
# Dump IPv6 RA for all interfaces
ip6_ra_dump = self.vapi.sw_interface_ip6nd_ra_dump(sw_if_index=0xFFFFFFFF)
self.assertEqual(len(ip6_ra_dump), len(self.interfaces))
for ip6_ra in ip6_ra_dump:
self.assertFalse(ip6_ra.send_radv)
self.assertEqual(ip6_ra.n_prefixes, 0)
self.assertEqual(len(ip6_ra.prefixes), 0)
self.assertEqual(ip6_ra.last_radv_time, 0.0)
self.assertEqual(ip6_ra.last_multicast_time, 0.0)
self.assertEqual(ip6_ra.next_multicast_time, 0.0)
self.assertEqual(ip6_ra.n_advertisements_sent, 0)
self.assertEqual(ip6_ra.n_solicitations_rcvd, 0)
self.assertEqual(ip6_ra.n_solicitations_dropped, 0)
# Enable sending IPv6 RA for an interface
self.pg0.ip6_ra_config(no=1, suppress=1)
# Add IPv6 RA prefixes for the interface
pfx0 = IPv6Network(
"%s/%s" % (self.pg0.local_ip6, self.pg0.local_ip6_prefix_len), strict=False
)
pfx1 = IPv6Network("fafa::/96")
self.pg0.ip6_ra_prefix(pfx0, off_link=0, no_autoconfig=0)
self.pg0.ip6_ra_prefix(pfx1, off_link=1, no_autoconfig=1)
# Wait for multicast IPv6 RA
self.sleep(1)
# Dump IPv6 RA for the interface
ip6_ra_dump = self.vapi.sw_interface_ip6nd_ra_dump(
sw_if_index=self.pg0.sw_if_index
)
self.assertEqual(len(ip6_ra_dump), 1)
ip6_ra = ip6_ra_dump[0]
self.assertEqual(ip6_ra.sw_if_index, self.pg0.sw_if_index)
self.assertTrue(ip6_ra.send_radv)
self.assertEqual(ip6_ra.n_prefixes, 2)
self.assertEqual(len(ip6_ra.prefixes), 2)
self.assertEqual(ip6_ra.last_radv_time, 0.0)
self.assertGreater(ip6_ra.last_multicast_time, 0.0)
self.assertGreater(ip6_ra.next_multicast_time, 0.0)
self.assertGreater(ip6_ra.n_advertisements_sent, 0)
self.assertEqual(ip6_ra.n_solicitations_rcvd, 0)
self.assertEqual(ip6_ra.n_solicitations_dropped, 0)
self.assertEqual(ip6_ra.prefixes[0].prefix, pfx0)
self.assertTrue(ip6_ra.prefixes[0].onlink_flag)
self.assertTrue(ip6_ra.prefixes[0].autonomous_flag)
self.assertFalse(ip6_ra.prefixes[0].no_advertise)
self.assertEqual(ip6_ra.prefixes[1].prefix, pfx1)
self.assertFalse(ip6_ra.prefixes[1].onlink_flag)
self.assertFalse(ip6_ra.prefixes[1].autonomous_flag)
self.assertFalse(ip6_ra.prefixes[1].no_advertise)
# Reset sending IPv6 RA for the interface
self.pg0.ip6_ra_config(suppress=1)
# Remove IPv6 RA prefixes for the interface
self.pg0.ip6_ra_prefix(pfx0, is_no=1)
self.pg0.ip6_ra_prefix(pfx1, is_no=1)
# Dump IPv6 RA for the interface
ip6_ra_dump = self.vapi.sw_interface_ip6nd_ra_dump(
sw_if_index=self.pg0.sw_if_index
)
self.assertEqual(len(ip6_ra_dump), 1)
ip6_ra = ip6_ra_dump[0]
self.assertEqual(ip6_ra.sw_if_index, self.pg0.sw_if_index)
self.assertFalse(ip6_ra.send_radv)
self.assertEqual(ip6_ra.n_prefixes, 0)
self.assertEqual(len(ip6_ra.prefixes), 0)
def test_rs(self):
"""IPv6 Router Solicitation Exceptions