From d724e4f43b1d143a3f35492bce72fdc3e8af553e Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Thu, 2 Apr 2020 15:02:16 +0000 Subject: 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 --- src/plugins/urpf/CMakeLists.txt | 27 +++ src/plugins/urpf/ip4_urpf.c | 171 +++++++++++++++++++ src/plugins/urpf/ip6_urpf.c | 171 +++++++++++++++++++ src/plugins/urpf/test/test_urpf.py | 295 +++++++++++++++++++++++++++++++++ src/plugins/urpf/urpf.api | 59 +++++++ src/plugins/urpf/urpf.c | 323 ++++++++++++++++++++++++++++++++++++ src/plugins/urpf/urpf.h | 50 ++++++ src/plugins/urpf/urpf_api.c | 108 ++++++++++++ src/plugins/urpf/urpf_dp.h | 331 +++++++++++++++++++++++++++++++++++++ 9 files changed, 1535 insertions(+) create mode 100644 src/plugins/urpf/CMakeLists.txt create mode 100644 src/plugins/urpf/ip4_urpf.c create mode 100644 src/plugins/urpf/ip6_urpf.c create mode 100644 src/plugins/urpf/test/test_urpf.py create mode 100644 src/plugins/urpf/urpf.api create mode 100644 src/plugins/urpf/urpf.c create mode 100644 src/plugins/urpf/urpf.h create mode 100644 src/plugins/urpf/urpf_api.c create mode 100644 src/plugins/urpf/urpf_dp.h (limited to 'src/plugins') diff --git a/src/plugins/urpf/CMakeLists.txt b/src/plugins/urpf/CMakeLists.txt new file mode 100644 index 00000000000..2f44e3b2344 --- /dev/null +++ b/src/plugins/urpf/CMakeLists.txt @@ -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 +) diff --git a/src/plugins/urpf/ip4_urpf.c b/src/plugins/urpf/ip4_urpf.c new file mode 100644 index 00000000000..25b6c94bde3 --- /dev/null +++ b/src/plugins/urpf/ip4_urpf.c @@ -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 +#include + +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: + */ diff --git a/src/plugins/urpf/ip6_urpf.c b/src/plugins/urpf/ip6_urpf.c new file mode 100644 index 00000000000..3a94a456cb7 --- /dev/null +++ b/src/plugins/urpf/ip6_urpf.c @@ -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 +#include + +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: + */ diff --git a/src/plugins/urpf/test/test_urpf.py b/src/plugins/urpf/test/test_urpf.py new file mode 100644 index 00000000000..64b246cd663 --- /dev/null +++ b/src/plugins/urpf/test/test_urpf.py @@ -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) diff --git a/src/plugins/urpf/urpf.api b/src/plugins/urpf/urpf.api new file mode 100644 index 00000000000..944db08cc94 --- /dev/null +++ b/src/plugins/urpf/urpf.api @@ -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: + */ diff --git a/src/plugins/urpf/urpf.c b/src/plugins/urpf/urpf.c new file mode 100644 index 00000000000..7e1986a4250 --- /dev/null +++ b/src/plugins/urpf/urpf.c @@ -0,0 +1,323 @@ +/* + * 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. + */ + +#include + +#include + +/* *INDENT-OFF* */ +static const char *urpf_feat_arcs[N_AF][VLIB_N_DIR] = +{ + [AF_IP4] = { + [VLIB_RX] = "ip4-unicast", + [VLIB_TX] = "ip4-output", + }, + [AF_IP6] = { + [VLIB_RX] = "ip6-unicast", + [VLIB_TX] = "ip6-output", + }, +}; + +static const char *urpf_feats[N_AF][VLIB_N_DIR][URPF_N_MODES] = +{ + [AF_IP4] = { + [VLIB_RX] = { + [URPF_MODE_STRICT] = "ip4-rx-urpf-strict", + [URPF_MODE_LOOSE] = "ip4-rx-urpf-loose", + }, + [VLIB_TX] = { + [URPF_MODE_STRICT] = "ip4-tx-urpf-strict", + [URPF_MODE_LOOSE] = "ip4-tx-urpf-loose", + }, + }, + [AF_IP6] = { + [VLIB_RX] = { + [URPF_MODE_STRICT] = "ip6-rx-urpf-strict", + [URPF_MODE_LOOSE] = "ip6-rx-urpf-loose", + }, + [VLIB_TX] = { + [URPF_MODE_STRICT] = "ip6-tx-urpf-strict", + [URPF_MODE_LOOSE] = "ip6-tx-urpf-loose", + }, + }, +}; +/* *INDENT-ON* */ + +/** + * Per-af, per-direction, per-interface uRPF configs + */ +static urpf_mode_t *urpf_cfgs[N_AF][VLIB_N_DIR]; + +u8 * +format_urpf_mode (u8 * s, va_list * a) +{ + urpf_mode_t mode = va_arg (*a, int); + + switch (mode) + { +#define _(a,b) \ + case URPF_MODE_##a: \ + return (format (s, "%s", b)); + foreach_urpf_mode +#undef _ + } + + return (format (s, "unknown")); +} + +static uword +unformat_urpf_mode (unformat_input_t * input, va_list * args) +{ + urpf_mode_t *mode = va_arg (*args, urpf_mode_t *); + + if (0) + ; +#define _(a,b) \ + else if (unformat (input, b)) \ + { \ + *mode = URPF_MODE_##a; \ + return (1); \ + } + foreach_urpf_mode +#undef _ + return 0; +} + +void +urpf_update (urpf_mode_t mode, + u32 sw_if_index, ip_address_family_t af, vlib_dir_t dir) +{ + urpf_mode_t old; + + vec_validate_init_empty (urpf_cfgs[af][dir], sw_if_index, URPF_MODE_OFF); + old = urpf_cfgs[af][dir][sw_if_index]; + + if (mode != old) + { + if (URPF_MODE_OFF != old) + /* disable what we have */ + vnet_feature_enable_disable (urpf_feat_arcs[af][dir], + urpf_feats[af][dir][old], + sw_if_index, 0, 0, 0); + + if (URPF_MODE_OFF != mode) + /* enable what's new */ + vnet_feature_enable_disable (urpf_feat_arcs[af][dir], + urpf_feats[af][dir][mode], + sw_if_index, 1, 0, 0); + } + /* else - no change to existing config */ + + urpf_cfgs[af][dir][sw_if_index] = mode; +} + +static clib_error_t * +urpf_cli_update (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = NULL; + ip_address_family_t af; + urpf_mode_t mode; + u32 sw_if_index; + vlib_dir_t dir; + + sw_if_index = ~0; + af = AF_IP4; + dir = VLIB_RX; + mode = URPF_MODE_STRICT; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "%U", unformat_urpf_mode, &mode)) + ; + else if (unformat (line_input, "%U", unformat_ip_address_family, &af)) + ; + else if (unformat (line_input, "%U", unformat_vlib_rx_tx, &dir)) + ; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (~0 == sw_if_index) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, line_input); + goto done; + } + + urpf_update (mode, sw_if_index, af, dir); +done: + unformat_free (line_input); + + return error; +} + +/*? + * This command configures uRPF on an interface. + * Two flavours are supported (the default is strict): + * - loose: accept ingress packet if there is a route to reach the source + * - strict: accept ingress packet if it arrived on an interface which + * the route to the source uses. i.e. an interface that the source + * is reachable via. + * + * @cliexpar + * @parblock + * Example of graph node before range checking is enabled: + * @cliexstart{show vlib graph ip4-rx-urpf-strict} + * Name Next Previous + * ip4-rx-urpf-strict ip4-drop [0] + * @cliexend + * + * Example of how to enable unicast source checking on an interface: + * @cliexcmd{set urpf ip4 rx GigabitEthernet2/0/0 loose} + * + * Example of graph node after range checking is enabled: + * @cliexstart{show vlib graph ip4-rx-urpf-loose} + * Name Next Previous + * ip4-rx-urpf-loose ip4-drop [0] ip4-input-no-checksum + * ip4-source-and-port-range- ip4-input + * @cliexend + * + * Example of how to display the feature enabed on an interface: + * @cliexstart{show ip interface features GigabitEthernet2/0/0} + * IP feature paths configured on GigabitEthernet2/0/0... + * + * ipv4 unicast: + * ip4-rx-urpf-loose + * ip4-lookup + * + * ipv4 multicast: + * ip4-lookup-multicast + * + * ipv4 multicast: + * interface-output + * + * ipv6 unicast: + * ip6-lookup + * + * ipv6 multicast: + * ip6-lookup + * + * ipv6 multicast: + * interface-output + * @cliexend + * + * Example of how to disable unicast source checking on an interface: + * @cliexcmd{set urpf ip4 off GigabitEthernet2/0/0} + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip_source_check_command, static) = { + .path = "set urpf", + .function = urpf_cli_update, + .short_help = "set urpf [ip4|ip6] [rx|tx] [off|strict|loose] ", +}; +/* *INDENT-ON* */ + +static clib_error_t * +urpf_cli_accept (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = NULL; + fib_prefix_t fpfx; + ip_prefix_t pfx; + u32 table_id, is_add, fib_index; + + is_add = 1; + table_id = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "table %d", &table_id)) + ; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else if (unformat (line_input, "%U", unformat_ip_prefix, &pfx)) + ; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + ip_prefix_to_fib_prefix (&pfx, &fpfx); + + fib_index = fib_table_find (fpfx.fp_proto, table_id); + + if (~0 == fib_index) + { + error = clib_error_return (0, "Nonexistent table id %d", table_id); + goto done; + } + + if (is_add) + fib_table_entry_special_add (fib_index, + &fpfx, + FIB_SOURCE_URPF_EXEMPT, FIB_ENTRY_FLAG_DROP); + else + fib_table_entry_special_remove (fib_index, &fpfx, FIB_SOURCE_URPF_EXEMPT); + +done: + unformat_free (line_input); + + return (error); +} + +/*? + * Add an exemption for a prefix to pass the Unicast Reverse Path + * Forwarding (uRPF) loose check. This is for testing purposes only. + * If the 'table' is not enter it is defaulted to 0. Default + * is to 'add'. VPP always performs a loose uRPF check for + * for-us traffic. + * + * @cliexpar + * Example of how to add a uRPF exception to a FIB table to pass the + * loose RPF tests: + * @cliexcmd{set urpf-accept table 7 10.0.0.0/8 add} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (urpf_accept_command, static) = { + .path = "set urpf-accept", + .function = urpf_cli_accept, + .short_help = "urpf-accept [table ] [add|del] ", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/urpf/urpf.h b/src/plugins/urpf/urpf.h new file mode 100644 index 00000000000..941cda25f4b --- /dev/null +++ b/src/plugins/urpf/urpf.h @@ -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 + +#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: + */ diff --git a/src/plugins/urpf/urpf_api.c b/src/plugins/urpf/urpf_api.c new file mode 100644 index 00000000000..ad060399347 --- /dev/null +++ b/src/plugins/urpf/urpf_api.c @@ -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 +#include +#include + +#include + +#include +#include + +/* define message IDs */ +#include +#include +#include + +/** + * Base message ID fot the plugin + */ +static u32 urpf_base_msg_id; +#define REPLY_MSG_ID_BASE urpf_base_msg_id + +#include + +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 + +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: + */ diff --git a/src/plugins/urpf/urpf_dp.h b/src/plugins/urpf/urpf_dp.h new file mode 100644 index 00000000000..3d3f19cfb1d --- /dev/null +++ b/src/plugins/urpf/urpf_dp.h @@ -0,0 +1,331 @@ +/* + * 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. + */ + +#ifndef __URPF_DP_H__ +#define __URPF_DP_H__ + +#include +#include +#include +#include + +#include + +/** + * @file + * @brief Unicast Reverse Path forwarding. + * + * This file contains the interface unicast source check. + */ +typedef struct +{ + index_t urpf; +} urpf_trace_t; + +static u8 * +format_urpf_trace (u8 * s, va_list * va) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); + urpf_trace_t *t = va_arg (*va, urpf_trace_t *); + + s = format (s, "uRPF:%d", t->urpf); + + return s; +} + +#define foreach_urpf_error \ + _(DROP, "uRPF Drop") \ + +typedef enum urpf_error_t_ +{ +#define _(a,b) URPF_ERROR_##a, + foreach_urpf_error +#undef _ + URPF_N_ERROR, +} urpf_error_t; + +typedef enum +{ + URPF_NEXT_DROP, + URPF_N_NEXT, +} urpf_next_t; + +static_always_inline uword +urpf_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + ip_address_family_t af, vlib_dir_t dir, urpf_mode_t mode) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u16 nexts[VLIB_FRAME_SIZE], *next; + u32 n_left, *from; + + from = vlib_frame_vector_args (frame); + n_left = frame->n_vectors; + b = bufs; + next = nexts; + + vlib_get_buffers (vm, from, bufs, n_left); + + while (n_left >= 4) + { + u32 pass0, lb_index0, pass1, lb_index1; + const load_balance_t *lb0, *lb1; + u32 fib_index0, fib_index1; + const u8 *h0, *h1; + + /* Prefetch next iteration. */ + { + vlib_prefetch_buffer_header (b[2], LOAD); + vlib_prefetch_buffer_header (b[3], LOAD); + vlib_prefetch_buffer_data (b[2], LOAD); + vlib_prefetch_buffer_data (b[3], LOAD); + } + + h0 = (u8 *) vlib_buffer_get_current (b[0]); + h1 = (u8 *) vlib_buffer_get_current (b[1]); + + if (VLIB_TX == dir) + { + h0 += vnet_buffer (b[0])->ip.save_rewrite_length; + h1 += vnet_buffer (b[1])->ip.save_rewrite_length; + } + + if (AF_IP4 == af) + { + const ip4_header_t *ip0, *ip1; + + ip0 = (ip4_header_t *) h0; + ip1 = (ip4_header_t *) h1; + + fib_index0 = ip4_main.fib_index_by_sw_if_index + [vnet_buffer (b[0])->sw_if_index[dir]]; + fib_index1 = ip4_main.fib_index_by_sw_if_index + [vnet_buffer (b[1])->sw_if_index[dir]]; + + ip4_fib_forwarding_lookup_x2 (fib_index0, + fib_index1, + &ip0->src_address, + &ip1->src_address, + &lb_index0, &lb_index1); + /* Pass multicast. */ + pass0 = (ip4_address_is_multicast (&ip0->src_address) || + ip4_address_is_global_broadcast (&ip0->src_address)); + pass1 = (ip4_address_is_multicast (&ip1->src_address) || + ip4_address_is_global_broadcast (&ip1->src_address)); + } + else + { + const ip6_header_t *ip0, *ip1; + + fib_index0 = ip6_main.fib_index_by_sw_if_index + [vnet_buffer (b[0])->sw_if_index[dir]]; + fib_index1 = ip6_main.fib_index_by_sw_if_index + [vnet_buffer (b[1])->sw_if_index[dir]]; + + ip0 = (ip6_header_t *) h0; + ip1 = (ip6_header_t *) h1; + + lb_index0 = ip6_fib_table_fwding_lookup (fib_index0, + &ip0->src_address); + lb_index1 = ip6_fib_table_fwding_lookup (fib_index1, + &ip1->src_address); + pass0 = ip6_address_is_multicast (&ip0->src_address); + pass1 = ip6_address_is_multicast (&ip1->src_address); + } + + lb0 = load_balance_get (lb_index0); + lb1 = load_balance_get (lb_index1); + + if (URPF_MODE_STRICT == mode) + { + /* for RX the check is: would this source adddress be forwarded + * out of the interface on which it was recieved, if yes allow. + * For TX it's; would this source addres be forwarded out of the + * interface through which it is being sent, if yes drop. + */ + int res0, res1; + + res0 = fib_urpf_check (lb0->lb_urpf, + vnet_buffer (b[0])->sw_if_index[dir]); + res1 = fib_urpf_check (lb1->lb_urpf, + vnet_buffer (b[1])->sw_if_index[dir]); + + if (VLIB_RX == dir) + { + pass0 |= res0; + pass1 |= res1; + } + else + { + pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf); + pass1 |= !res1 && fib_urpf_check_size (lb1->lb_urpf); + } + } + else + { + pass0 |= fib_urpf_check_size (lb0->lb_urpf); + pass1 |= fib_urpf_check_size (lb1->lb_urpf); + } + + if (PREDICT_TRUE (pass0)) + vnet_feature_next_u16 (&next[0], b[0]); + else + { + next[0] = URPF_NEXT_DROP; + b[0]->error = node->errors[URPF_ERROR_DROP]; + } + if (PREDICT_TRUE (pass1)) + vnet_feature_next_u16 (&next[1], b[1]); + else + { + next[1] = URPF_NEXT_DROP; + b[1]->error = node->errors[URPF_ERROR_DROP]; + } + + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + urpf_trace_t *t; + + t = vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->urpf = lb0->lb_urpf; + } + if (b[1]->flags & VLIB_BUFFER_IS_TRACED) + { + urpf_trace_t *t; + + t = vlib_add_trace (vm, node, b[1], sizeof (*t)); + t->urpf = lb1->lb_urpf; + } + + b += 2; + next += 2; + n_left -= 2; + } + + while (n_left) + { + u32 pass0, lb_index0, fib_index0; + const load_balance_t *lb0; + const u8 *h0; + + h0 = (u8 *) vlib_buffer_get_current (b[0]); + + if (VLIB_TX == dir) + h0 += vnet_buffer (b[0])->ip.save_rewrite_length; + + if (AF_IP4 == af) + { + const ip4_header_t *ip0; + + fib_index0 = ip4_main.fib_index_by_sw_if_index + [vnet_buffer (b[0])->sw_if_index[dir]]; + ip0 = (ip4_header_t *) h0; + + lb_index0 = ip4_fib_forwarding_lookup (fib_index0, + &ip0->src_address); + + /* Pass multicast. */ + pass0 = (ip4_address_is_multicast (&ip0->src_address) || + ip4_address_is_global_broadcast (&ip0->src_address)); + } + else + { + const ip6_header_t *ip0; + + ip0 = (ip6_header_t *) h0; + fib_index0 = ip6_main.fib_index_by_sw_if_index + [vnet_buffer (b[0])->sw_if_index[dir]]; + + lb_index0 = ip6_fib_table_fwding_lookup (fib_index0, + &ip0->src_address); + pass0 = ip6_address_is_multicast (&ip0->src_address); + } + + lb0 = load_balance_get (lb_index0); + + if (URPF_MODE_STRICT == mode) + { + int res0; + + res0 = fib_urpf_check (lb0->lb_urpf, + vnet_buffer (b[0])->sw_if_index[dir]); + if (VLIB_RX == dir) + pass0 |= res0; + else + pass0 |= !res0 && fib_urpf_check_size (lb0->lb_urpf); + } + else + pass0 |= fib_urpf_check_size (lb0->lb_urpf); + + if (PREDICT_TRUE (pass0)) + vnet_feature_next_u16 (&next[0], b[0]); + else + { + next[0] = URPF_NEXT_DROP; + b[0]->error = node->errors[URPF_ERROR_DROP]; + } + + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + urpf_trace_t *t; + + t = vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->urpf = lb0->lb_urpf; + } + b++; + next++; + n_left--; + } + + vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); + + return frame->n_vectors; +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg