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:

committed by
Damjan Marion

parent
4008ac998f
commit
87df12d5de
@ -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
@ -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 */
|
||||
|
||||
/*
|
||||
|
219
test/test_ip6.py
219
test/test_ip6.py
@ -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
|
||||
#
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user