urpf: Unicast reverse Path Forwarding (plugin)

Type: feature

 - move the IP4 code to plugin
 - add ip6 support
 - add suport for uRPF on TX
 - add tests

Change-Id: I074c2debc486d3e79c12fad4b8dbd72c41e841a0
Signed-off-by: Neale Ranns <nranns@cisco.com>
This commit is contained in:
Neale Ranns
2020-04-02 15:02:16 +00:00
committed by Ole Trøan
parent dc3e966485
commit d724e4f43b
20 changed files with 1658 additions and 616 deletions

View File

@ -660,6 +660,11 @@ I: vrrp
M: Matthew Smith <mgsmith@netgate.com>
F: src/plugins/vrrp
Plugin - Unicast Reverse Path forwarding
I: urpf
M: Neale Ranns <nranns@cisco.com>
F: src/plugins/urpf
VPP Config Tooling
I: vpp_config
M: John DeNisco <jdenisco@cisco.com>

View File

@ -0,0 +1,27 @@
# Copyright (c) 2020 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.
add_vpp_plugin(urpf
SOURCES
urpf.c
urpf_api.c
ip4_urpf.c
ip6_urpf.c
MULTIARCH_SOURCES
ip4_urpf.c
ip6_urpf.c
API_FILES
urpf.api
)

171
src/plugins/urpf/ip4_urpf.c Normal file
View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 2015 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.
*/
/*
* ip/ip4_source_check.c: IP v4 check source address (unicast RPF check)
*
* Copyright (c) 2008 Eliot Dresselhaus
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <urpf/urpf.h>
#include <urpf/urpf_dp.h>
static char *ip4_urpf_error_strings[] = {
#define _(a,b) "ip4-" # b,
foreach_urpf_error
#undef _
};
VLIB_NODE_FN (ip4_rx_urpf_loose) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP4, VLIB_RX, URPF_MODE_LOOSE));
}
VLIB_NODE_FN (ip4_rx_urpf_strict) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP4, VLIB_RX, URPF_MODE_STRICT));
}
VLIB_NODE_FN (ip4_tx_urpf_loose) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP4, VLIB_TX, URPF_MODE_LOOSE));
}
VLIB_NODE_FN (ip4_tx_urpf_strict) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP4, VLIB_TX, URPF_MODE_STRICT));
}
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip4_rx_urpf_loose) = {
.name = "ip4-rx-urpf-loose",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip4-drop",
},
.n_errors = ARRAY_LEN (ip4_urpf_error_strings),
.error_strings = ip4_urpf_error_strings,
.format_buffer = format_ip4_header,
.format_trace = format_urpf_trace,
};
VLIB_REGISTER_NODE (ip4_rx_urpf_strict) = {
.name = "ip4-rx-urpf-strict",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip4-drop",
},
.n_errors = ARRAY_LEN (ip4_urpf_error_strings),
.error_strings = ip4_urpf_error_strings,
.format_buffer = format_ip4_header,
.format_trace = format_urpf_trace,
};
VLIB_REGISTER_NODE (ip4_tx_urpf_loose) = {
.name = "ip4-tx-urpf-loose",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip4-drop",
},
.n_errors = ARRAY_LEN (ip4_urpf_error_strings),
.error_strings = ip4_urpf_error_strings,
.format_buffer = format_ip4_header,
.format_trace = format_urpf_trace,
};
VLIB_REGISTER_NODE (ip4_tx_urpf_strict) = {
.name = "ip4-tx-urpf-strict",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip4-drop",
},
.n_errors = ARRAY_LEN (ip4_urpf_error_strings),
.error_strings = ip4_urpf_error_strings,
.format_buffer = format_ip4_header,
.format_trace = format_urpf_trace,
};
VNET_FEATURE_INIT (ip4_rx_urpf_loose_feat, static) =
{
.arc_name = "ip4-unicast",
.node_name = "ip4-rx-urpf-loose",
.runs_before = VNET_FEATURES ("ip4-rx-urpf-strict"),
};
VNET_FEATURE_INIT (ip4_rx_urpf_strict_feat, static) =
{
.arc_name = "ip4-unicast",
.node_name = "ip4-rx-urpf-strict",
.runs_before = VNET_FEATURES ("ip4-policer-classify"),
};
VNET_FEATURE_INIT (ip4_tx_urpf_loose_feat, static) =
{
.arc_name = "ip4-output",
.node_name = "ip4-tx-urpf-loose",
};
VNET_FEATURE_INIT (ip4_tx_urpf_strict_feat, static) =
{
.arc_name = "ip4-output",
.node_name = "ip4-tx-urpf-strict",
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

171
src/plugins/urpf/ip6_urpf.c Normal file
View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 2015 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.
*/
/*
* ip/ip4_source_check.c: IP v4 check source address (unicast RPF check)
*
* Copyright (c) 2008 Eliot Dresselhaus
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <urpf/urpf.h>
#include <urpf/urpf_dp.h>
static char *ip6_urpf_error_strings[] = {
#define _(a,b) "ip6-" # b,
foreach_urpf_error
#undef _
};
VLIB_NODE_FN (ip6_rx_urpf_loose) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP6, VLIB_RX, URPF_MODE_LOOSE));
}
VLIB_NODE_FN (ip6_rx_urpf_strict) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP6, VLIB_RX, URPF_MODE_STRICT));
}
VLIB_NODE_FN (ip6_tx_urpf_loose) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP6, VLIB_TX, URPF_MODE_LOOSE));
}
VLIB_NODE_FN (ip6_tx_urpf_strict) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
return (urpf_inline (vm, node, frame, AF_IP6, VLIB_TX, URPF_MODE_STRICT));
}
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip6_rx_urpf_loose) = {
.name = "ip6-rx-urpf-loose",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip6-drop",
},
.n_errors = ARRAY_LEN (ip6_urpf_error_strings),
.error_strings = ip6_urpf_error_strings,
.format_buffer = format_ip6_header,
.format_trace = format_urpf_trace,
};
VLIB_REGISTER_NODE (ip6_rx_urpf_strict) = {
.name = "ip6-rx-urpf-strict",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip6-drop",
},
.n_errors = ARRAY_LEN (ip6_urpf_error_strings),
.error_strings = ip6_urpf_error_strings,
.format_buffer = format_ip6_header,
.format_trace = format_urpf_trace,
};
VLIB_REGISTER_NODE (ip6_tx_urpf_loose) = {
.name = "ip6-tx-urpf-loose",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip6-drop",
},
.n_errors = ARRAY_LEN (ip6_urpf_error_strings),
.error_strings = ip6_urpf_error_strings,
.format_buffer = format_ip6_header,
.format_trace = format_urpf_trace,
};
VLIB_REGISTER_NODE (ip6_tx_urpf_strict) = {
.name = "ip6-tx-urpf-strict",
.vector_size = sizeof (u32),
.n_next_nodes = URPF_N_NEXT,
.next_nodes = {
[URPF_NEXT_DROP] = "ip6-drop",
},
.n_errors = ARRAY_LEN (ip6_urpf_error_strings),
.error_strings = ip6_urpf_error_strings,
.format_buffer = format_ip6_header,
.format_trace = format_urpf_trace,
};
VNET_FEATURE_INIT (ip6_rx_urpf_loose_feat, static) =
{
.arc_name = "ip6-unicast",
.node_name = "ip6-rx-urpf-loose",
.runs_before = VNET_FEATURES ("ip6-rx-urpf-strict"),
};
VNET_FEATURE_INIT (ip6_rx_urpf_strict_feat, static) =
{
.arc_name = "ip6-unicast",
.node_name = "ip6-rx-urpf-strict",
.runs_before = VNET_FEATURES ("ip6-policer-classify"),
};
VNET_FEATURE_INIT (ip6_tx_urpf_loose_feat, static) =
{
.arc_name = "ip6-output",
.node_name = "ip6-tx-urpf-loose",
};
VNET_FEATURE_INIT (ip6_tx_urpf_strict_feat, static) =
{
.arc_name = "ip6-output",
.node_name = "ip6-tx-urpf-strict",
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,295 @@
#!/usr/bin/env python3
import unittest
from framework import VppTestCase, VppTestRunner
from scapy.packet import Raw
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP, ICMP
from scapy.layers.inet6 import IPv6
from vpp_papi import VppEnum
N_PKTS = 63
class TestURPF(VppTestCase):
""" Unicast Reverse Path Forwarding Test Case """
@classmethod
def setUpClass(cls):
super(TestURPF, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TestURPF, cls).tearDownClass()
def setUp(self):
super(TestURPF, self).setUp()
# create 4 pg interfaces so there are a few addresses
# in the FIB
self.create_pg_interfaces(range(4))
for i in self.pg_interfaces:
i.admin_up()
i.config_ip4()
i.resolve_arp()
i.config_ip6()
i.resolve_ndp()
def tearDown(self):
for i in self.pg_interfaces:
i.unconfig_ip4()
i.unconfig_ip6()
i.admin_down()
super(TestURPF, self).tearDown()
def test_urpf4(self):
""" uRPF IP4 """
e = VppEnum
p_spoof_loose = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IP(src="3.3.3.3", dst=self.pg1.remote_ip4) /
UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100)) * N_PKTS
p_spoof_strict = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IP(src=self.pg2.remote_ip4,
dst=self.pg1.remote_ip4) /
UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100)) * N_PKTS
p_good = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IP(src=self.pg0.remote_ip4,
dst=self.pg1.remote_ip4) /
UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100)) * N_PKTS
#
# before adding the uRPF, ensure all packets are forwarded
#
self.send_and_expect(self.pg0, p_good, self.pg1)
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_expect(self.pg0, p_spoof_loose, self.pg1)
#
# apply loose uRPF check on pg0 rx
#
self.vapi.urpf_update(is_input=True,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_LOOSE,
af=e.vl_api_address_family_t.ADDRESS_IP4,
sw_if_index=self.pg0.sw_if_index)
# good packets still pass
self.send_and_expect(self.pg0, p_good, self.pg1)
# packets from address for which there is a route are forwarded
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
# packets from address to which there is no route are dropped
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip4-rx-urpf-loose", N_PKTS)
#
# crank it up to strict mode
#
self.vapi.urpf_update(is_input=True,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_STRICT,
af=e.vl_api_address_family_t.ADDRESS_IP4,
sw_if_index=self.pg0.sw_if_index)
# good packets still pass
self.send_and_expect(self.pg0, p_good, self.pg1)
# packets that would not be routed back thru pg0 are dropped
self.send_and_assert_no_replies(self.pg0, p_spoof_strict)
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip4-rx-urpf-strict", 2 * N_PKTS)
#
# disable uRPF, all traffic should pass
#
self.vapi.urpf_update(is_input=True,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_OFF,
af=e.vl_api_address_family_t.ADDRESS_IP4,
sw_if_index=self.pg0.sw_if_index)
self.send_and_expect(self.pg0, p_good, self.pg1)
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_expect(self.pg0, p_spoof_loose, self.pg1)
#
# Now apply in the TX direction
# for loose it is the same deal, they should not be forwarded
# if there's no route
# for strict they should not be forwarded if they would be
# forwarded thru that interface.
#
self.vapi.urpf_update(is_input=False,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_LOOSE,
af=e.vl_api_address_family_t.ADDRESS_IP4,
sw_if_index=self.pg1.sw_if_index)
self.send_and_expect(self.pg0, p_good, self.pg1)
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip4-tx-urpf-loose", N_PKTS)
self.vapi.urpf_update(is_input=False,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_STRICT,
af=e.vl_api_address_family_t.ADDRESS_IP4,
sw_if_index=self.pg1.sw_if_index)
self.send_and_expect(self.pg0, p_good, self.pg1)
# the strict packet, from a peer is allowed, since it does
# not forward via pg1
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip4-tx-urpf-strict", N_PKTS)
# change the strict packet so that it would forward through pg1
p_spoof_strict = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IP(src=self.pg1.remote_ip4,
dst=self.pg1.remote_ip4) /
UDP(sport=1234, dport=1234) /
Raw(b'\xa5' * 100)) * N_PKTS
self.send_and_assert_no_replies(self.pg0, p_spoof_strict)
self.assert_error_counter_equal("ip4-tx-urpf-strict", 2 * N_PKTS)
# cleanup
self.vapi.urpf_update(is_input=False,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_OFF,
af=e.vl_api_address_family_t.ADDRESS_IP4,
sw_if_index=self.pg1.sw_if_index)
def test_urpf6(self):
""" uRPF IP6 """
e = VppEnum
p_spoof_loose = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IPv6(src="3::3", dst=self.pg1.remote_ip6) /
UDP(sport=1236, dport=1236) /
Raw(b'\xa5' * 100)) * N_PKTS
p_spoof_strict = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IPv6(src=self.pg2.remote_ip6,
dst=self.pg1.remote_ip6) /
UDP(sport=1236, dport=1236) /
Raw(b'\xa5' * 100)) * N_PKTS
p_good = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IPv6(src=self.pg0.remote_ip6,
dst=self.pg1.remote_ip6) /
UDP(sport=1236, dport=1236) /
Raw(b'\xa5' * 100)) * N_PKTS
#
# before adding the uRPF, ensure all packets are forwarded
#
self.send_and_expect(self.pg0, p_good, self.pg1)
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_expect(self.pg0, p_spoof_loose, self.pg1)
#
# apply loose uRPF check on pg0 rx
#
self.vapi.urpf_update(is_input=True,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_LOOSE,
af=e.vl_api_address_family_t.ADDRESS_IP6,
sw_if_index=self.pg0.sw_if_index)
# good packets still pass
self.send_and_expect(self.pg0, p_good, self.pg1)
# packets from address for which there is a route are forwarded
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
# packets from address to which there is no route are dropped
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip6-rx-urpf-loose", N_PKTS)
#
# crank it up to strict mode
#
self.vapi.urpf_update(is_input=True,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_STRICT,
af=e.vl_api_address_family_t.ADDRESS_IP6,
sw_if_index=self.pg0.sw_if_index)
# good packets still pass
self.send_and_expect(self.pg0, p_good, self.pg1)
# packets that would not be routed back thru pg0 are dropped
self.send_and_assert_no_replies(self.pg0, p_spoof_strict)
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip6-rx-urpf-strict", 2 * N_PKTS)
#
# disable uRPF, all traffic should pass
#
self.vapi.urpf_update(is_input=True,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_OFF,
af=e.vl_api_address_family_t.ADDRESS_IP6,
sw_if_index=self.pg0.sw_if_index)
self.send_and_expect(self.pg0, p_good, self.pg1)
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_expect(self.pg0, p_spoof_loose, self.pg1)
#
# Now apply in the TX direction
# for loose it is the same deal, they should not be forwarded
# if there's no route
# for strict they should not be forwarded if they would be
# forwarded thru that interface.
#
self.vapi.urpf_update(is_input=False,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_LOOSE,
af=e.vl_api_address_family_t.ADDRESS_IP6,
sw_if_index=self.pg1.sw_if_index)
self.send_and_expect(self.pg0, p_good, self.pg1)
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip6-tx-urpf-loose", N_PKTS)
self.vapi.urpf_update(is_input=False,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_STRICT,
af=e.vl_api_address_family_t.ADDRESS_IP6,
sw_if_index=self.pg1.sw_if_index)
self.send_and_expect(self.pg0, p_good, self.pg1)
# the strict packet, from a peer is allowed, since it does
# not forward via pg1
self.send_and_expect(self.pg0, p_spoof_strict, self.pg1)
self.send_and_assert_no_replies(self.pg0, p_spoof_loose)
self.assert_error_counter_equal("ip6-tx-urpf-strict", N_PKTS)
# change the strict packet so that it would forward through pg1
p_spoof_strict = (Ether(dst=self.pg0.local_mac,
src=self.pg0.remote_mac) /
IPv6(src=self.pg1.remote_ip6,
dst=self.pg1.remote_ip6) /
UDP(sport=1236, dport=1236) /
Raw(b'\xa5' * 100)) * N_PKTS
self.send_and_assert_no_replies(self.pg0, p_spoof_strict)
self.assert_error_counter_equal("ip6-tx-urpf-strict", 2 * N_PKTS)
# cleanup
self.vapi.urpf_update(is_input=False,
mode=e.vl_api_urpf_mode_t.URPF_API_MODE_OFF,
af=e.vl_api_address_family_t.ADDRESS_IP6,
sw_if_index=self.pg1.sw_if_index)
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)

59
src/plugins/urpf/urpf.api Normal file
View File

@ -0,0 +1,59 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* Copyright (c) 2016 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.
*/
/** \file
This file defines the vpp control-plane API messages
used to control the URPF plugin
*/
option version = "1.0.0";
import "vnet/ip/ip_types.api";
import "vnet/fib/fib_types.api";
import "vnet/interface_types.api";
enum urpf_mode:u8
{
URPF_API_MODE_OFF,
URPF_API_MODE_LOOSE,
URPF_API_MODE_STRICT,
};
/**
* @brief Enable uRPF on a given interface in a given direction
* @param client_index - opaque cookie to identify the sender
* @param context - sender context, to match reply w/ request
* @param mode - Mode
* @param af - Address Family
* @param sw_if_index - Interface
* @param is_input - Direction.
*/
autoreply define urpf_update
{
u32 client_index;
u32 context;
bool is_input[default = true];
vl_api_urpf_mode_t mode;
vl_api_address_family_t af;
vl_api_interface_index_t sw_if_index;
};
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

323
src/plugins/urpf/urpf.c Normal file

File diff suppressed because it is too large Load Diff

50
src/plugins/urpf/urpf.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef __URPF_H__
#define __URPF_H__
#include <vnet/ip/ip_types.h>
#define foreach_urpf_mode \
_(OFF, "off") \
_(LOOSE, "loose") \
_(STRICT, "strict") \
typedef enum urpf_mode_t_
{
#define _(a,b) URPF_MODE_##a,
foreach_urpf_mode
#undef _
} __clib_packed urpf_mode_t;
#define URPF_N_MODES (URPF_MODE_STRICT+1)
extern u8 *format_urpf_mode (u8 * s, va_list * a);
extern void urpf_update (urpf_mode_t mode,
u32 sw_if_index,
ip_address_family_t af, vlib_dir_t dir);
#endif
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

108
src/plugins/urpf/urpf_api.c Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2016 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.
*/
#include <urpf/urpf.h>
#include <vnet/plugin/plugin.h>
#include <vnet/ip/ip_types_api.h>
#include <vpp/app/version.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
/* define message IDs */
#include <vnet/format_fns.h>
#include <urpf/urpf.api_enum.h>
#include <urpf/urpf.api_types.h>
/**
* Base message ID fot the plugin
*/
static u32 urpf_base_msg_id;
#define REPLY_MSG_ID_BASE urpf_base_msg_id
#include <vlibapi/api_helper_macros.h>
static int
urpf_mode_decode (vl_api_urpf_mode_t in, urpf_mode_t * out)
{
if (0)
;
#define _(a,b) \
else if (URPF_API_MODE_##a == in) \
{ \
*out = URPF_MODE_##a; \
return (0); \
}
foreach_urpf_mode
#undef _
return (VNET_API_ERROR_INVALID_VALUE);
}
static void
vl_api_urpf_update_t_handler (vl_api_urpf_update_t * mp)
{
vl_api_urpf_update_reply_t *rmp;
ip_address_family_t af;
urpf_mode_t mode;
int rv = 0;
VALIDATE_SW_IF_INDEX (mp);
rv = urpf_mode_decode (mp->mode, &mode);
if (rv)
goto done;
rv = ip_address_family_decode (mp->af, &af);
if (rv)
goto done;
urpf_update (mode, htonl (mp->sw_if_index), af,
(mp->is_input ? VLIB_RX : VLIB_TX));
BAD_SW_IF_INDEX_LABEL;
done:
REPLY_MACRO (VL_API_URPF_UPDATE_REPLY);
}
#include <urpf/urpf.api.c>
static clib_error_t *
urpf_api_init (vlib_main_t * vm)
{
/* Ask for a correctly-sized block of API message decode slots */
urpf_base_msg_id = setup_message_id_table ();
return 0;
}
VLIB_INIT_FUNCTION (urpf_api_init);
/* *INDENT-OFF* */
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
.description = "Unicast Reverse Path Forwarding (uRPF)",
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

331
src/plugins/urpf/urpf_dp.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -440,7 +440,6 @@ list(APPEND VNET_SOURCES
ip/ip4_mtrie.c
ip/ip4_pg.c
ip/ip4_source_and_port_range_check.c
ip/ip4_source_check.c
ip/reass/ip4_full_reass.c
ip/reass/ip4_sv_reass.c
ip/ip6_format.c
@ -470,7 +469,6 @@ list(APPEND VNET_SOURCES
)
list(APPEND VNET_MULTIARCH_SOURCES
ip/ip4_source_check.c
ip/ip4_punt_drop.c
ip/reass/ip4_full_reass.c
ip/ip6_hop_by_hop.c

View File

@ -172,6 +172,30 @@ ip4_fib_forwarding_lookup (u32 fib_index,
return (ip4_fib_mtrie_leaf_get_adj_index(leaf));
}
static_always_inline void
ip4_fib_forwarding_lookup_x2 (u32 fib_index0,
u32 fib_index1,
const ip4_address_t * addr0,
const ip4_address_t * addr1,
index_t *lb0,
index_t *lb1)
{
ip4_fib_mtrie_leaf_t leaf[2];
ip4_fib_mtrie_t * mtrie[2];
mtrie[0] = &ip4_fib_get(fib_index0)->mtrie;
mtrie[1] = &ip4_fib_get(fib_index1)->mtrie;
leaf[0] = ip4_fib_mtrie_lookup_step_one (mtrie[0], addr0);
leaf[1] = ip4_fib_mtrie_lookup_step_one (mtrie[1], addr1);
leaf[0] = ip4_fib_mtrie_lookup_step (mtrie[0], leaf[0], addr0, 2);
leaf[1] = ip4_fib_mtrie_lookup_step (mtrie[1], leaf[1], addr1, 2);
leaf[0] = ip4_fib_mtrie_lookup_step (mtrie[0], leaf[0], addr0, 3);
leaf[1] = ip4_fib_mtrie_lookup_step (mtrie[1], leaf[1], addr1, 3);
*lb0 = ip4_fib_mtrie_leaf_get_adj_index(leaf[0]);
*lb1 = ip4_fib_mtrie_leaf_get_adj_index(leaf[1]);
}
#endif

View File

@ -0,0 +1,88 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* 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.
*/
typedef u32 interface_index;
enum if_status_flags
{
IF_STATUS_API_FLAG_ADMIN_UP = 1,
IF_STATUS_API_FLAG_LINK_UP = 2,
};
/* Per protocol MTU */
enum mtu_proto
{
MTU_PROTO_API_L3, /* Default payload MTU (without L2 headers) */
MTU_PROTO_API_IP4, /* Per-protocol MTUs overriding default */
MTU_PROTO_API_IP6,
MTU_PROTO_API_MPLS,
MTU_PROTO_API_N,
};
enum link_duplex
{
LINK_DUPLEX_API_UNKNOWN = 0,
LINK_DUPLEX_API_HALF = 1,
LINK_DUPLEX_API_FULL = 2,
};
enum sub_if_flags
{
SUB_IF_API_FLAG_NO_TAGS = 1,
SUB_IF_API_FLAG_ONE_TAG = 2,
SUB_IF_API_FLAG_TWO_TAGS = 4,
SUB_IF_API_FLAG_DOT1AD = 8,
SUB_IF_API_FLAG_EXACT_MATCH = 16,
SUB_IF_API_FLAG_DEFAULT = 32,
SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY = 64,
SUB_IF_API_FLAG_INNER_VLAN_ID_ANY = 128,
SUB_IF_API_FLAG_MASK_VNET = 254, /* use with vnet_sub_interface_t raw_flags */
SUB_IF_API_FLAG_DOT1AH = 256,
};
enum rx_mode
{
RX_MODE_API_UNKNOWN = 0,
RX_MODE_API_POLLING,
RX_MODE_API_INTERRUPT,
RX_MODE_API_ADAPTIVE,
RX_MODE_API_DEFAULT,
};
enum if_type
{
/* A hw interface. */
IF_API_TYPE_HARDWARE,
/* A sub-interface. */
IF_API_TYPE_SUB,
IF_API_TYPE_P2P,
IF_API_TYPE_PIPE,
};
enum direction:u8
{
DIRECTION_RX,
DIRECTION_TX,
};
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -509,22 +509,6 @@ autoreply define ip_source_and_port_range_check_interface_add_del
u32 udp_out_vrf_id;
};
/** \brief Set interface source check request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param is_add - add or del
@param loose - strict or loose
@param sw_if_index - interface index
*/
autoreply define ip_source_check_interface_add_del
{
u32 client_index;
u32 context;
bool is_add [default=true];
bool loose;
vl_api_interface_index_t sw_if_index;
};
/** \brief IPv6 set link local address on interface request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request

View File

@ -67,9 +67,6 @@
_ (UDP_CHECKSUM, "bad udp checksum") \
_ (UDP_LENGTH, "inconsistent udp/ip lengths") \
\
/* Errors signalled by ip4-source-check. */ \
_ (UNICAST_SOURCE_CHECK_FAILS, "ip4 unicast source check fails") \
\
/* Spoofed packets in ip4-rewrite-local */ \
_ (SPOOFED_LOCAL_PACKETS, "ip4 spoofed local-address packet drops") \
\

View File

@ -903,20 +903,6 @@ VNET_FEATURE_INIT (ip4_inacl, static) =
{
.arc_name = "ip4-unicast",
.node_name = "ip4-inacl",
.runs_before = VNET_FEATURES ("ip4-source-check-via-rx"),
};
VNET_FEATURE_INIT (ip4_source_check_1, static) =
{
.arc_name = "ip4-unicast",
.node_name = "ip4-source-check-via-rx",
.runs_before = VNET_FEATURES ("ip4-source-check-via-any"),
};
VNET_FEATURE_INIT (ip4_source_check_2, static) =
{
.arc_name = "ip4-unicast",
.node_name = "ip4-source-check-via-any",
.runs_before = VNET_FEATURES ("ip4-policer-classify"),
};

View File

@ -436,9 +436,6 @@ ip4_init (vlib_main_t * vm)
if ((error = vlib_call_init_function (vm, ip4_cli_init)))
return error;
if ((error = vlib_call_init_function (vm, ip4_source_check_init)))
return error;
if ((error = vlib_call_init_function
(vm, ip4_source_and_port_range_check_init)))
return error;

View File

@ -384,6 +384,12 @@ ip4_address_is_multicast (const ip4_address_t * a)
return (a->data[0] & 0xf0) == 0xe0;
}
always_inline uword
ip4_address_is_global_broadcast (const ip4_address_t * a)
{
return (a->as_u32) == 0xffffffff;
}
always_inline void
ip4_multicast_address_set_for_group (ip4_address_t * a,
ip_multicast_group_t g)

File diff suppressed because it is too large Load Diff

View File

@ -94,8 +94,6 @@ _(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL, \
ip_source_and_port_range_check_add_del) \
_(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL, \
ip_source_and_port_range_check_interface_add_del) \
_(IP_SOURCE_CHECK_INTERFACE_ADD_DEL, \
ip_source_check_interface_add_del) \
_(SW_INTERFACE_IP6_SET_LINK_LOCAL_ADDRESS, \
sw_interface_ip6_set_link_local_address) \
_(IP_REASSEMBLY_SET, ip_reassembly_set) \
@ -1270,37 +1268,6 @@ static void
REPLY_MACRO (VL_API_SW_INTERFACE_IP6_SET_LINK_LOCAL_ADDRESS_REPLY);
}
typedef union
{
u32 fib_index;
} ip4_source_check_config_t;
static void
vl_api_ip_source_check_interface_add_del_t_handler
(vl_api_ip_source_check_interface_add_del_t * mp)
{
vl_api_ip_source_check_interface_add_del_reply_t *rmp;
int rv;
u32 sw_if_index = ntohl (mp->sw_if_index);
u8 is_add = mp->is_add;
char *feature_name =
mp->loose ? "ip4-source-check-via-any" : "ip4-source-check-via-rx";
ip4_source_check_config_t config;
VALIDATE_SW_IF_INDEX (mp);
config.fib_index =
fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
rv =
vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index,
is_add, &config, sizeof (config));
BAD_SW_IF_INDEX_LABEL;
REPLY_MACRO (VL_API_IP_SOURCE_CHECK_INTERFACE_ADD_DEL_REPLY);
}
static void
vl_api_ip_table_replace_begin_t_handler (vl_api_ip_table_replace_begin_t * mp)
{