diff mbox series

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

Message ID df21ea8379481a9bed2fa8bf34cc7b24e760378d.1622154070.git.lorenzo.bianconi@redhat.com
State Changes Requested
Headers show
Series Introduce check_pkt_larger for ingress traffic | expand

Commit Message

Lorenzo Bianconi May 27, 2021, 10:26 p.m. UTC
As it is already done for distributed gw router scenario, introduce
check_pkt_larger 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(-)

Comments

Mark Michelson June 8, 2021, 4:46 p.m. UTC | #1
Equivalent DDLog code is missing from this patch.

The new test is pretty much exactly the same as "ovn -- router - check 
packet length - icmp defrag". I think there are about 3-4 changed lines, 
including the title and keywords. I think you could add the gateway 
router testing to the existing test instead of writing an entirely new test.

On 5/27/21 6:26 PM, Lorenzo Bianconi wrote:
> As it is already done for distributed gw router scenario, introduce
> check_pkt_larger 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 --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index d849e6abc..2269f0185 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -10525,17 +10525,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 71d2bab4d..7ad0dcb54 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -16412,6 +16412,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])
>
Lorenzo Bianconi June 8, 2021, 5:01 p.m. UTC | #2
> Equivalent DDLog code is missing from this patch.
> 
> The new test is pretty much exactly the same as "ovn -- router - check
> packet length - icmp defrag". I think there are about 3-4 changed lines,
> including the title and keywords. I think you could add the gateway router
> testing to the existing test instead of writing an entirely new test.

ack, I will fix it in v3.

Regards,
Lorenzo

> 
> On 5/27/21 6:26 PM, Lorenzo Bianconi wrote:
> > As it is already done for distributed gw router scenario, introduce
> > check_pkt_larger 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 --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > index d849e6abc..2269f0185 100644
> > --- a/northd/ovn-northd.c
> > +++ b/northd/ovn-northd.c
> > @@ -10525,17 +10525,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 71d2bab4d..7ad0dcb54 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -16412,6 +16412,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])
> > 
>
diff mbox series

Patch

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index d849e6abc..2269f0185 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -10525,17 +10525,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 71d2bab4d..7ad0dcb54 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -16412,6 +16412,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])