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:
27
src/plugins/urpf/CMakeLists.txt
Normal file
27
src/plugins/urpf/CMakeLists.txt
Normal 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
171
src/plugins/urpf/ip4_urpf.c
Normal 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
171
src/plugins/urpf/ip6_urpf.c
Normal 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:
|
||||
*/
|
||||
295
src/plugins/urpf/test/test_urpf.py
Normal file
295
src/plugins/urpf/test/test_urpf.py
Normal 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
59
src/plugins/urpf/urpf.api
Normal 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
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
50
src/plugins/urpf/urpf.h
Normal 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
108
src/plugins/urpf/urpf_api.c
Normal 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
331
src/plugins/urpf/urpf_dp.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user