IPv6 ND Router discovery control plane (VPP-1095)
Change-Id: I4b5b60e7c6f618bb935eab1e96a2e79bbb14f58f Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
This commit is contained in:
@ -342,6 +342,7 @@ libvnet_la_SOURCES += \
|
||||
vnet/ip/ip6_neighbor.c \
|
||||
vnet/ip/ip6_pg.c \
|
||||
vnet/ip/ip6_reassembly.c \
|
||||
vnet/ip/rd_cp.c \
|
||||
vnet/ip/ip_api.c \
|
||||
vnet/ip/ip_checksum.c \
|
||||
vnet/ip/ip_frag.c \
|
||||
@ -360,6 +361,7 @@ nobase_include_HEADERS += \
|
||||
vnet/ip/icmp6.h \
|
||||
vnet/ip/igmp_packet.h \
|
||||
vnet/ip/ip.api.h \
|
||||
vnet/ip/rd_cp.api.h \
|
||||
vnet/ip/ip4_error.h \
|
||||
vnet/ip/ip4.h \
|
||||
vnet/ip/ip4_mtrie.h \
|
||||
@ -382,6 +384,7 @@ nobase_include_HEADERS += \
|
||||
|
||||
API_FILES += \
|
||||
vnet/ip/ip.api \
|
||||
vnet/ip/rd_cp.api \
|
||||
vnet/ip/punt.api
|
||||
|
||||
########################################
|
||||
|
@ -1,4 +1,4 @@
|
||||
option version = "1.0.0";
|
||||
option version = "1.1.0";
|
||||
|
||||
service {
|
||||
rpc want_interface_events returns want_interface_events_reply
|
||||
@ -336,6 +336,30 @@ autoreply define sw_interface_set_mac_address
|
||||
u8 mac_address[6];
|
||||
};
|
||||
|
||||
/** \brief Get interface's MAC address
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param sw_if_index - the interface whose MAC will be returned
|
||||
*/
|
||||
define sw_interface_get_mac_address
|
||||
{
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 sw_if_index;
|
||||
};
|
||||
|
||||
/** \brief Reply for get interface's MAC address request
|
||||
@param context - returned sender context, to match reply w/ request
|
||||
@param retval - return code
|
||||
@param mac_addr - returned interface's MAC address
|
||||
*/
|
||||
define sw_interface_get_mac_address_reply
|
||||
{
|
||||
u32 context;
|
||||
i32 retval;
|
||||
u8 mac_address[6];
|
||||
};
|
||||
|
||||
/** \brief Set an interface's rx-mode
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
|
@ -61,6 +61,7 @@ _(SW_INTERFACE_SET_UNNUMBERED, sw_interface_set_unnumbered) \
|
||||
_(SW_INTERFACE_CLEAR_STATS, sw_interface_clear_stats) \
|
||||
_(SW_INTERFACE_TAG_ADD_DEL, sw_interface_tag_add_del) \
|
||||
_(SW_INTERFACE_SET_MAC_ADDRESS, sw_interface_set_mac_address) \
|
||||
_(SW_INTERFACE_GET_MAC_ADDRESS, sw_interface_get_mac_address) \
|
||||
_(CREATE_VLAN_SUBIF, create_vlan_subif) \
|
||||
_(CREATE_SUBIF, create_subif) \
|
||||
_(DELETE_SUBIF, delete_subif) \
|
||||
@ -903,6 +904,37 @@ out:
|
||||
REPLY_MACRO (VL_API_SW_INTERFACE_SET_MAC_ADDRESS_REPLY);
|
||||
}
|
||||
|
||||
static void vl_api_sw_interface_get_mac_address_t_handler
|
||||
(vl_api_sw_interface_get_mac_address_t * mp)
|
||||
{
|
||||
vl_api_sw_interface_get_mac_address_reply_t *rmp;
|
||||
vl_api_registration_t *reg;
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
u32 sw_if_index = ntohl (mp->sw_if_index);
|
||||
vnet_sw_interface_t *si;
|
||||
ethernet_interface_t *eth_if = 0;
|
||||
int rv = 0;
|
||||
|
||||
VALIDATE_SW_IF_INDEX (mp);
|
||||
|
||||
si = vnet_get_sup_sw_interface (vnm, sw_if_index);
|
||||
if (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
|
||||
eth_if = ethernet_get_interface (ðernet_main, si->hw_if_index);
|
||||
|
||||
BAD_SW_IF_INDEX_LABEL;
|
||||
|
||||
reg = vl_api_client_index_to_registration (mp->client_index);
|
||||
if (!reg)
|
||||
return;
|
||||
rmp = vl_msg_api_alloc (sizeof (*rmp));
|
||||
rmp->_vl_msg_id = htons (VL_API_SW_INTERFACE_GET_MAC_ADDRESS_REPLY);
|
||||
rmp->context = mp->context;
|
||||
rmp->retval = htonl (rv);
|
||||
if (!rv && eth_if)
|
||||
memcpy (rmp->mac_address, eth_if->address, 6);
|
||||
vl_api_send_msg (reg, (u8 *) rmp);
|
||||
}
|
||||
|
||||
static void vl_api_sw_interface_set_rx_mode_t_handler
|
||||
(vl_api_sw_interface_set_rx_mode_t * mp)
|
||||
{
|
||||
|
42
src/vnet/ip/rd_cp.api
Normal file
42
src/vnet/ip/rd_cp.api
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Cisco and/or its affiliates.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
option version = "1.0.0";
|
||||
|
||||
/** \brief Enable/disable IPv6 ND address autoconfiguration
|
||||
and setting up default routes
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param sw_if_index - interface to enable the autoconfigutation on
|
||||
@param enable - 1 to enable address autoconfiguration, 0 to disable
|
||||
@param install_default_routes - 1 to enable installing defaut routes,
|
||||
0 to disable installing defaut routes,
|
||||
the value is ignored (and taken as 0)
|
||||
when enable param is set to 0
|
||||
*/
|
||||
autoreply define ip6_nd_address_autoconfig
|
||||
{
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 sw_if_index;
|
||||
u8 enable;
|
||||
u8 install_default_routes;
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
929
src/vnet/ip/rd_cp.c
Normal file
929
src/vnet/ip/rd_cp.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -41,6 +41,7 @@
|
||||
#include <vnet/l2tp/l2tp.api.h>
|
||||
#include <vnet/span/span.api.h>
|
||||
#include <vnet/ip/ip.api.h>
|
||||
#include <vnet/ip/rd_cp.api.h>
|
||||
#include <vnet/unix/tap.api.h>
|
||||
#include <vnet/vxlan/vxlan.api.h>
|
||||
#include <vnet/geneve/geneve.api.h>
|
||||
|
175
test/test_ip6.py
175
test/test_ip6.py
@ -1005,6 +1005,181 @@ class TestIPv6RD(TestIPv6ND):
|
||||
self.verify_prefix_info(ev.prefixes[1], prefix_info_2)
|
||||
|
||||
|
||||
class TestIPv6RDControlPlane(TestIPv6ND):
|
||||
""" IPv6 Router Discovery Control Plane Test Case """
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestIPv6RDControlPlane, cls).setUpClass()
|
||||
|
||||
def setUp(self):
|
||||
super(TestIPv6RDControlPlane, self).setUp()
|
||||
|
||||
# create 1 pg interface
|
||||
self.create_pg_interfaces(range(1))
|
||||
|
||||
self.interfaces = list(self.pg_interfaces)
|
||||
|
||||
# setup all interfaces
|
||||
for i in self.interfaces:
|
||||
i.admin_up()
|
||||
i.config_ip6()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestIPv6RDControlPlane, self).tearDown()
|
||||
|
||||
@staticmethod
|
||||
def create_ra_packet(pg, routerlifetime=None):
|
||||
src_ip = pg.remote_ip6_ll
|
||||
dst_ip = pg.local_ip6
|
||||
if routerlifetime is not None:
|
||||
ra = ICMPv6ND_RA(routerlifetime=routerlifetime)
|
||||
else:
|
||||
ra = ICMPv6ND_RA()
|
||||
p = (Ether(dst=pg.local_mac, src=pg.remote_mac) /
|
||||
IPv6(dst=dst_ip, src=src_ip) / ra)
|
||||
return p
|
||||
|
||||
@staticmethod
|
||||
def get_default_routes(fib):
|
||||
list = []
|
||||
for entry in fib:
|
||||
if entry.address_length == 0:
|
||||
for path in entry.path:
|
||||
if path.sw_if_index != 0xFFFFFFFF:
|
||||
defaut_route = {}
|
||||
defaut_route['sw_if_index'] = path.sw_if_index
|
||||
defaut_route['next_hop'] = path.next_hop
|
||||
list.append(defaut_route)
|
||||
return list
|
||||
|
||||
@staticmethod
|
||||
def get_interface_addresses(fib, pg):
|
||||
list = []
|
||||
for entry in fib:
|
||||
if entry.address_length == 128:
|
||||
path = entry.path[0]
|
||||
if path.sw_if_index == pg.sw_if_index:
|
||||
list.append(entry.address)
|
||||
return list
|
||||
|
||||
def test_all(self):
|
||||
""" Test handling of SLAAC addresses and default routes """
|
||||
|
||||
fib = self.vapi.ip6_fib_dump()
|
||||
default_routes = self.get_default_routes(fib)
|
||||
initial_addresses = set(self.get_interface_addresses(fib, self.pg0))
|
||||
self.assertEqual(default_routes, [])
|
||||
router_address = self.pg0.remote_ip6n_ll
|
||||
|
||||
self.vapi.ip6_nd_address_autoconfig(self.pg0.sw_if_index, 1, 1)
|
||||
|
||||
self.sleep(0.1)
|
||||
|
||||
# send RA
|
||||
packet = (self.create_ra_packet(self.pg0) / ICMPv6NDOptPrefixInfo(
|
||||
prefix="1::",
|
||||
prefixlen=64,
|
||||
validlifetime=2,
|
||||
preferredlifetime=2,
|
||||
L=1,
|
||||
A=1,
|
||||
) / ICMPv6NDOptPrefixInfo(
|
||||
prefix="7::",
|
||||
prefixlen=20,
|
||||
validlifetime=1500,
|
||||
preferredlifetime=1000,
|
||||
L=1,
|
||||
A=0,
|
||||
))
|
||||
self.pg0.add_stream([packet])
|
||||
self.pg_start()
|
||||
|
||||
self.sleep(0.1)
|
||||
|
||||
fib = self.vapi.ip6_fib_dump()
|
||||
|
||||
# check FIB for new address
|
||||
addresses = set(self.get_interface_addresses(fib, self.pg0))
|
||||
new_addresses = addresses.difference(initial_addresses)
|
||||
self.assertEqual(len(new_addresses), 1)
|
||||
prefix = list(new_addresses)[0][:8] + '\0\0\0\0\0\0\0\0'
|
||||
self.assertEqual(inet_ntop(AF_INET6, prefix), '1::')
|
||||
|
||||
# check FIB for new default route
|
||||
default_routes = self.get_default_routes(fib)
|
||||
self.assertEqual(len(default_routes), 1)
|
||||
dr = default_routes[0]
|
||||
self.assertEqual(dr['sw_if_index'], self.pg0.sw_if_index)
|
||||
self.assertEqual(dr['next_hop'], router_address)
|
||||
|
||||
# send RA to delete default route
|
||||
packet = self.create_ra_packet(self.pg0, routerlifetime=0)
|
||||
self.pg0.add_stream([packet])
|
||||
self.pg_start()
|
||||
|
||||
self.sleep(0.1)
|
||||
|
||||
# check that default route is deleted
|
||||
fib = self.vapi.ip6_fib_dump()
|
||||
default_routes = self.get_default_routes(fib)
|
||||
self.assertEqual(len(default_routes), 0)
|
||||
|
||||
self.sleep(0.1)
|
||||
|
||||
# send RA
|
||||
packet = self.create_ra_packet(self.pg0)
|
||||
self.pg0.add_stream([packet])
|
||||
self.pg_start()
|
||||
|
||||
self.sleep(0.1)
|
||||
|
||||
# check FIB for new default route
|
||||
fib = self.vapi.ip6_fib_dump()
|
||||
default_routes = self.get_default_routes(fib)
|
||||
self.assertEqual(len(default_routes), 1)
|
||||
dr = default_routes[0]
|
||||
self.assertEqual(dr['sw_if_index'], self.pg0.sw_if_index)
|
||||
self.assertEqual(dr['next_hop'], router_address)
|
||||
|
||||
# send RA, updating router lifetime to 1s
|
||||
packet = self.create_ra_packet(self.pg0, 1)
|
||||
self.pg0.add_stream([packet])
|
||||
self.pg_start()
|
||||
|
||||
self.sleep(0.1)
|
||||
|
||||
# check that default route still exists
|
||||
fib = self.vapi.ip6_fib_dump()
|
||||
default_routes = self.get_default_routes(fib)
|
||||
self.assertEqual(len(default_routes), 1)
|
||||
dr = default_routes[0]
|
||||
self.assertEqual(dr['sw_if_index'], self.pg0.sw_if_index)
|
||||
self.assertEqual(dr['next_hop'], router_address)
|
||||
|
||||
self.sleep(1)
|
||||
|
||||
# check that default route is deleted
|
||||
fib = self.vapi.ip6_fib_dump()
|
||||
default_routes = self.get_default_routes(fib)
|
||||
self.assertEqual(len(default_routes), 0)
|
||||
|
||||
# check FIB still contains the SLAAC address
|
||||
addresses = set(self.get_interface_addresses(fib, self.pg0))
|
||||
new_addresses = addresses.difference(initial_addresses)
|
||||
self.assertEqual(len(new_addresses), 1)
|
||||
prefix = list(new_addresses)[0][:8] + '\0\0\0\0\0\0\0\0'
|
||||
self.assertEqual(inet_ntop(AF_INET6, prefix), '1::')
|
||||
|
||||
self.sleep(1)
|
||||
|
||||
# check that SLAAC address is deleted
|
||||
fib = self.vapi.ip6_fib_dump()
|
||||
addresses = set(self.get_interface_addresses(fib, self.pg0))
|
||||
new_addresses = addresses.difference(initial_addresses)
|
||||
self.assertEqual(len(new_addresses), 0)
|
||||
|
||||
|
||||
class IPv6NDProxyTest(TestIPv6ND):
|
||||
""" IPv6 ND ProxyTest Case """
|
||||
|
||||
|
@ -54,16 +54,6 @@ class VppInterface(object):
|
||||
"""Local IPv6 address - raw, suitable as API parameter."""
|
||||
return socket.inet_pton(socket.AF_INET6, self.local_ip6)
|
||||
|
||||
@property
|
||||
def local_ip6_ll(self):
|
||||
"""Local IPv6 linnk-local address on VPP interface (string)."""
|
||||
return self._local_ip6_ll
|
||||
|
||||
@property
|
||||
def local_ip6n_ll(self):
|
||||
"""Local IPv6 link-local address - raw, suitable as API parameter."""
|
||||
return self.local_ip6n_ll
|
||||
|
||||
@property
|
||||
def remote_ip6(self):
|
||||
"""IPv6 address of remote peer "connected" to this interface."""
|
||||
@ -74,6 +64,28 @@ class VppInterface(object):
|
||||
"""IPv6 address of remote peer - raw, suitable as API parameter"""
|
||||
return socket.inet_pton(socket.AF_INET6, self.remote_ip6)
|
||||
|
||||
@property
|
||||
def local_ip6_ll(self):
|
||||
"""Local IPv6 linnk-local address on VPP interface (string)."""
|
||||
return self._local_ip6_ll
|
||||
|
||||
@property
|
||||
def local_ip6n_ll(self):
|
||||
"""Local IPv6 link-local address - raw, suitable as API parameter."""
|
||||
return self._local_ip6n_ll
|
||||
|
||||
@property
|
||||
def remote_ip6_ll(self):
|
||||
"""Link-local IPv6 address of remote peer
|
||||
"connected" to this interface."""
|
||||
return self._remote_ip6_ll
|
||||
|
||||
@property
|
||||
def remote_ip6n_ll(self):
|
||||
"""Link-local IPv6 address of remote peer
|
||||
- raw, suitable as API parameter"""
|
||||
return self._remote_ip6n_ll
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Name of the interface."""
|
||||
@ -196,6 +208,9 @@ class VppInterface(object):
|
||||
self._local_ip6_ll = mk_ll_addr(self.local_mac)
|
||||
self._local_ip6n_ll = socket.inet_pton(socket.AF_INET6,
|
||||
self.local_ip6_ll)
|
||||
self._remote_ip6_ll = mk_ll_addr(self.remote_mac)
|
||||
self._remote_ip6n_ll = socket.inet_pton(socket.AF_INET6,
|
||||
self.remote_ip6_ll)
|
||||
|
||||
def config_ip4(self):
|
||||
"""Configure IPv4 address on the VPP interface."""
|
||||
|
@ -480,6 +480,13 @@ class VppPapiProvider(object):
|
||||
'mrd': mrd,
|
||||
'sw_if_index': sw_if_index})
|
||||
|
||||
def ip6_nd_address_autoconfig(self, sw_if_index, enable,
|
||||
install_default_routes):
|
||||
return self.api(self.papi.ip6_nd_address_autoconfig,
|
||||
{'sw_if_index': sw_if_index,
|
||||
'enable': enable,
|
||||
'install_default_routes': install_default_routes})
|
||||
|
||||
def want_macs_learn_events(self, enable_disable=1, scan_delay=0,
|
||||
max_macs_in_event=0, learn_limit=0):
|
||||
return self.api(self.papi.want_l2_macs_events,
|
||||
|
Reference in New Issue
Block a user