diff mbox series

[ovs-dev,2/3] northd: enable check_pkt_len for gw router

Message ID 9bce796dc23e2b58ee9aeab7244156b063379c5a.1621438083.git.lorenzo.bianconi@redhat.com
State Changes Requested
Headers show
Series Introduce check_pkt_len for ingress traffic | expand

Commit Message

Lorenzo Bianconi May 19, 2021, 3:35 p.m. UTC
As it is already done for distributed gw router scenario, introduce
check_pkt_len logical flows for gw router use case.

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 northd/ovn-northd.c |  31 ++++--
 tests/ovn.at        | 238 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 260 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index a642d9020..ec63eb75c 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -10511,17 +10511,30 @@  build_check_pkt_len_flows_for_lrouter(
         struct hmap *ports,
         struct ds *match, struct ds *actions)
 {
-    if (od->nbr) {
+    if (!od->nbr) {
+        return;
+    }
 
-        /* Packets are allowed by default. */
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 0, "1",
-                      "next;");
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 0, "1",
-                      "next;");
+    /* Packets are allowed by default. */
+    ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 0, "1",
+                  "next;");
+    ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 0, "1",
+                  "next;");
 
-        if (od->l3dgw_port && od->l3redirect_port) {
-            build_check_pkt_len_flows_for_lrp(od->l3dgw_port, lflows,
-                                              ports, match, actions);
+    if (od->l3dgw_port && od->l3redirect_port) {
+        /* gw router port */
+        build_check_pkt_len_flows_for_lrp(od->l3dgw_port, lflows,
+                                          ports, match, actions);
+    } else if (smap_get(&od->nbr->options, "chassis")) {
+        for (size_t i = 0; i < od->nbr->n_ports; i++) {
+            /* gw router */
+            struct ovn_port *rp = ovn_port_find(ports,
+                                                od->nbr->ports[i]->name);
+            if (!rp) {
+                continue;
+            }
+            build_check_pkt_len_flows_for_lrp(rp, lflows, ports, match,
+                                              actions);
         }
     }
 }
diff --git a/tests/ovn.at b/tests/ovn.at
index db877cb7e..248319262 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -16375,6 +16375,244 @@  OVN_CLEANUP([hv1])
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn -- gw router - check packet length - icmp defrag])
+AT_KEYWORDS([gwr-check_packet_length])
+ovn_start
+
+ovn-nbctl ls-add sw0
+ovn-nbctl lsp-add sw0 sw0-port1
+ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3 1000::3"
+
+ovn-nbctl create Logical_Router name=lr0 options:chassis="hv1"
+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::1/64
+ovn-nbctl lsp-add sw0 sw0-lr0
+ovn-nbctl lsp-set-type sw0-lr0 router
+ovn-nbctl lsp-set-addresses sw0-lr0 router
+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
+
+ovn-nbctl ls-add public
+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 2000::1/64
+ovn-nbctl lsp-add public public-lr0
+ovn-nbctl lsp-set-type public-lr0 router
+ovn-nbctl lsp-set-addresses public-lr0 router
+ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
+
+# localnet port
+ovn-nbctl lsp-add public ln-public
+ovn-nbctl lsp-set-type ln-public localnet
+ovn-nbctl lsp-set-addresses ln-public unknown
+ovn-nbctl lsp-set-options ln-public network_name=phys
+
+ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24
+ovn-nbctl lr-nat-add lr0 snat 2000::1 1000::/64
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=sw0-port1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+reset_pcap_file() {
+     local iface=$1
+     local pcap_file=$2
+     ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+ options:rxq_pcap=dummy-rx.pcap
+     rm -f ${pcap_file}*.pcap
+     ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+ options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
+test_ip_packet_larger() {
+    local mtu=$1
+
+    # Send ip packet from sw0-port1 to outside
+    src_mac="505400000001" # sw-port1 mac
+    dst_mac="00000000ff01" # sw0-lr0 mac (internal router leg)
+    src_ip=`ip_to_hex 10 0 0 3`
+    dst_ip=`ip_to_hex 172 168 0 3`
+    # Set the packet length to 118.
+    pkt_len=0076
+    packet=${dst_mac}${src_mac}08004500${pkt_len}000000004001c3d9
+    orig_packet_l3=${src_ip}${dst_ip}0304000000000000
+    orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
+    orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
+    orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
+    orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
+    orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
+
+    packet=${packet}${orig_packet_l3}
+
+    gw_ip_garp=ffffffffffff00002020121308060001080006040001000020201213aca80064000000000000aca80064
+
+    packet_bytes=$(expr ${#packet} / 2)
+    mtu_needed=$(expr ${packet_bytes} - 18)
+
+    # If icmp_pmtu_reply_expected is 0, it means the packet is lesser than
+    # the gateway mtu and should be delivered to the provider bridge via the
+    # localnet port.
+    # If icmp_pmtu_reply_expected is 1, it means the packet is larger than
+    # the gateway mtu and ovn-controller should drop the packet and instead
+    # generate ICMPv4  Destination Unreachable message with pmtu set to 100.
+    if test $mtu -ge $mtu_needed; then
+        # Packet to expect at br-phys.
+        src_mac="000020201213"
+        dst_mac="00000012af11"
+        src_ip=`ip_to_hex 10 0 0 3`
+        dst_ip=`ip_to_hex 172 168 0 3`
+        expected=${dst_mac}${src_mac}08004500${pkt_len}000000003f01c4d9
+        expected=${expected}${src_ip}${dst_ip}0304000000000000
+        expected=${expected}000000000000000000000000000000000000
+        expected=${expected}000000000000000000000000000000000000
+        expected=${expected}000000000000000000000000000000000000
+        expected=${expected}000000000000000000000000000000000000
+        expected=${expected}000000000000000000000000000000000000
+        echo $expected > br_phys_n1.expected
+        echo $gw_ip_garp >> br_phys_n1.expected
+    else
+        src_ip=`ip_to_hex 10 0 0 1`
+        dst_ip=`ip_to_hex 10 0 0 3`
+        # pkt len should be 146 (28 (icmp packet) + 118 (orig ip + payload))
+        reply_pkt_len=0092
+        ip_csum=f993
+        icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe016867
+        icmp_reply=${icmp_reply}${src_ip}${dst_ip}0304${ip_csum}0000$(printf "%04x" $mtu)
+        icmp_reply=${icmp_reply}4500${pkt_len}000000003f01c4d9
+        icmp_reply=${icmp_reply}${orig_packet_l3}
+        echo $icmp_reply > hv1-vif1.expected
+    fi
+
+    as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
+    as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+
+    # Send packet from sw0-port1 to outside
+    check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
+
+    if test $mtu -ge $mtu_needed; then
+        OVN_CHECK_PACKETS([hv1/br-phys_n1-tx.pcap], [br_phys_n1.expected])
+        $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap  > pkts
+        # hv1/vif1-tx.pcap can receive the GARP packet generated by ovn-controller
+        # for the gateway router port. So ignore this packet.
+        cat pkts | grep -v $gw_ip_garp > packets
+        AT_CHECK([cat packets], [0], [])
+    else
+        OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [hv1-vif1.expected])
+        $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap  > \
+        pkts
+        # hv1/br-phys_n1-tx.pcap can receive the GARP packet generated by ovn-controller
+        # for the gateway router port. So ignore this packet.
+        cat pkts | grep -v $gw_ip_garp > packets
+        AT_CHECK([cat packets], [0], [])
+    fi
+}
+
+test_ip6_packet_larger() {
+    local mtu=$1
+
+    local eth_src=505400000001
+    local eth_dst=00000000ff01
+
+    local ipv6_src=10000000000000000000000000000003
+    local ipv6_dst=20000000000000000000000000000002
+    local ipv6_rt=10000000000000000000000000000001
+
+    local payload=0000000000000000000000000000000000000000
+    local payload=${payload}0000000000000000000000000000000000000000
+    local payload=${payload}0000000000000000000000000000000000000000
+    local payload=${payload}0000000000000000000000000000000000000000
+
+    local ip6_hdr=6000000000583aff${ipv6_src}${ipv6_dst}
+    local packet=${eth_dst}${eth_src}86dd${ip6_hdr}8000ec7662f00001${payload}
+
+    as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
+    as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+
+    # Send packet from sw0-port1 to outside
+    tcpdump_hex ">> sending packet:" $packet
+    check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
+    AT_CHECK([as hv1 ovs-appctl ofproto/trace br-int in_port=hv1-vif1 $packet > trace-$mtu],
+             [0], [ignore])
+    AT_CAPTURE_FILE([trace-$mtu])
+
+    packet_bytes=$(expr ${#packet} / 2)
+    mtu_needed=$(expr ${packet_bytes} - 18)
+    if test $mtu -lt $mtu_needed; then
+        # First construct the inner IPv6 packet.
+        inner_ip6=6000000000583afe${ipv6_src}${ipv6_dst}
+        inner_icmp6=8000000062f00001
+        inner_icmp6_and_payload=$(icmp6_csum_inplace ${inner_icmp6}${payload} ${inner_ip6})
+        inner_packet=${inner_ip6}${inner_icmp6_and_payload}
+
+        # Then the outer.
+        outer_ip6=6000000000883afe${ipv6_rt}${ipv6_src}
+        outer_icmp6_and_payload=$(icmp6_csum_inplace 020000000000$(printf "%04x" $mtu)${inner_packet} $outer_ip6)
+        outer_packet=${outer_ip6}${outer_icmp6_and_payload}
+
+        icmp6_reply=${eth_src}${eth_dst}86dd${outer_packet}
+
+        echo
+        tcpdump_hex ">> expecting reply packet" $icmp6_reply
+
+        # The "trace" above sends a second packets as a side effect.
+        (echo $icmp6_reply; echo $icmp6_reply) > hv1-vif1.expected
+
+        OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [hv1-vif1.expected])
+    fi
+}
+
+wait_for_ports_up
+ovn-nbctl --wait=hv sync
+
+ovn-nbctl show > nbdump
+AT_CAPTURE_FILE([nbdump])
+
+ovn-sbctl show > sbdump
+AT_CAPTURE_FILE([sbdump])
+
+ovn-sbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
+
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int  \
+| grep "check_pkt_larger" | wc -l], [0], [[0
+]])
+dp_uuid=$(ovn-sbctl find datapath_binding | grep sw0 -B2 | grep _uuid | \
+awk '{print $3}')
+ovn-sbctl create MAC_Binding ip=172.168.0.3 datapath=$dp_uuid \
+logical_port=lr0-public mac="00\:00\:00\:12\:af\:11"
+
+# Try different gateway mtus and send a 142-byte packet (corresponding
+# to a 124-byte MTU).  If the MTU is less than 124, ovn-controller
+# should send icmp host not reachable with pmtu set to $mtu.
+for mtu in 100 500 118; do
+    AS_BOX([testing mtu $mtu])
+    check ovn-nbctl --wait=hv set logical_router_port lr0-public options:gateway_mtu=$mtu
+    ovn-sbctl dump-flows > sbflows-$mtu
+    AT_CAPTURE_FILE([sbflows-$mtu])
+
+    OVS_WAIT_FOR_OUTPUT([
+        as hv1 ovs-ofctl dump-flows br-int > br-int-flows-$mtu
+        AT_CAPTURE_FILE([br-int-flows-$mtu])
+        grep "check_pkt_larger($(expr $mtu + 18))" br-int-flows-$mtu | wc -l], [0], [1
+])
+
+    AS_BOX([testing mtu $mtu - IPv4])
+    test_ip_packet_larger $mtu
+
+    AS_BOX([testing mtu $mtu - IPv6])
+    test_ip6_packet_larger $mtu
+done
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([ovn -- IP packet buffering])
 AT_KEYWORDS([ip-buffering])