Skip to content

Invalid ct_ipv6_src / ct_ipv6_dst PacketIn match metadata for ICMPv6 #327

Closed
@antoninbas

Description

@antoninbas

We ran into a surprising issue when using conntrack in OVS with ICMPv6 packets. When using the ct action on a packet and punting the forked packet to the controller, the ct_ipv6_src and ct_ipv6_dst match fields included in the metadata are incorrect, but only for ICMPv6 packets.

To reproduce on a standalone OVS instance, I used the following flows:

$ ovs-ofctl dump-flows br0 -O Openflow15 --no-stats
 priority=10,ip actions=ct(table=1)
 priority=10,ipv6 actions=ct(table=2)
 priority=0 actions=CONTROLLER:128
 table=1, priority=10,ct_state=+new+trk,ip actions=CONTROLLER:128
 table=2, priority=10,ct_state=+new+trk,ipv6 actions=CONTROLLER:128

To show the PacketIns, I used the ovs-testcontroller.
Easiest way to reproduce is as follows:

cat <<EOF > flows.txt
table=0,priority=10,ip,actions=ct(table=1)
table=0,priority=10,ipv6,actions=ct(table=2)
table=1,priority=10,ip,ct_state=+new+trk,actions=CONTROLLER:128
table=2,priority=10,ipv6,ct_state=+new+trk,actions=CONTROLLER:128
EOF

ovs-vsctl add-br br0
ovs-testcontroller unix:/var/run/openvswitch/br0.mgmt -v -n --with-flows flows.txt

After that, you will need to inject the right packets. I used a veth pair and scapy to send packets from a separate network namespace, but I imagine there are plenty of options to achieve the same thing.

The snippet below shows the Scapy commands used to craft and send the packet, and the PacketIn message logged by the ovs-testcontroller.

# 1. ICMPv4 packet
>>> p1 = Ether()/IP(src="10.0.0.1",dst="10.0.0.2")/ICMP(type=8)
>>> sendp(p1, iface="veth0")

2024-05-07T03:51:37Z|00027|vconn|DBG|unix:/var/run/openvswitch/br0.mgmt: received: OFPT_PACKET_IN (OF1.5) (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=new|trk,ct_nw_src=10.0.0.1,ct_nw_dst=10.0.0.2,ct_nw_proto=1,ct_tp_src=8,ct_tp_dst=0,ip,in_port=1 (via action) data_len=42 (unbuffered)

# 2. TCPv6 packet
>>> p2 = Ether()/IPv6(src="2001:db8:dead::1",dst="2001:db8:dead::2")/TCP()
>>> sendp(p2, iface="veth0")

2024-05-07T03:53:13Z|00032|vconn|DBG|unix:/var/run/openvswitch/br0.mgmt: received: OFPT_PACKET_IN (OF1.5) (xid=0x0): table_id=2 cookie=0x0 total_len=74 ct_state=new|trk,ct_ipv6_src=2001:db8:dead::1,ct_ipv6_dst=2001:db8:dead::2,ct_nw_proto=6,ct_tp_src=20,ct_tp_dst=80,ipv6,in_port=1 (via action) data_len=74 (unbuffered)

# 3. ICMPv6 packet
>>> p3 = Ether()/IPv6(src="2001:db8:dead::1",dst="2001:db8:dead::2")/ICMPv6EchoRequest()
>>> sendp(p3, iface="veth0")

2024-05-07T03:53:57Z|00037|vconn|DBG|unix:/var/run/openvswitch/br0.mgmt: received: OFPT_PACKET_IN (OF1.5) (xid=0x0): table_id=2 cookie=0x0 total_len=62 ct_state=new|trk,ct_ipv6_src=::,ct_ipv6_dst=::2,ct_nw_proto=58,ct_tp_src=128,ct_tp_dst=0,ipv6,in_port=1 (via action) data_len=62 (unbuffered)

Note that the ICMPv6 packet is a "ping", and not an NDP packet.

You can see above that for the ICMPv6 packet, we have ct_ipv6_src=::,ct_ipv6_dst=::2. This is not what we'd expect. We'd expect the same match value as for the TCPv6 packet: ct_ipv6_src=2001:db8:dead::1,ct_ipv6_dst=2001:db8:dead::2.

In this example, we don't commit the connection for the sake of simplicity. But note that if we were to commit the connection and use conntrack -L to dump the connection tracking table, we would get the excepted entry, with the correct origin tuple (this is something I verified). So this does seem to be specific to OVS.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions