IPv6 RA improvements

1) tests for RA options
 2) memleaks deleteing a ip6_radv_info_t
 3) MLD prefix code refactoring

Change-Id: I34db103994bd8fbdbbec50b202d72770dd145681
Signed-off-by: Neale Ranns <nranns@cisco.com>
This commit is contained in:
Neale Ranns
2017-02-18 08:16:41 -08:00
committed by Damjan Marion
parent 4008ac998f
commit 87df12d5de
7 changed files with 400 additions and 163 deletions

View File

@ -241,17 +241,31 @@ define sw_interface_ip6nd_ra_config_reply
/** \brief IPv6 router advertisement prefix config request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sw_if_index -
@param address[] -
@param address_length -
@param use_default -
@param no_advertise -
@param off_link -
@param no_autoconfig -
@param no_onlink -
@param is_no -
@param val_lifetime -
@param pref_lifetime -
@param sw_if_index - The interface the RA prefix information is for
@param address[] - The prefix to advertise
@param address_length - the prefix length
@param use_default - Revert to default settings
@param no_advertise - Do not advertise this prefix
@param off_link - The prefix is off link (it is not configured on the interface)
Configures the L-flag, When set, indicates that this
prefix can be used for on-link determination.
@param no_autoconfig - Setting for the A-flag. When
set indicates that this prefix can be used for
stateless address configuration.
@param no_onlink - The prefix is not on link. Make sure this is consistent
with the off_link parameter else YMMV
@param is_no - add/delete
@param val_lifetime - The length of time in
seconds (relative to the time the packet is sent)
that the prefix is valid for the purpose of on-link
determination. A value of all one bits
(0xffffffff) represents infinity
@param pref_lifetime - The length of time in
seconds (relative to the time the packet is sent)
that addresses generated from the prefix via
stateless address autoconfiguration remain
preferred [ADDRCONF]. A value of all one bits
(0xffffffff) represents infinity.
*/
define sw_interface_ip6nd_ra_prefix
{

File diff suppressed because it is too large Load Diff

View File

@ -350,6 +350,7 @@ do { \
It is a bad idea to allocate or free pool element from within
@c pool_foreach. Build a vector of indices and dispose of them later.
Or call pool_flush.
@par Example
@ -421,6 +422,31 @@ do { \
do { body; } while (0); \
}
/**
* @brief Remove all elemenets from a pool in a safe way
*
* @param VAR each element in the pool
* @param POOL The pool to flush
* @param BODY The actions to perform on each element before it is returned to
* the pool. i.e. before it is 'freed'
*/
#define pool_flush(VAR, POOL, BODY) \
{ \
uword *_pool_var(ii), *_pool_var(dv) = NULL; \
\
pool_foreach((VAR), (POOL), \
({ \
vec_add1(_pool_var(dv), (VAR) - (POOL)); \
})); \
vec_foreach(_pool_var(ii), _pool_var(dv)) \
{ \
(VAR) = pool_elt_at_index((POOL), *_pool_var(ii)); \
do { BODY; } while (0); \
pool_put((POOL), (VAR)); \
} \
vec_free(_pool_var(dv)); \
}
#endif /* included_pool_h */
/*

View File

@ -10,7 +10,8 @@ from vpp_pg_interface import is_ipv6_misc
from scapy.packet import Raw
from scapy.layers.l2 import Ether, Dot1Q
from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, \
ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation
ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \
ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo
from util import ppp
from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \
in6_mactoifaceid, in6_ismaddr
@ -288,7 +289,7 @@ class TestIPv6(VppTestCase):
self.send_and_assert_no_replies(self.pg0, pkts,
"No response to NS for unknown target")
def validate_ra(self, intf, rx, dst_ip=None):
def validate_ra(self, intf, rx, dst_ip=None, mtu=9000, pi_opt=None):
if not dst_ip:
dst_ip = intf.remote_ip6
@ -308,17 +309,47 @@ class TestIPv6(VppTestCase):
self.assertEqual(in6_ptop(rx[IPv6].src),
in6_ptop(mk_ll_addr(intf.local_mac)))
# it should contain the links MTU
ra = rx[ICMPv6ND_RA]
self.assertEqual(ra[ICMPv6NDOptMTU].mtu, mtu)
# it should contain the source's link layer address option
sll = ra[ICMPv6NDOptSrcLLAddr]
self.assertEqual(sll.lladdr, intf.local_mac)
if not pi_opt:
# the RA should not contain prefix information
self.assertFalse(ra.haslayer(ICMPv6NDOptPrefixInfo))
else:
raos = rx.getlayer(ICMPv6NDOptPrefixInfo, 1)
# the options are nested in the scapy packet in way that i cannot
# decipher how to decode. this 1st layer of option always returns
# nested classes, so a direct obj1=obj2 comparison always fails.
# however, the getlayer(.., 2) does give one instnace.
# so we cheat here and construct a new opt instnace for comparison
rd = ICMPv6NDOptPrefixInfo(prefixlen=raos.prefixlen,
prefix=raos.prefix,
L=raos.L,
A=raos.A)
if type(pi_opt) is list:
for ii in range(len(pi_opt)):
self.assertEqual(pi_opt[ii], rd)
rd = rx.getlayer(ICMPv6NDOptPrefixInfo, ii+2)
else:
self.assertEqual(pi_opt, raos)
def send_and_expect_ra(self, intf, pkts, remark, dst_ip=None,
filter_out_fn=is_ipv6_misc):
filter_out_fn=is_ipv6_misc,
opt=None):
intf.add_stream(pkts)
self.pg0.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
rx = intf.get_capture(1, filter_out_fn=filter_out_fn)
self.assertEqual(len(rx), 1)
rx = rx[0]
self.validate_ra(intf, rx, dst_ip)
self.validate_ra(intf, rx, dst_ip, pi_opt=opt)
def test_rs(self):
""" IPv6 Router Solicitation Exceptions
@ -415,6 +446,184 @@ class TestIPv6(VppTestCase):
dst_ip="ff02::1",
filter_out_fn=None)
#
# Configure The RA to announce the links prefix
#
self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
self.pg0.local_ip6_prefix_len)
#
# RAs should now contain the prefix information option
#
opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
prefix=self.pg0.local_ip6,
L=1,
A=1)
self.pg0.ip6_ra_config(send_unicast=1)
ll = mk_ll_addr(self.pg0.remote_mac)
p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
IPv6(dst=self.pg0.local_ip6, src=ll) /
ICMPv6ND_RS())
self.send_and_expect_ra(self.pg0, p,
"RA with prefix-info",
dst_ip=ll,
opt=opt)
#
# Change the prefix info to not off-link
# L-flag is clear
#
self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
self.pg0.local_ip6_prefix_len,
off_link=1)
opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
prefix=self.pg0.local_ip6,
L=0,
A=1)
self.pg0.ip6_ra_config(send_unicast=1)
self.send_and_expect_ra(self.pg0, p,
"RA with Prefix info with L-flag=0",
dst_ip=ll,
opt=opt)
#
# Change the prefix info to not off-link, no-autoconfig
# L and A flag are clear in the advert
#
self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
self.pg0.local_ip6_prefix_len,
off_link=1,
no_autoconfig=1)
opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
prefix=self.pg0.local_ip6,
L=0,
A=0)
self.pg0.ip6_ra_config(send_unicast=1)
self.send_and_expect_ra(self.pg0, p,
"RA with Prefix info with A & L-flag=0",
dst_ip=ll,
opt=opt)
#
# Change the flag settings back to the defaults
# L and A flag are set in the advert
#
self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
self.pg0.local_ip6_prefix_len)
opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
prefix=self.pg0.local_ip6,
L=1,
A=1)
self.pg0.ip6_ra_config(send_unicast=1)
self.send_and_expect_ra(self.pg0, p,
"RA with Prefix info",
dst_ip=ll,
opt=opt)
#
# Change the prefix info to not off-link, no-autoconfig
# L and A flag are clear in the advert
#
self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
self.pg0.local_ip6_prefix_len,
off_link=1,
no_autoconfig=1)
opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
prefix=self.pg0.local_ip6,
L=0,
A=0)
self.pg0.ip6_ra_config(send_unicast=1)
self.send_and_expect_ra(self.pg0, p,
"RA with Prefix info with A & L-flag=0",
dst_ip=ll,
opt=opt)
#
# Use the reset to defults option to revert to defaults
# L and A flag are clear in the advert
#
self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
self.pg0.local_ip6_prefix_len,
use_default=1)
opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
prefix=self.pg0.local_ip6,
L=1,
A=1)
self.pg0.ip6_ra_config(send_unicast=1)
self.send_and_expect_ra(self.pg0, p,
"RA with Prefix reverted to defaults",
dst_ip=ll,
opt=opt)
#
# Advertise Another prefix. With no L-flag/A-flag
#
self.pg0.ip6_ra_prefix(self.pg1.local_ip6n,
self.pg1.local_ip6_prefix_len,
off_link=1,
no_autoconfig=1)
opt = [ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
prefix=self.pg0.local_ip6,
L=1,
A=1),
ICMPv6NDOptPrefixInfo(prefixlen=self.pg1.local_ip6_prefix_len,
prefix=self.pg1.local_ip6,
L=0,
A=0)]
self.pg0.ip6_ra_config(send_unicast=1)
ll = mk_ll_addr(self.pg0.remote_mac)
p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
IPv6(dst=self.pg0.local_ip6, src=ll) /
ICMPv6ND_RS())
self.send_and_expect_ra(self.pg0, p,
"RA with multiple Prefix infos",
dst_ip=ll,
opt=opt)
#
# Remove the first refix-info - expect the second is still in the
# advert
#
self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
self.pg0.local_ip6_prefix_len,
is_no=1)
opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg1.local_ip6_prefix_len,
prefix=self.pg1.local_ip6,
L=0,
A=0)
self.pg0.ip6_ra_config(send_unicast=1)
self.send_and_expect_ra(self.pg0, p,
"RA with Prefix reverted to defaults",
dst_ip=ll,
opt=opt)
#
# Remove the second prefix-info - expect no prefix-info i nthe adverts
#
self.pg0.ip6_ra_prefix(self.pg1.local_ip6n,
self.pg1.local_ip6_prefix_len,
is_no=1)
self.pg0.ip6_ra_config(send_unicast=1)
self.send_and_expect_ra(self.pg0, p,
"RA with Prefix reverted to defaults",
dst_ip=ll)
#
# Reset the periodic advertisements back to default values
#

View File

@ -272,6 +272,17 @@ class VppInterface(object):
suppress,
send_unicast)
def ip6_ra_prefix(self, address, address_length, is_no=0,
off_link=0, no_autoconfig=0, use_default=0):
"""Configure IPv6 RA suppress on the VPP interface."""
self.test.vapi.ip6_sw_interface_ra_prefix(self.sw_if_index,
address,
address_length,
is_no=is_no,
off_link=off_link,
no_autoconfig=no_autoconfig,
use_default=use_default)
def admin_up(self):
"""Put interface ADMIN-UP."""
self.test.vapi.sw_interface_set_flags(self.sw_if_index,

View File

@ -31,7 +31,7 @@ class VppNeighbor(VppObject):
"""
def __init__(self, test, sw_if_index, mac_addr, nbr_addr,
af=AF_INET, is_static=0):
af=AF_INET, is_static=False):
self._test = test
self.sw_if_index = sw_if_index
self.mac_addr = mactobinary(mac_addr)

View File

@ -286,6 +286,31 @@ class VppPapiProvider(object):
'suppress': suppress,
'send_unicast': send_unicast})
def ip6_sw_interface_ra_prefix(self,
sw_if_index,
address,
address_length,
use_default=0,
no_advertise=0,
off_link=0,
no_autoconfig=0,
no_onlink=0,
is_no=0,
val_lifetime=0xffffffff,
pref_lifetime=0xffffffff):
return self.api(self.papi.sw_interface_ip6nd_ra_prefix,
{'sw_if_index': sw_if_index,
'address': address,
'address_length': address_length,
'use_default': use_default,
'no_advertise': no_advertise,
'off_link': off_link,
'no_autoconfig': no_autoconfig,
'no_onlink': no_onlink,
'is_no': is_no,
'val_lifetime': val_lifetime,
'pref_lifetime': pref_lifetime})
def ip6_sw_interface_enable_disable(self, sw_if_index, enable):
"""
Enable/Disable An interface for IPv6