ip: The check for 'same packet' must include the FIB index
Type: fix otherwise if two packets arrive with the same source address but from different VRFs, then they are treated as the same and they use the same LB and thus share the same fate. but the lookup, when done, results in two different LBs, and hence the fate can be different. Signed-off-by: Neale Ranns <neale@graphiant.com> Change-Id: Id6e16f7c577a561d9ddd7066339fa4385361d07f
This commit is contained in:

committed by
Florin Coras

parent
e99f762346
commit
aa7cfd04e7
@ -1517,7 +1517,9 @@ ip4_local_set_next_and_error (vlib_node_runtime_t * error_node,
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
/* The src and fib-index together determine if packet n is the same as n-1 */
|
||||||
ip4_address_t src;
|
ip4_address_t src;
|
||||||
|
u32 fib_index;
|
||||||
u32 lbi;
|
u32 lbi;
|
||||||
u8 error;
|
u8 error;
|
||||||
u8 first;
|
u8 first;
|
||||||
@ -1551,7 +1553,8 @@ ip4_local_check_src (vlib_buffer_t *b, ip4_header_t *ip0,
|
|||||||
* vnet_buffer()->ip.adj_index[VLIB_TX] will be set to the index of the
|
* vnet_buffer()->ip.adj_index[VLIB_TX] will be set to the index of the
|
||||||
* adjacency for the source address (the remote sender's address)
|
* adjacency for the source address (the remote sender's address)
|
||||||
*/
|
*/
|
||||||
if (PREDICT_TRUE (last_check->src.as_u32 != ip0->src_address.as_u32) ||
|
if (PREDICT_TRUE ((last_check->src.as_u32 != ip0->src_address.as_u32)) ||
|
||||||
|
(last_check->fib_index != vnet_buffer (b)->ip.fib_index) ||
|
||||||
last_check->first)
|
last_check->first)
|
||||||
{
|
{
|
||||||
lbi0 = ip4_fib_forwarding_lookup (vnet_buffer (b)->ip.fib_index,
|
lbi0 = ip4_fib_forwarding_lookup (vnet_buffer (b)->ip.fib_index,
|
||||||
@ -1587,6 +1590,7 @@ ip4_local_check_src (vlib_buffer_t *b, ip4_header_t *ip0,
|
|||||||
last_check->lbi = lbi0;
|
last_check->lbi = lbi0;
|
||||||
last_check->error = *error0;
|
last_check->error = *error0;
|
||||||
last_check->first = 0;
|
last_check->first = 0;
|
||||||
|
last_check->fib_index = vnet_buffer (b)->ip.fib_index;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1621,6 +1625,9 @@ ip4_local_check_src_x2 (vlib_buffer_t **b, ip4_header_t **ip,
|
|||||||
vnet_buffer (b[1])->sw_if_index[VLIB_TX] :
|
vnet_buffer (b[1])->sw_if_index[VLIB_TX] :
|
||||||
vnet_buffer (b[1])->ip.fib_index;
|
vnet_buffer (b[1])->ip.fib_index;
|
||||||
|
|
||||||
|
not_last_hit |= vnet_buffer (b[0])->ip.fib_index ^ last_check->fib_index;
|
||||||
|
not_last_hit |= vnet_buffer (b[1])->ip.fib_index ^ last_check->fib_index;
|
||||||
|
|
||||||
if (is_receive_dpo)
|
if (is_receive_dpo)
|
||||||
{
|
{
|
||||||
const receive_dpo_t *rd0, *rd1;
|
const receive_dpo_t *rd0, *rd1;
|
||||||
@ -1683,6 +1690,7 @@ ip4_local_check_src_x2 (vlib_buffer_t **b, ip4_header_t **ip,
|
|||||||
last_check->lbi = lbi[1];
|
last_check->lbi = lbi[1];
|
||||||
last_check->error = error[1];
|
last_check->error = error[1];
|
||||||
last_check->first = 0;
|
last_check->first = 0;
|
||||||
|
last_check->fib_index = vnet_buffer (b[1])->ip.fib_index;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1752,10 +1760,11 @@ ip4_local_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
|
|||||||
* member to make sure the .lbi is initialised for the first
|
* member to make sure the .lbi is initialised for the first
|
||||||
* packet.
|
* packet.
|
||||||
*/
|
*/
|
||||||
.src = {.as_u32 = 0},
|
.src = { .as_u32 = 0 },
|
||||||
.lbi = ~0,
|
.lbi = ~0,
|
||||||
.error = IP4_ERROR_UNKNOWN_PROTOCOL,
|
.error = IP4_ERROR_UNKNOWN_PROTOCOL,
|
||||||
.first = 1,
|
.first = 1,
|
||||||
|
.fib_index = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
from = vlib_frame_vector_args (frame);
|
from = vlib_frame_vector_args (frame);
|
||||||
|
181
test/test_ip4.py
181
test/test_ip4.py
@ -7,6 +7,7 @@ import unittest
|
|||||||
import scapy.compat
|
import scapy.compat
|
||||||
from scapy.contrib.mpls import MPLS
|
from scapy.contrib.mpls import MPLS
|
||||||
from scapy.layers.inet import IP, UDP, TCP, ICMP, icmptypes, icmpcodes
|
from scapy.layers.inet import IP, UDP, TCP, ICMP, icmptypes, icmpcodes
|
||||||
|
from scapy.layers.inet6 import IPv6
|
||||||
from scapy.layers.l2 import Ether, Dot1Q, ARP
|
from scapy.layers.l2 import Ether, Dot1Q, ARP
|
||||||
from scapy.packet import Raw
|
from scapy.packet import Raw
|
||||||
from six import moves
|
from six import moves
|
||||||
@ -1460,6 +1461,20 @@ class IPPuntSetup(object):
|
|||||||
|
|
||||||
self.vapi.set_punt(is_add=1, punt=punt_udp)
|
self.vapi.set_punt(is_add=1, punt=punt_udp)
|
||||||
|
|
||||||
|
af_ip6 = VppEnum.vl_api_address_family_t.ADDRESS_IP6
|
||||||
|
punt_udp = {
|
||||||
|
'type': pt_l4,
|
||||||
|
'punt': {
|
||||||
|
'l4': {
|
||||||
|
'af': af_ip6,
|
||||||
|
'protocol': udp_proto,
|
||||||
|
'port': 1236,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vapi.set_punt(is_add=1, punt=punt_udp)
|
||||||
|
|
||||||
self.pkt = (Ether(src=self.pg0.remote_mac,
|
self.pkt = (Ether(src=self.pg0.remote_mac,
|
||||||
dst=self.pg0.local_mac) /
|
dst=self.pg0.local_mac) /
|
||||||
IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
|
IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
|
||||||
@ -1569,6 +1584,172 @@ class TestIPPunt(IPPuntSetup, VppTestCase):
|
|||||||
self.send_and_expect(self.pg0, pkts, self.pg1)
|
self.send_and_expect(self.pg0, pkts, self.pg1)
|
||||||
ip_punt_redirect.remove_vpp_config()
|
ip_punt_redirect.remove_vpp_config()
|
||||||
|
|
||||||
|
def test_ip_punt_vrf(self):
|
||||||
|
""" IP punt/local with VRFs """
|
||||||
|
|
||||||
|
# use a punt redirect to test if for-us packets are accepted
|
||||||
|
pkts = self.pkt * 1025
|
||||||
|
|
||||||
|
vlans_pg0 = [VppDot1QSubint(self, self.pg0, v)
|
||||||
|
for v in range(100, 104)]
|
||||||
|
vlans_pg1 = [VppDot1QSubint(self, self.pg1, v)
|
||||||
|
for v in range(100, 104)]
|
||||||
|
tbl4 = [VppIpTable(self, v).add_vpp_config()
|
||||||
|
for v in range(100, 104)]
|
||||||
|
tbl6 = [VppIpTable(self, v, True).add_vpp_config()
|
||||||
|
for v in range(100, 104)]
|
||||||
|
|
||||||
|
for v in vlans_pg0 + vlans_pg1:
|
||||||
|
v.admin_up()
|
||||||
|
v.set_table_ip4(v.vlan)
|
||||||
|
v.set_table_ip6(v.vlan)
|
||||||
|
v.config_ip4()
|
||||||
|
v.config_ip6()
|
||||||
|
v.resolve_arp()
|
||||||
|
v.resolve_ndp()
|
||||||
|
|
||||||
|
[VppIpPuntRedirect
|
||||||
|
(self,
|
||||||
|
vlans_pg0[i].sw_if_index,
|
||||||
|
vlans_pg1[i].sw_if_index,
|
||||||
|
vlans_pg1[i].remote_ip4).add_vpp_config()
|
||||||
|
for i in range(4)]
|
||||||
|
[VppIpPuntRedirect
|
||||||
|
(self,
|
||||||
|
vlans_pg0[i].sw_if_index,
|
||||||
|
vlans_pg1[i].sw_if_index,
|
||||||
|
vlans_pg1[i].remote_ip6).add_vpp_config()
|
||||||
|
for i in range(4)]
|
||||||
|
|
||||||
|
pkts = [(Ether(src=self.pg0.remote_mac,
|
||||||
|
dst=self.pg0.local_mac) /
|
||||||
|
Dot1Q(vlan=i.vlan) /
|
||||||
|
IP(src=i.remote_ip4,
|
||||||
|
dst=i.local_ip4) /
|
||||||
|
UDP(sport=1234, dport=1234) /
|
||||||
|
Raw(b'\xa5' * 100))
|
||||||
|
for i in vlans_pg0]
|
||||||
|
|
||||||
|
self.send_and_expect(self.pg0, pkts, self.pg1)
|
||||||
|
|
||||||
|
#
|
||||||
|
# IPv4
|
||||||
|
#
|
||||||
|
|
||||||
|
# we reject packets for source addresses in the wrong vlan/VRF
|
||||||
|
pkts = [(Ether(src=self.pg0.remote_mac,
|
||||||
|
dst=self.pg0.local_mac) /
|
||||||
|
Dot1Q(vlan=i.vlan) /
|
||||||
|
IP(src="1.1.1.1",
|
||||||
|
dst=i.local_ip4) /
|
||||||
|
UDP(sport=1234, dport=1234) /
|
||||||
|
Raw(b'\xa5' * 100))
|
||||||
|
for i in vlans_pg0]
|
||||||
|
# single and dual loop
|
||||||
|
self.send_and_assert_no_replies(self.pg0, [pkts[0]])
|
||||||
|
self.send_and_assert_no_replies(self.pg0, pkts)
|
||||||
|
|
||||||
|
self.assert_error_counter_equal(
|
||||||
|
"/err/ip4-local/ip4 source lookup miss",
|
||||||
|
len(pkts) + 1)
|
||||||
|
|
||||||
|
# using the same source in different tables, should reject
|
||||||
|
# for the table that the source is not present in
|
||||||
|
# the first packet in the stream is drop
|
||||||
|
pkts = [(Ether(src=self.pg0.remote_mac,
|
||||||
|
dst=self.pg0.local_mac) /
|
||||||
|
Dot1Q(vlan=i.vlan) /
|
||||||
|
IP(src=vlans_pg0[0].remote_ip4,
|
||||||
|
dst=i.local_ip4) /
|
||||||
|
UDP(sport=1234, dport=1234) /
|
||||||
|
Raw(b'\xa5' * 100))
|
||||||
|
for i in vlans_pg0]
|
||||||
|
# single loop accept and drop
|
||||||
|
# followed by both in the same frame/loop
|
||||||
|
self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
|
||||||
|
self.send_and_assert_no_replies(self.pg0, [pkts[1]])
|
||||||
|
self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
|
||||||
|
|
||||||
|
# using the same source in different tables, should reject
|
||||||
|
# for the table that the source is not present in
|
||||||
|
# the first packet in the stream is accept
|
||||||
|
pkts = [(Ether(src=self.pg0.remote_mac,
|
||||||
|
dst=self.pg0.local_mac) /
|
||||||
|
Dot1Q(vlan=i.vlan) /
|
||||||
|
IP(src=vlans_pg0[3].remote_ip4,
|
||||||
|
dst=i.local_ip4) /
|
||||||
|
UDP(sport=1234, dport=1234) /
|
||||||
|
Raw(b'\xa5' * 100))
|
||||||
|
for i in vlans_pg0]
|
||||||
|
|
||||||
|
# single loop accept and drop
|
||||||
|
# followed by both in the same frame/loop
|
||||||
|
self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
|
||||||
|
self.send_and_assert_no_replies(self.pg0, [pkts[1]])
|
||||||
|
self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
|
||||||
|
|
||||||
|
#
|
||||||
|
# IPv6
|
||||||
|
#
|
||||||
|
|
||||||
|
# we reject packets for source addresses in the wrong vlan/VRF
|
||||||
|
pkts = [(Ether(src=self.pg0.remote_mac,
|
||||||
|
dst=self.pg0.local_mac) /
|
||||||
|
Dot1Q(vlan=i.vlan) /
|
||||||
|
IPv6(src="1::1",
|
||||||
|
dst=i.local_ip6) /
|
||||||
|
UDP(sport=1236, dport=1236) /
|
||||||
|
Raw(b'\xa5' * 100))
|
||||||
|
for i in vlans_pg0]
|
||||||
|
# single and dual loop
|
||||||
|
self.send_and_assert_no_replies(self.pg0, [pkts[0]])
|
||||||
|
self.send_and_assert_no_replies(self.pg0, pkts)
|
||||||
|
|
||||||
|
self.assert_error_counter_equal(
|
||||||
|
"/err/ip6-input/ip6 source lookup miss",
|
||||||
|
len(pkts) + 1)
|
||||||
|
|
||||||
|
# using the same source in different tables, should reject
|
||||||
|
# for the table that the source is not present in
|
||||||
|
# the first packet in the stream is drop
|
||||||
|
pkts = [(Ether(src=self.pg0.remote_mac,
|
||||||
|
dst=self.pg0.local_mac) /
|
||||||
|
Dot1Q(vlan=i.vlan) /
|
||||||
|
IPv6(src=vlans_pg0[0].remote_ip6,
|
||||||
|
dst=i.local_ip6) /
|
||||||
|
UDP(sport=1236, dport=1236) /
|
||||||
|
Raw(b'\xa5' * 100))
|
||||||
|
for i in vlans_pg0]
|
||||||
|
# single loop accept and drop
|
||||||
|
# followed by both in the same frame/loop
|
||||||
|
self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
|
||||||
|
self.send_and_assert_no_replies(self.pg0, [pkts[1]])
|
||||||
|
self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
|
||||||
|
|
||||||
|
# using the same source in different tables, should reject
|
||||||
|
# for the table that the source is not present in
|
||||||
|
# the first packet in the stream is accept
|
||||||
|
pkts = [(Ether(src=self.pg0.remote_mac,
|
||||||
|
dst=self.pg0.local_mac) /
|
||||||
|
Dot1Q(vlan=i.vlan) /
|
||||||
|
IPv6(src=vlans_pg0[3].remote_ip6,
|
||||||
|
dst=i.local_ip6) /
|
||||||
|
UDP(sport=1236, dport=1236) /
|
||||||
|
Raw(b'\xa5' * 100))
|
||||||
|
for i in vlans_pg0]
|
||||||
|
|
||||||
|
# single loop accept and drop
|
||||||
|
# followed by both in the same frame/loop
|
||||||
|
self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
|
||||||
|
self.send_and_assert_no_replies(self.pg0, [pkts[1]])
|
||||||
|
self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
|
||||||
|
|
||||||
|
for v in vlans_pg0 + vlans_pg1:
|
||||||
|
v.unconfig_ip4()
|
||||||
|
v.unconfig_ip6()
|
||||||
|
v.set_table_ip4(0)
|
||||||
|
v.set_table_ip6(0)
|
||||||
|
|
||||||
def test_ip_punt_dump(self):
|
def test_ip_punt_dump(self):
|
||||||
""" IP4 punt redirect dump"""
|
""" IP4 punt redirect dump"""
|
||||||
|
|
||||||
|
@ -168,6 +168,7 @@ class VppIpPuntRedirect(VppObject):
|
|||||||
def add_vpp_config(self):
|
def add_vpp_config(self):
|
||||||
self._test.vapi.ip_punt_redirect(punt=self.encode(), is_add=True)
|
self._test.vapi.ip_punt_redirect(punt=self.encode(), is_add=True)
|
||||||
self._test.registry.register(self, self._test.logger)
|
self._test.registry.register(self, self._test.logger)
|
||||||
|
return self
|
||||||
|
|
||||||
def remove_vpp_config(self):
|
def remove_vpp_config(self):
|
||||||
self._test.vapi.ip_punt_redirect(punt=self.encode(), is_add=False)
|
self._test.vapi.ip_punt_redirect(punt=self.encode(), is_add=False)
|
||||||
|
Reference in New Issue
Block a user