diff --git a/SOURCES/0001-Add-DNSSL-support-to-OVN.patch b/SOURCES/0001-Add-DNSSL-support-to-OVN.patch new file mode 100644 index 0000000..9b53940 --- /dev/null +++ b/SOURCES/0001-Add-DNSSL-support-to-OVN.patch @@ -0,0 +1,250 @@ +From 5a12a940f63a5909baee6d00ce9e1696a2e7c96c Mon Sep 17 00:00:00 2001 +Message-Id: <5a12a940f63a5909baee6d00ce9e1696a2e7c96c.1572427188.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 29 Oct 2019 13:18:34 +0100 +Subject: [PATCH ovn] Add DNSSL support to OVN + +Introduce the possibility to specify a DNSSL option to Router +Advertisement packets. DNS Search list can be specified using +'dnssl' tag in the ipv6_ra_configs column of logical router +port table + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/pinctrl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ + ovn/lib/ovn-l7.h | 11 ++++++++ + ovn/northd/ovn-northd.c | 4 +++ + ovn/ovn-nb.xml | 5 ++++ + tests/ovn.at | 30 ++++++++++++++------- + 5 files changed, 103 insertions(+), 10 deletions(-) + +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2198,6 +2198,7 @@ struct ipv6_ra_config { + struct lport_addresses prefixes; + struct in6_addr rdnss; + bool has_rdnss; ++ struct ds dnssl; + }; + + struct ipv6_ra_state { +@@ -2219,6 +2220,7 @@ ipv6_ra_config_delete(struct ipv6_ra_con + { + if (config) { + destroy_lport_addresses(&config->prefixes); ++ ds_destroy(&config->dnssl); + free(config); + } + } +@@ -2257,6 +2259,7 @@ ipv6_ra_update_config(const struct sbrec + nd_ra_min_interval_default(config->max_interval)); + config->mtu = smap_get_int(&pb->options, "ipv6_ra_mtu", ND_MTU_DEFAULT); + config->la_flags = IPV6_ND_RA_OPT_PREFIX_ON_LINK; ++ ds_init(&config->dnssl); + + const char *address_mode = smap_get(&pb->options, "ipv6_ra_address_mode"); + if (!address_mode) { +@@ -2302,6 +2305,11 @@ ipv6_ra_update_config(const struct sbrec + } + config->has_rdnss = !!rdnss; + ++ const char *dnssl = smap_get(&pb->options, "ipv6_ra_dnssl"); ++ if (dnssl) { ++ ds_put_buffer(&config->dnssl, dnssl, strlen(dnssl)); ++ } ++ + return config; + + fail: +@@ -2358,6 +2366,57 @@ packet_put_ra_rdnss_opt(struct dp_packet + prev_l4_size + len * 8)); + } + ++static void ++packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, ++ char *dnssl_list) ++{ ++ size_t prev_l4_size = dp_packet_l4_size(b); ++ size_t size = sizeof(struct ovs_nd_dnssl); ++ struct ip6_hdr *nh = dp_packet_l3(b); ++ char *t0, *r0, dnssl[255] = {}; ++ int i = 0; ++ ++ /* Multiple DNS Search List must be 'comma' separated ++ * (e.g. "a.b.c, d.e.f"). Domain names must be encoded ++ * as described in Section 3.1 of RFC1035. ++ * (e.g if dns list is a.b.c,www.ovn.org, it will be encoded as: ++ * 01 61 01 62 01 63 00 03 77 77 77 03 6f 76 63 03 6f 72 67 00 ++ */ ++ for (t0 = strtok_r(dnssl_list, ",", &r0); t0; ++ t0 = strtok_r(NULL, ",", &r0)) { ++ char *t1, *r1; ++ ++ size += strlen(t0) + 2; ++ if (size > sizeof(dnssl)) { ++ return; ++ } ++ ++ for (t1 = strtok_r(t0, ".", &r1); t1; ++ t1 = strtok_r(NULL, ".", &r1)) { ++ dnssl[i++] = strlen(t1); ++ memcpy(&dnssl[i], t1, strlen(t1)); ++ i += strlen(t1); ++ } ++ dnssl[i++] = 0; ++ } ++ size = ROUND_UP(size, 8); ++ nh->ip6_plen = htons(prev_l4_size + size); ++ ++ struct ovs_nd_dnssl *nd_dnssl = dp_packet_put_uninit(b, sizeof *nd_dnssl); ++ nd_dnssl->type = ND_OPT_DNSSL; ++ nd_dnssl->len = size / 8; ++ nd_dnssl->reserved = 0; ++ put_16aligned_be32(&nd_dnssl->lifetime, lifetime); ++ ++ dp_packet_put(b, dnssl, size - sizeof *nd_dnssl); ++ ++ struct ovs_ra_msg *ra = dp_packet_l4(b); ++ ra->icmph.icmp6_cksum = 0; ++ uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); ++ ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra, ++ prev_l4_size + size)); ++} ++ + /* Called with in the pinctrl_handler thread context. */ + static long long int + ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra) +@@ -2386,6 +2445,10 @@ ipv6_ra_send(struct rconn *swconn, struc + packet_put_ra_rdnss_opt(&packet, 1, htonl(0xffffffff), + &ra->config->rdnss); + } ++ if (ra->config->dnssl.length) { ++ packet_put_ra_dnssl_opt(&packet, htonl(0xffffffff), ++ ra->config->dnssl.string); ++ } + + uint64_t ofpacts_stub[4096 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); +--- a/ovn/lib/ovn-l7.h ++++ b/ovn/lib/ovn-l7.h +@@ -231,6 +231,17 @@ struct nd_rdnss_opt { + }; + BUILD_ASSERT_DECL(ND_RDNSS_OPT_LEN == sizeof(struct nd_rdnss_opt)); + ++/* DNSSL option RFC 6106 */ ++#define ND_OPT_DNSSL 31 ++#define ND_DNSSL_OPT_LEN 8 ++struct ovs_nd_dnssl { ++ u_int8_t type; /* ND_OPT_DNSSL */ ++ u_int8_t len; /* >= 2 */ ++ ovs_be16 reserved; ++ ovs_16aligned_be32 lifetime; ++}; ++BUILD_ASSERT_DECL(ND_DNSSL_OPT_LEN == sizeof(struct ovs_nd_dnssl)); ++ + #define DHCPV6_DUID_LL 3 + #define DHCPV6_HW_TYPE_ETH 1 + +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6477,6 +6477,10 @@ copy_ra_to_sb(struct ovn_port *op, const + if (rdnss) { + smap_add(&options, "ipv6_ra_rdnss", rdnss); + } ++ const char *dnssl = smap_get(&op->nbrp->ipv6_ra_configs, "dnssl"); ++ if (dnssl) { ++ smap_add(&options, "ipv6_ra_dnssl", dnssl); ++ } + + smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s); + +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -1890,6 +1890,11 @@ + IPv6 address of RDNSS server announced in RA packets. At the moment + OVN supports just one RDNSS server. + ++ ++ ++ DNS Search List announced in RA packets. Multiple DNS Search List ++ must be 'comma' separated (e.g. "a.b.c, d.e.f") ++ + + + +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -12522,14 +12522,15 @@ construct_expected_ra() { + local mtu=$1 + local ra_mo=$2 + local rdnss=$3 +- local ra_prefix_la=$4 ++ local dnssl=$4 ++ local ra_prefix_la=$5 + + local slla=0101${src_mac} + local mtu_opt="" + if test $mtu != 0; then + mtu_opt=05010000${mtu} + fi +- shift 4 ++ shift 5 + + local prefix="" + while [[ $# -gt 0 ]] ; do +@@ -12543,8 +12544,12 @@ construct_expected_ra() { + if test $rdnss != 0; then + rdnss_opt=19030000ffffffff${rdnss} + fi ++ local dnssl_opt="" ++ if test $dnssl != 0; then ++ dnssl_opt=1f030000ffffffff${dnssl} ++ fi + +- local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt} ++ local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}${dnssl_opt} + local icmp=8600XXXX${ra} + + local ip_len=$(expr ${#icmp} / 2) +@@ -12579,28 +12584,33 @@ ra_test() { + } + + # Baseline test with no MTU +-ra_test 0 00 0 c0 40 aef00000000000000000000000000000 ++ra_test 0 00 0 0 c0 40 aef00000000000000000000000000000 + + # Now make sure an MTU option makes it + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500 +-ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000 ++ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 + + # Now test for multiple network prefixes + ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48' +-ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # Now test for RDNSS + ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11' + dns_addr=aef00000000000000000000000000011 +-ra_test 000005dc 00 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 00 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ ++# Now test for DNSSL ++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:dnssl="aa.bb.cc" ++dnssl=02616102626202636300000000000000 ++ra_test 000005dc 00 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + +-# Test a different address mode now ++## Test a different address mode now + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful +-ra_test 000005dc 80 $dns_addr 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 80 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # And the other address mode + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless +-ra_test 000005dc 40 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 40 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP diff --git a/SOURCES/0001-Add-RDNSS-support-to-OVN.patch b/SOURCES/0001-Add-RDNSS-support-to-OVN.patch new file mode 100644 index 0000000..cb385f4 --- /dev/null +++ b/SOURCES/0001-Add-RDNSS-support-to-OVN.patch @@ -0,0 +1,207 @@ +From fd8175f3e6b1d2b17521ab7bee3c86a6b5c20cc7 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Lorenzo Bianconi +Date: Mon, 28 Oct 2019 15:37:39 +0100 +Subject: [PATCH ovn] Add RDNSS support to OVN + +Introduce the possibility to specify an RDNSS option to Router +Advertisement packets. DNS IPv6 address can be specified using +'rdnss' tag in the ipv6_ra_configs column of logical router +port table + +Acked-by: Mark Michelson +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/pinctrl.c | 39 +++++++++++++++++++++++++++++++++++++++ + ovn/lib/ovn-l7.h | 11 +++++++++++ + ovn/northd/ovn-northd.c | 5 +++++ + ovn/ovn-nb.xml | 5 +++++ + tests/ovn.at | 27 +++++++++++++++++++-------- + 5 files changed, 79 insertions(+), 8 deletions(-) + +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2196,6 +2196,8 @@ struct ipv6_ra_config { + uint8_t mo_flags; /* Managed/Other flags for RAs */ + uint8_t la_flags; /* On-link/autonomous flags for address prefixes */ + struct lport_addresses prefixes; ++ struct in6_addr rdnss; ++ bool has_rdnss; + }; + + struct ipv6_ra_state { +@@ -2293,6 +2295,12 @@ ipv6_ra_update_config(const struct sbrec + VLOG_WARN("Invalid IP source %s", ip_addr); + goto fail; + } ++ const char *rdnss = smap_get(&pb->options, "ipv6_ra_rdnss"); ++ if (rdnss && !ipv6_parse(rdnss, &config->rdnss)) { ++ VLOG_WARN("Invalid RDNSS source %s", rdnss); ++ goto fail; ++ } ++ config->has_rdnss = !!rdnss; + + return config; + +@@ -2323,6 +2331,33 @@ put_load(uint64_t value, enum mf_field_i + bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits); + } + ++static void ++packet_put_ra_rdnss_opt(struct dp_packet *b, uint8_t num, ++ ovs_be32 lifetime, const struct in6_addr *dns) ++{ ++ size_t prev_l4_size = dp_packet_l4_size(b); ++ struct ip6_hdr *nh = dp_packet_l3(b); ++ size_t len = 2 * num + 1; ++ ++ nh->ip6_plen = htons(prev_l4_size + len * 8); ++ ++ struct nd_rdnss_opt *nd_rdnss = dp_packet_put_uninit(b, sizeof *nd_rdnss); ++ nd_rdnss->type = ND_OPT_RDNSS; ++ nd_rdnss->len = len; ++ nd_rdnss->reserved = 0; ++ put_16aligned_be32(&nd_rdnss->lifetime, lifetime); ++ ++ for (int i = 0; i < num; i++) { ++ dp_packet_put(b, &dns[i], sizeof(ovs_be32[4])); ++ } ++ ++ struct ovs_ra_msg *ra = dp_packet_l4(b); ++ ra->icmph.icmp6_cksum = 0; ++ uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); ++ ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra, ++ prev_l4_size + len * 8)); ++} ++ + /* Called with in the pinctrl_handler thread context. */ + static long long int + ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra) +@@ -2347,6 +2382,10 @@ ipv6_ra_send(struct rconn *swconn, struc + ra->config->la_flags, htonl(IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME), + htonl(IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME), addr); + } ++ if (ra->config->has_rdnss) { ++ packet_put_ra_rdnss_opt(&packet, 1, htonl(0xffffffff), ++ &ra->config->rdnss); ++ } + + uint64_t ofpacts_stub[4096 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); +--- a/ovn/lib/ovn-l7.h ++++ b/ovn/lib/ovn-l7.h +@@ -220,6 +220,17 @@ struct dhcpv6_opt_ia_na { + ovs_be32 t2; + }); + ++/* RDNSS option RFC 6106 */ ++#define ND_RDNSS_OPT_LEN 8 ++#define ND_OPT_RDNSS 25 ++struct nd_rdnss_opt { ++ uint8_t type; /* ND_OPT_RDNSS. */ ++ uint8_t len; /* >= 3. */ ++ ovs_be16 reserved; /* Always 0. */ ++ ovs_16aligned_be32 lifetime; ++}; ++BUILD_ASSERT_DECL(ND_RDNSS_OPT_LEN == sizeof(struct nd_rdnss_opt)); ++ + #define DHCPV6_DUID_LL 3 + #define DHCPV6_HW_TYPE_ETH 1 + +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6473,6 +6473,11 @@ copy_ra_to_sb(struct ovn_port *op, const + smap_add(&options, "ipv6_ra_prefixes", ds_cstr(&s)); + ds_destroy(&s); + ++ const char *rdnss = smap_get(&op->nbrp->ipv6_ra_configs, "rdnss"); ++ if (rdnss) { ++ smap_add(&options, "ipv6_ra_rdnss", rdnss); ++ } ++ + smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s); + + sbrec_port_binding_set_options(op->sb, &options); +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -1885,6 +1885,11 @@ + is one-third of , + i.e. 200 seconds if that key is unset. + ++ ++ ++ IPv6 address of RDNSS server announced in RA packets. At the moment ++ OVN supports just one RDNSS server. ++ + + + +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -12521,14 +12521,15 @@ construct_expected_ra() { + + local mtu=$1 + local ra_mo=$2 +- local ra_prefix_la=$3 ++ local rdnss=$3 ++ local ra_prefix_la=$4 + + local slla=0101${src_mac} + local mtu_opt="" + if test $mtu != 0; then + mtu_opt=05010000${mtu} + fi +- shift 3 ++ shift 4 + + local prefix="" + while [[ $# -gt 0 ]] ; do +@@ -12538,7 +12539,12 @@ construct_expected_ra() { + shift 2 + done + +- local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix} ++ local rdnss_opt="" ++ if test $rdnss != 0; then ++ rdnss_opt=19030000ffffffff${rdnss} ++ fi ++ ++ local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt} + local icmp=8600XXXX${ra} + + local ip_len=$(expr ${#icmp} / 2) +@@ -12573,23 +12579,28 @@ ra_test() { + } + + # Baseline test with no MTU +-ra_test 0 00 c0 40 aef00000000000000000000000000000 ++ra_test 0 00 0 c0 40 aef00000000000000000000000000000 + + # Now make sure an MTU option makes it + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500 +-ra_test 000005dc 00 c0 40 aef00000000000000000000000000000 ++ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000 + + # Now test for multiple network prefixes + ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48' +-ra_test 000005dc 00 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ ++# Now test for RDNSS ++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11' ++dns_addr=aef00000000000000000000000000011 ++ra_test 000005dc 00 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # Test a different address mode now + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful +-ra_test 000005dc 80 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 80 $dns_addr 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # And the other address mode + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless +-ra_test 000005dc 40 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 40 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP diff --git a/SOURCES/0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch b/SOURCES/0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch new file mode 100644 index 0000000..14c1025 --- /dev/null +++ b/SOURCES/0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch @@ -0,0 +1,270 @@ +From 9c04a4b62484895dc301875ad9da7c77c17a99c7 Mon Sep 17 00:00:00 2001 +Message-Id: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 24 Sep 2019 18:39:54 +0200 +Subject: [PATCH ovn 1/3] Add egress QoS mapping for non-tunnel interfaces + +Introduce add_localnet_egress_interface_mappings routine in order to collect as +egress interfaces all ovs bridge interfaces marked with ovn-egress-iface +in the external_ids column of ovs interface table. +ovn-egress-iface is used to indicate to which localnet ports QoS egress +shaping has to be applied. +Refactor add_bridge_mappings routine + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Mark Michelson +Acked-by: Mark Michelson +Acked-by: Dumitru Ceara +--- + ovn/controller/binding.c | 51 ++++++++++++++++++++++++- + ovn/controller/binding.h | 4 ++ + ovn/controller/ovn-controller.c | 3 +- + ovn/controller/patch.c | 76 +++++++++++++++++++++---------------- + ovn/controller/patch.h | 4 ++ + 5 files changed, 103 insertions(+), 35 deletions(-) + +--- a/ovn/controller/binding.c ++++ b/ovn/controller/binding.c +@@ -18,6 +18,7 @@ + #include "ha-chassis.h" + #include "lflow.h" + #include "lport.h" ++#include "patch.h" + + #include "lib/bitmap.h" + #include "openvswitch/poll-loop.h" +@@ -521,6 +522,9 @@ consider_local_datapath(struct ovsdb_idl + /* Add all localnet ports to local_lports so that we allocate ct zones + * for them. */ + sset_add(local_lports, binding_rec->logical_port); ++ if (qos_map && ovs_idl_txn) { ++ get_qos_params(binding_rec, qos_map); ++ } + } else if (!strcmp(binding_rec->type, "external")) { + if (ha_chassis_group_contains(binding_rec->ha_chassis_group, + chassis_rec)) { +@@ -609,9 +613,47 @@ consider_local_datapath(struct ovsdb_idl + } + + static void ++add_localnet_egress_interface_mappings( ++ const struct sbrec_port_binding *port_binding, ++ struct shash *bridge_mappings, struct sset *egress_ifaces) ++{ ++ const char *network = smap_get(&port_binding->options, "network_name"); ++ if (!network) { ++ return; ++ } ++ ++ struct ovsrec_bridge *br_ln = shash_find_data(bridge_mappings, network); ++ if (!br_ln) { ++ return; ++ } ++ ++ /* Add egress-ifaces from the connected bridge */ ++ for (size_t i = 0; i < br_ln->n_ports; i++) { ++ const struct ovsrec_port *port_rec = br_ln->ports[i]; ++ ++ for (size_t j = 0; j < port_rec->n_interfaces; j++) { ++ const struct ovsrec_interface *iface_rec; ++ ++ iface_rec = port_rec->interfaces[j]; ++ bool is_egress_iface = smap_get_bool(&iface_rec->external_ids, ++ "ovn-egress-iface", false); ++ if (!is_egress_iface) { ++ continue; ++ } ++ sset_add(egress_ifaces, iface_rec->name); ++ } ++ } ++} ++ ++static void + consider_localnet_port(const struct sbrec_port_binding *binding_rec, ++ struct shash *bridge_mappings, ++ struct sset *egress_ifaces, + struct hmap *local_datapaths) + { ++ add_localnet_egress_interface_mappings(binding_rec, ++ bridge_mappings, egress_ifaces); ++ + struct local_datapath *ld + = get_local_datapath(local_datapaths, + binding_rec->datapath->tunnel_key); +@@ -644,6 +686,8 @@ binding_run(struct ovsdb_idl_txn *ovnsb_ + const struct ovsrec_bridge *br_int, + const struct sbrec_chassis *chassis_rec, + const struct sset *active_tunnels, ++ const struct ovsrec_bridge_table *bridge_table, ++ const struct ovsrec_open_vswitch_table *ovs_table, + struct hmap *local_datapaths, struct sset *local_lports, + struct sset *local_lport_ids) + { +@@ -652,6 +696,7 @@ binding_run(struct ovsdb_idl_txn *ovnsb_ + } + + const struct sbrec_port_binding *binding_rec; ++ struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings); + struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface); + struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces); + struct hmap qos_map; +@@ -677,14 +722,18 @@ binding_run(struct ovsdb_idl_txn *ovnsb_ + + } + ++ add_ovs_bridge_mappings(ovs_table, bridge_table, &bridge_mappings); ++ + /* Run through each binding record to see if it is a localnet port + * on local datapaths discovered from above loop, and update the + * corresponding local datapath accordingly. */ + SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) { + if (!strcmp(binding_rec->type, "localnet")) { +- consider_localnet_port(binding_rec, local_datapaths); ++ consider_localnet_port(binding_rec, &bridge_mappings, ++ &egress_ifaces, local_datapaths); + } + } ++ shash_destroy(&bridge_mappings); + + if (!sset_is_empty(&egress_ifaces) + && set_noop_qos(ovs_idl_txn, port_table, qos_table, &egress_ifaces)) { +--- a/ovn/controller/binding.h ++++ b/ovn/controller/binding.h +@@ -26,6 +26,8 @@ struct ovsdb_idl_txn; + struct ovsrec_bridge; + struct ovsrec_port_table; + struct ovsrec_qos_table; ++struct ovsrec_bridge_table; ++struct ovsrec_open_vswitch_table; + struct sbrec_chassis; + struct sbrec_port_binding_table; + struct sset; +@@ -42,6 +44,8 @@ void binding_run(struct ovsdb_idl_txn *o + const struct ovsrec_bridge *br_int, + const struct sbrec_chassis *, + const struct sset *active_tunnels, ++ const struct ovsrec_bridge_table *bridge_table, ++ const struct ovsrec_open_vswitch_table *ovs_table, + struct hmap *local_datapaths, + struct sset *local_lports, struct sset *local_lport_ids); + bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn, +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -1045,7 +1045,8 @@ en_runtime_data_run(struct engine_node * + sbrec_port_binding_by_name, + port_table, qos_table, pb_table, + br_int, chassis, +- active_tunnels, local_datapaths, ++ active_tunnels, bridge_table, ++ ovs_table, local_datapaths, + local_lports, local_lport_ids); + + update_ct_zones(local_lports, local_datapaths, ct_zones, +--- a/ovn/controller/patch.c ++++ b/ovn/controller/patch.c +@@ -129,6 +129,48 @@ remove_port(const struct ovsrec_bridge_t + } + } + ++void ++add_ovs_bridge_mappings(const struct ovsrec_open_vswitch_table *ovs_table, ++ const struct ovsrec_bridge_table *bridge_table, ++ struct shash *bridge_mappings) ++{ ++ const struct ovsrec_open_vswitch *cfg; ++ ++ cfg = ovsrec_open_vswitch_table_first(ovs_table); ++ if (cfg) { ++ const char *mappings_cfg; ++ char *cur, *next, *start; ++ ++ mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings"); ++ if (!mappings_cfg || !mappings_cfg[0]) { ++ return; ++ } ++ ++ next = start = xstrdup(mappings_cfg); ++ while ((cur = strsep(&next, ",")) && *cur) { ++ const struct ovsrec_bridge *ovs_bridge; ++ char *network, *bridge = cur; ++ ++ network = strsep(&bridge, ":"); ++ if (!bridge || !*network || !*bridge) { ++ VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'", ++ mappings_cfg); ++ break; ++ } ++ ++ ovs_bridge = get_bridge(bridge_table, bridge); ++ if (!ovs_bridge) { ++ VLOG_WARN("Bridge '%s' not found for network '%s'", ++ bridge, network); ++ continue; ++ } ++ ++ shash_add(bridge_mappings, network, ovs_bridge); ++ } ++ free(start); ++ } ++} ++ + /* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for + * the local bridge mappings. Removes any patch ports for bridge mappings that + * already existed from 'existing_ports'. */ +@@ -142,41 +184,9 @@ add_bridge_mappings(struct ovsdb_idl_txn + const struct sbrec_chassis *chassis) + { + /* Get ovn-bridge-mappings. */ +- const char *mappings_cfg = ""; +- const struct ovsrec_open_vswitch *cfg; +- cfg = ovsrec_open_vswitch_table_first(ovs_table); +- if (cfg) { +- mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings"); +- if (!mappings_cfg || !mappings_cfg[0]) { +- return; +- } +- } +- +- /* Parse bridge mappings. */ + struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings); +- char *cur, *next, *start; +- next = start = xstrdup(mappings_cfg); +- while ((cur = strsep(&next, ",")) && *cur) { +- char *network, *bridge = cur; +- const struct ovsrec_bridge *ovs_bridge; +- +- network = strsep(&bridge, ":"); +- if (!bridge || !*network || !*bridge) { +- VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'", +- mappings_cfg); +- break; +- } + +- ovs_bridge = get_bridge(bridge_table, bridge); +- if (!ovs_bridge) { +- VLOG_WARN("Bridge '%s' not found for network '%s'", +- bridge, network); +- continue; +- } +- +- shash_add(&bridge_mappings, network, ovs_bridge); +- } +- free(start); ++ add_ovs_bridge_mappings(ovs_table, bridge_table, &bridge_mappings); + + const struct sbrec_port_binding *binding; + SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) { +--- a/ovn/controller/patch.h ++++ b/ovn/controller/patch.h +@@ -30,7 +30,11 @@ struct ovsrec_open_vswitch_table; + struct ovsrec_port_table; + struct sbrec_port_binding_table; + struct sbrec_chassis; ++struct shash; + ++void add_ovs_bridge_mappings(const struct ovsrec_open_vswitch_table *ovs_table, ++ const struct ovsrec_bridge_table *bridge_table, ++ struct shash *bridge_mappings); + void patch_run(struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_bridge_table *, + const struct ovsrec_open_vswitch_table *, diff --git a/SOURCES/0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch b/SOURCES/0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch new file mode 100644 index 0000000..279a123 --- /dev/null +++ b/SOURCES/0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch @@ -0,0 +1,166 @@ +From d7b024a24ee249c3ce6bf593499fdb6a79d78706 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Lorenzo Bianconi +Date: Thu, 28 Nov 2019 14:48:35 +0200 +Subject: [PATCH 1/2] Add support to Default Router Preference (PRF) - RFC 4191 + +Introduce support for Default Router Preference (PRF) in IPv6 Router +Advertisement according to RFC 4191 + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/pinctrl.c | 20 +++++++++++++++++--- + ovn/lib/ovn-l7.h | 4 ++++ + ovn/northd/ovn-northd.c | 8 ++++++++ + ovn/ovn-nb.xml | 12 ++++++++++++ + tests/ovn.at | 13 +++++++++---- + 5 files changed, 50 insertions(+), 7 deletions(-) + +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index fa9c827c1..ea0551c21 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2267,10 +2267,10 @@ ipv6_ra_update_config(const struct sbrec_port_binding *pb) + goto fail; + } + if (!strcmp(address_mode, "dhcpv6_stateless")) { +- config->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG; ++ config->mo_flags |= IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG; + config->la_flags |= IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS; + } else if (!strcmp(address_mode, "dhcpv6_stateful")) { +- config->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG; ++ config->mo_flags |= IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG; + } else if (!strcmp(address_mode, "slaac")) { + config->la_flags |= IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS; + } else { +@@ -2278,6 +2278,13 @@ ipv6_ra_update_config(const struct sbrec_port_binding *pb) + goto fail; + } + ++ const char *prf = smap_get(&pb->options, "ipv6_ra_prf"); ++ if (!strcmp(prf, "HIGH")) { ++ config->mo_flags |= IPV6_ND_RA_OPT_PRF_HIGH; ++ } else if (!strcmp(prf, "LOW")) { ++ config->mo_flags |= IPV6_ND_RA_OPT_PRF_LOW; ++ } ++ + const char *prefixes = smap_get(&pb->options, "ipv6_ra_prefixes"); + if (prefixes && !extract_ip_addresses(prefixes, &config->prefixes)) { + VLOG_WARN("Invalid IPv6 prefixes: %s", prefixes); +@@ -2427,10 +2434,17 @@ ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra) + + uint64_t packet_stub[128 / 8]; + struct dp_packet packet; ++ uint16_t router_lt = IPV6_ND_RA_LIFETIME; ++ ++ if (!router_lt) { ++ /* Reset PRF to MEDIUM if router lifetime is not set */ ++ ra->config->mo_flags &= ~IPV6_ND_RA_OPT_PRF_LOW; ++ } ++ + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); + compose_nd_ra(&packet, ra->config->eth_src, ra->config->eth_dst, + &ra->config->ipv6_src, &ra->config->ipv6_dst, +- 255, ra->config->mo_flags, htons(IPV6_ND_RA_LIFETIME), 0, 0, ++ 255, ra->config->mo_flags, htons(router_lt), 0, 0, + ra->config->mtu); + + for (int i = 0; i < ra->config->prefixes.n_ipv6_addrs; i++) { +diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h +index 5fc370bf5..fd898b0dc 100644 +--- a/ovn/lib/ovn-l7.h ++++ b/ovn/lib/ovn-l7.h +@@ -287,6 +287,10 @@ nd_ra_opts_destroy(struct hmap *nd_ra_opts) + #define IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME 0xffffffff + #define IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME 0xffffffff + ++#define IPV6_ND_RA_OPT_PRF_NORMAL 0x00 ++#define IPV6_ND_RA_OPT_PRF_HIGH 0x08 ++#define IPV6_ND_RA_OPT_PRF_LOW 0x18 ++ + static inline void + nd_ra_opts_init(struct hmap *nd_ra_opts) + { +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 0e6f8f621..1bcf34369 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6755,6 +6755,14 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode) + + smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s); + ++ const char *prf = smap_get(&op->nbrp->ipv6_ra_configs, ++ "router_preference"); ++ if (!prf || (strcmp(prf, "HIGH") && strcmp(prf, "LOW"))) { ++ smap_add(&options, "ipv6_ra_prf", "MEDIUM"); ++ } else { ++ smap_add(&options, "ipv6_ra_prf", prf); ++ } ++ + sbrec_port_binding_set_options(op->sb, &options); + smap_destroy(&options); + } +diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml +index 6f6e06777..16851dba1 100644 +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -1859,6 +1859,18 @@ + + + ++ ++ Default Router Preference (PRF) indicates whether to prefer this ++ router over other default routers (RFC 4191). ++ Possible values are: ++ ++
    ++
  • HIGH: mapped to 0x01 in RA PRF field
  • ++
  • MEDIUM: mapped to 0x00 in RA PRF field
  • ++
  • LOW: mapped to 0x11 in RA PRF field
  • ++
++
++ + + The recommended MTU for the link. Default is 0, which means no MTU + Option will be included in RA packet replied by ovn-controller. +diff --git a/tests/ovn.at b/tests/ovn.at +index 33989e697..78524256f 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -11488,23 +11488,28 @@ ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 + ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48' + ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + ++# Test PRF for default gw ++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="LOW" ++ra_test 000005dc 18 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ + # Now test for RDNSS + ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11' + dns_addr=aef00000000000000000000000000011 +-ra_test 000005dc 00 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 18 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # Now test for DNSSL + ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:dnssl="aa.bb.cc" ++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="HIGH" + dnssl=02616102626202636300000000000000 +-ra_test 000005dc 00 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 08 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + ## Test a different address mode now + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful +-ra_test 000005dc 80 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 88 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # And the other address mode + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless +-ra_test 000005dc 40 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 48 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP +-- +2.21.0 + diff --git a/SOURCES/0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch b/SOURCES/0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch new file mode 100644 index 0000000..460f9d0 --- /dev/null +++ b/SOURCES/0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch @@ -0,0 +1,402 @@ +From 1abcdb8d6995fac449e1a06dfd5a2b25ed888daa Mon Sep 17 00:00:00 2001 +From: Ihar Hrachyshka +Date: Thu, 5 Mar 2020 20:44:24 -0500 +Subject: [PATCH] Broadcast DHCPREPLY when BROADCAST flag is set + +As per RFC2131, section 4.1: + A server or relay agent sending or relaying a DHCP message directly + to a DHCP client (i.e., not to a relay agent specified in the + 'giaddr' field) SHOULD examine the BROADCAST bit in the 'flags' + field. If this bit is set to 1, the DHCP message SHOULD be sent as + an IP broadcast using an IP broadcast address (preferably 0xffffffff) + as the IP destination address and the link-layer broadcast address as + the link-layer destination address. + +This patch changes destination IP address to 255.255.255.255 when client +set BROADCAST flag in their DHCPREQUEST. Note: the offered IP address is +still part of the DHCP payload. + +While the new DHCP response is sent as a broadcast IP frame, it's +handled locally, as any other DHCP reply by the native responder. +Meaning, the reply is sent to the client port that initiated the DHCP +session only. + +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1801006 + +Backport changes: + * ovs_srcdir -> top_srcdir in tests + +Signed-off-by: Ihar Hrachyshka +Signed-off-by: Numan Siddique +(cherry picked from commit 4f8045b3b5f2c3376f86f5edc4e3f7507c2b1148) +--- + ovn/controller/pinctrl.c | 15 ++++++ + ovn/lib/ovn-l7.h | 2 + + ovn/northd/ovn-northd.8.xml | 5 +- + ovn/northd/ovn-northd.c | 7 ++- + tests/ovn.at | 98 +++++++++++++++++++++++++++---------- + 5 files changed, 93 insertions(+), 34 deletions(-) + +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 01a1bcbe0..2c7c2b217 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -972,6 +972,12 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, + dp_packet_uninit(&packet); + } + ++static bool ++is_dhcp_flags_broadcast(ovs_be16 flags) ++{ ++ return flags & htons(DHCP_BROADCAST_FLAG); ++} ++ + /* Called with in the pinctrl_handler thread context. */ + static void + pinctrl_handle_put_dhcp_opts( +@@ -1196,7 +1202,16 @@ pinctrl_handle_put_dhcp_opts( + + udp->udp_len = htons(new_l4_size); + ++ /* Send a broadcast IP frame when BROADCAST flag is set. */ + struct ip_header *out_ip = dp_packet_l3(&pkt_out); ++ ovs_be32 ip_dst; ++ if (!is_dhcp_flags_broadcast(dhcp_data->flags)) { ++ ip_dst = *offer_ip; ++ } else { ++ ip_dst = htonl(0xffffffff); ++ } ++ put_16aligned_be32(&out_ip->ip_dst, ip_dst); ++ + out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size); + udp->udp_csum = 0; + /* Checksum needs to be initialized to zero. */ +diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h +index ae6dbfdfb..c43218224 100644 +--- a/ovn/lib/ovn-l7.h ++++ b/ovn/lib/ovn-l7.h +@@ -32,6 +32,8 @@ struct gen_opts_map { + size_t code; + }; + ++#define DHCP_BROADCAST_FLAG 0x8000 ++ + #define DHCP_OPTION(NAME, CODE, TYPE) \ + {.name = NAME, .code = CODE, .type = TYPE} + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 04be2e7c7..c09e5ff44 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -855,7 +855,6 @@ next; +
+ eth.dst = eth.src;
+ eth.src = E;
+-ip4.dst = A;
+ ip4.src = S;
+ udp.src = 67;
+ udp.dst = 68;
+@@ -866,8 +865,8 @@ output;
+ 
+         

+ where E is the server MAC address and S is the +- server IPv4 address defined in the DHCPv4 options and A is +- the IPv4 address defined in the logical port's addresses column. ++ server IPv4 address defined in the DHCPv4 options. Note that ++ ip4.dst field is handled by put_dhcp_opts. +

+ +

+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index b324fa8e0..abe5508c3 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -4180,10 +4180,9 @@ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, + ds_put_cstr(options_action, "); next;"); + + ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; " +- "ip4.dst = "IP_FMT"; ip4.src = %s; udp.src = 67; " +- "udp.dst = 68; outport = inport; flags.loopback = 1; " +- "output;", +- server_mac, IP_ARGS(offer_ip), server_ip); ++ "ip4.src = %s; udp.src = 67; udp.dst = 68; " ++ "outport = inport; flags.loopback = 1; output;", ++ server_mac, server_ip); + + ds_put_format(ipv4_addr_match, + "ip4.src == "IP_FMT" && ip4.dst == {%s, 255.255.255.255}", +diff --git a/tests/ovn.at b/tests/ovn.at +index 3ce8514e5..dd94776e6 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -4567,10 +4567,11 @@ sleep 2 + as hv1 ovs-vsctl show + + # This shell function sends a DHCP request packet +-# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP REQUEST_IP ... ++# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP USE_IP ... + test_dhcp() { +- local inport=$1 src_mac=$2 dhcp_type=$3 ciaddr=$4 offer_ip=$5 request_ip=$6 use_ip=$7 +- shift; shift; shift; shift; shift; shift; shift; ++ local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 use_ip=$8 ++ shift; shift; shift; shift; shift; shift; shift; shift; ++ + if test $use_ip != 0; then + src_ip=$1 + dst_ip=$2 +@@ -4579,6 +4580,7 @@ test_dhcp() { + src_ip=`ip_to_hex 0 0 0 0` + dst_ip=`ip_to_hex 255 255 255 255` + fi ++ + if test $request_ip != 0; then + ip_len=0120 + udp_len=010b +@@ -4586,10 +4588,19 @@ test_dhcp() { + ip_len=011a + udp_len=0106 + fi ++ ++ if test $broadcast != 0; then ++ flags=8000 ++ reply_dst_ip=`ip_to_hex 255 255 255 255` ++ else ++ flags=0000 ++ reply_dst_ip=${offer_ip} ++ fi ++ + local request=ffffffffffff${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip} + # udp header and dhcp header + request=${request}00440043${udp_len}0000 +- request=${request}010106006359aa7600000000${ciaddr}000000000000000000000000${src_mac} ++ request=${request}010106006359aa760000${flags}${ciaddr}000000000000000000000000${src_mac} + # client hardware padding + request=${request}00000000000000000000 + # server hostname +@@ -4627,10 +4638,10 @@ test_dhcp() { + ip_len=$(printf "%x" $ip_len) + udp_len=$(printf "%x" $udp_len) + # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len +- local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip} ++ local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${reply_dst_ip} + # udp header and dhcp header. + # $udp_len var will be in 3 digits. So adding a '0' before $udp_len +- reply=${reply}004300440${udp_len}0000020106006359aa7600000000${ciaddr} ++ reply=${reply}004300440${udp_len}0000020106006359aa760000${flags}${ciaddr} + # your ip address; 0 for NAK + if test $dhcp_reply_type = 06; then + reply=${reply}00000000 +@@ -4701,7 +4712,7 @@ server_ip=`ip_to_hex 10 0 0 1` + ciaddr=`ip_to_hex 0 0 0 0` + request_ip=0 + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 1 f00000000001 01 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts ++test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts + + # NXT_RESUMEs should be 1. + OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4727,7 +4738,7 @@ server_ip=`ip_to_hex 10 0 0 1` + ciaddr=`ip_to_hex 0 0 0 0` + request_ip=$offer_ip + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts ++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts + + # NXT_RESUMEs should be 2. + OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4751,7 +4762,7 @@ server_ip=`ip_to_hex 10 0 0 1` + ciaddr=`ip_to_hex 0 0 0 0` + request_ip=`ip_to_hex 10 0 0 7` + expected_dhcp_opts="" +-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts ++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts + + # NXT_RESUMEs should be 3. + OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4775,7 +4786,7 @@ rm -f 2.expected + ciaddr=`ip_to_hex 0 0 0 0` + offer_ip=0 + request_ip=0 +-test_dhcp 2 f00000000002 08 $ciaddr $offer_ip $request_ip 0 1 1 ++test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 1 + + # NXT_RESUMEs should be 4. + OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4792,12 +4803,12 @@ rm -f 2.expected + # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once. + + ciaddr=`ip_to_hex 0 0 0 0` +-test_dhcp 3 f00000000003 01 $ciaddr 0 0 4 0 ++test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 4 0 + + # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for + # this lport. + ciaddr=`ip_to_hex 0 0 0 0` +-test_dhcp 4 f00000000004 01 $ciaddr 0 0 3 0 ++test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 3 0 + + # NXT_RESUMEs should be 4. + OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4814,7 +4825,7 @@ request_ip=0 + src_ip=$offer_ip + dst_ip=$server_ip + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts ++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts + + # NXT_RESUMEs should be 5. + OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4840,7 +4851,7 @@ request_ip=0 + src_ip=$offer_ip + dst_ip=`ip_to_hex 255 255 255 255` + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts ++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts + + # NXT_RESUMEs should be 6. + OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4866,7 +4877,7 @@ request_ip=0 + src_ip=$offer_ip + dst_ip=`ip_to_hex 255 255 255 255` + expected_dhcp_opts="" +-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts ++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts + + # NXT_RESUMEs should be 7. + OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4892,7 +4903,7 @@ request_ip=0 + src_ip=$offer_ip + dst_ip=`ip_to_hex 255 255 255 255` + expected_dhcp_opts="" +-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts ++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts + + # NXT_RESUMEs should be 8. + OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4914,7 +4925,7 @@ rm -f 2.expected + ciaddr=`ip_to_hex 0 0 0 0` + src_ip=`ip_to_hex 10 0 0 6` + dst_ip=`ip_to_hex 10 0 0 4` +-test_dhcp 2 f00000000002 03 $ciaddr 0 0 1 $src_ip $dst_ip 1 ++test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 1 $src_ip $dst_ip 1 + + # NXT_RESUMEs should be 8. + OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4922,6 +4933,29 @@ OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + # vif1-tx.pcap should have received the DHCPv4 request packet + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected]) + ++reset_pcap_file hv1-vif1 hv1/vif1 ++reset_pcap_file hv1-vif2 hv1/vif2 ++rm -f 1.expected ++rm -f 2.expected ++ ++# Send DHCPDISCOVER with BROADCAST flag on. ++offer_ip=`ip_to_hex 10 0 0 4` ++server_ip=`ip_to_hex 10 0 0 1` ++ciaddr=`ip_to_hex 0 0 0 0` ++request_ip=0 ++expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 ++test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts ++ ++# NXT_RESUMEs should be 9. ++OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++ ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets ++cat 1.expected | cut -c -48 > expout ++AT_CHECK([cat 1.packets | cut -c -48], [0], [expout]) ++# Skipping the IPv4 checksum. ++cat 1.expected | cut -c 53- > expout ++AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout]) ++ + OVN_CLEANUP([hv1]) + + AT_CLEANUP +@@ -12963,10 +12997,11 @@ as hv1 + ovs-vsctl show + + # This shell function sends a DHCP request packet +-# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP ... ++# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST OFFER_IP ... + test_dhcp() { +- local inport=$1 src_mac=$2 dhcp_type=$3 offer_ip=$4 use_ip=$5 +- shift; shift; shift; shift; shift; ++ local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 offer_ip=$5 use_ip=$6 ++ shift; shift; shift; shift; shift; shift; ++ + if test $use_ip != 0; then + src_ip=$1 + dst_ip=$2 +@@ -12975,10 +13010,19 @@ test_dhcp() { + src_ip=`ip_to_hex 0 0 0 0` + dst_ip=`ip_to_hex 255 255 255 255` + fi ++ ++ if test $broadcast != 0; then ++ flags=8000 ++ reply_dst_ip=`ip_to_hex 255 255 255 255` ++ else ++ flags=0000 ++ reply_dst_ip=${offer_ip} ++ fi ++ + local request=ffffffffffff${src_mac}0800451001100000000080110000${src_ip}${dst_ip} + # udp header and dhcp header + request=${request}0044004300fc0000 +- request=${request}010106006359aa760000000000000000000000000000000000000000${src_mac} ++ request=${request}010106006359aa760000${flags}00000000000000000000000000000000${src_mac} + # client hardware padding + request=${request}00000000000000000000 + # server hostname +@@ -13002,10 +13046,10 @@ test_dhcp() { + ip_len=$(printf "%x" $ip_len) + udp_len=$(printf "%x" $udp_len) + # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len +- local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip} ++ local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${reply_dst_ip} + # udp header and dhcp header. + # $udp_len var will be in 3 digits. So adding a '0' before $udp_len +- reply=${reply}004300440${udp_len}0000020106006359aa760000000000000000 ++ reply=${reply}004300440${udp_len}0000020106006359aa760000${flags}00000000 + # your ip address + reply=${reply}${offer_ip} + # next server ip address, relay agent ip address, client mac address +@@ -13124,7 +13168,7 @@ offer_ip=`ip_to_hex 10 0 0 6` + server_ip=`ip_to_hex 10 0 0 1` + server_mac=ff1000000001 + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \ ++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \ + $expected_dhcp_opts + + # NXT_RESUMEs should be 1 in hv1. +@@ -13222,7 +13266,7 @@ offer_ip=`ip_to_hex 10 0 0 6` + server_ip=`ip_to_hex 10 0 0 1` + server_mac=ff1000000001 + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \ ++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \ + $expected_dhcp_opts + + # NXT_RESUMEs should be 2 in hv1. +@@ -13332,7 +13376,7 @@ offer_ip=`ip_to_hex 10 0 0 6` + server_ip=`ip_to_hex 10 0 0 1` + server_mac=ff1000000001 + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \ ++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \ + $expected_dhcp_opts + + # NXT_RESUMEs should be 3 in hv1. +@@ -13412,7 +13456,7 @@ offer_ip=`ip_to_hex 10 0 0 6` + server_ip=`ip_to_hex 10 0 0 1` + server_mac=ff1000000001 + expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001 +-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \ ++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \ + $expected_dhcp_opts + + # NXT_RESUMEs should be 4 in hv1. +-- +2.24.1 + diff --git a/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch b/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch new file mode 100644 index 0000000..c27f337 --- /dev/null +++ b/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch @@ -0,0 +1,257 @@ +From afd10572ecbdd1d538ed4280f705ee79423b3c07 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Mark Michelson +Date: Mon, 20 Apr 2020 09:25:09 -0400 +Subject: [PATCH] DNS: Make DNS lookups case insensitive. + +From RFC 1035 Section 2.3.3: + +"For all parts of the DNS that are part of the official protocol, all +comparisons between character strings (e.g., labels, domain names, etc.) +are done in a case-insensitive manner." + +OVN was using case-sensitive lookups and therefore was not complying. +This change makes lookups case insensitive by storing lowercase record +names in the southbound database and converting incoming query names to +lowercase. + +Change-Id: I164898989a39c2eddac3f54396c5861ad8c18bb1 +Signed-off-by: Mark Michelson +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1819069 +Reported-by: Jianlin Shi +Acked-by: Numan Siddique +Signed-off-by: Lorenzo Bianconi +--- + ovn/controller/pinctrl.c | 7 ++++- + ovn/lib/ovn-util.c | 15 ++++++++++ + ovn/lib/ovn-util.h | 5 ++++ + ovn/northd/ovn-northd.c | 15 +++++++++- + ovn/ovn-sb.xml | 3 +- + tests/ovn.at | 61 +++++++++++++++++++++++++++++----------- + 6 files changed, 87 insertions(+), 19 deletions(-) + +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -1807,7 +1807,12 @@ pinctrl_handle_dns_lookup( + struct dns_data *d = iter->data; + for (size_t i = 0; i < d->n_dps; i++) { + if (d->dps[i] == dp_key) { +- answer_ips = smap_get(&d->records, ds_cstr(&query_name)); ++ /* DNS records in SBDB are stored in lowercase. Convert to ++ * lowercase to perform case insensitive lookup ++ */ ++ char *query_name_lower = str_tolower(ds_cstr(&query_name)); ++ answer_ips = smap_get(&d->records, query_name_lower); ++ free(query_name_lower); + if (answer_ips) { + break; + } +--- a/ovn/lib/ovn-util.c ++++ b/ovn/lib/ovn-util.c +@@ -19,6 +19,7 @@ + #include "openvswitch/ofp-parse.h" + #include "ovn/lib/ovn-nb-idl.h" + #include "ovn/lib/ovn-sb-idl.h" ++#include + + VLOG_DEFINE_THIS_MODULE(ovn_util); + +@@ -411,3 +412,17 @@ datapath_is_switch(const struct sbrec_da + { + return smap_get(&ldp->external_ids, "logical-switch") != NULL; + } ++ ++char * ++str_tolower(const char *orig) ++{ ++ char *copy = xmalloc(strlen(orig) + 1); ++ char *p = copy; ++ ++ while (*orig) { ++ *p++ = tolower(*orig++); ++ } ++ *p = '\0'; ++ ++ return copy; ++} +--- a/ovn/lib/ovn-util.h ++++ b/ovn/lib/ovn-util.h +@@ -87,4 +87,9 @@ uint32_t ovn_logical_flow_hash(const str + uint16_t priority, + const char *match, const char *actions); + bool datapath_is_switch(const struct sbrec_datapath_binding *); ++ ++/* Returns a lowercase copy of orig. ++ * Caller must free the returned string. ++ */ ++char *str_tolower(const char *orig); + #endif +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -9767,7 +9767,20 @@ sync_dns_entries(struct northd_context * + dns_info->sb_dns, + (struct sbrec_datapath_binding **)dns_info->sbs, + dns_info->n_sbs); +- sbrec_dns_set_records(dns_info->sb_dns, &dns_info->nb_dns->records); ++ ++ /* DNS lookups are case-insensitive. Convert records to lowercase so ++ * we can do consistent lookups when DNS requests arrive ++ */ ++ struct smap lower_records = SMAP_INITIALIZER(&lower_records); ++ struct smap_node *node; ++ SMAP_FOR_EACH (node, &dns_info->nb_dns->records) { ++ smap_add_nocopy(&lower_records, xstrdup(node->key), ++ str_tolower(node->value)); ++ } ++ ++ sbrec_dns_set_records(dns_info->sb_dns, &lower_records); ++ ++ smap_destroy(&lower_records); + free(dns_info->sbs); + free(dns_info); + } +--- a/ovn/ovn-sb.xml ++++ b/ovn/ovn-sb.xml +@@ -3502,7 +3502,8 @@ tcp.flags = RST; + + Key-value pair of DNS records with DNS query name as the key + and a string of IP address(es) separated by comma or space as the +- value. ++ value. ovn-northd stores the DNS query name in all lowercase in order to ++ facilitate case-insensitive lookups. + +

Example: "vm1.ovn.org" = "10.0.0.4 aef0::4"

+ +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -8380,6 +8380,12 @@ set_dns_params() { + # IPv4 address - 10.0.0.4 + expected_dns_answer=${query_name}00010001${ttl}00040a000004 + ;; ++ VM1) ++ # VM1.OVN.ORG ++ query_name=03564d31034f564e034f524700 ++ # IPv4 address - 10.0.0.4 ++ expected_dns_answer=${query_name}00010001${ttl}00040a000004 ++ ;; + vm2) + # vm2.ovn.org + query_name=03766d32036f766e036f726700 +@@ -8542,6 +8548,29 @@ reset_pcap_file hv1-vif2 hv1/vif2 + rm -f 1.expected + rm -f 2.expected + ++# Try vm1 again but an all-caps query name ++ ++set_dns_params VM1 ++src_ip=`ip_to_hex 10 0 0 6` ++dst_ip=`ip_to_hex 10 0 0 1` ++dns_reply=1 ++test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data ++ ++# NXT_RESUMEs should be 3. ++OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++ ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets ++cat 2.expected | cut -c -48 > expout ++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) ++# Skipping the IPv4 checksum. ++cat 2.expected | cut -c 53- > expout ++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) ++ ++reset_pcap_file hv1-vif1 hv1/vif1 ++reset_pcap_file hv1-vif2 hv1/vif2 ++rm -f 1.expected ++rm -f 2.expected ++ + # Clear the query name options for ls1-lp2 + ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org + +@@ -8551,8 +8580,8 @@ dst_ip=`ip_to_hex 10 0 0 1` + dns_reply=0 + test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data + +-# NXT_RESUMEs should be 3. +-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 4. ++OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets + AT_CHECK([cat 1.packets], [0], []) +@@ -8573,8 +8602,8 @@ dst_ip=`ip_to_hex 10 0 0 1` + dns_reply=0 + test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data + +-# NXT_RESUMEs should be 3 only. +-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 4 only. ++OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets + AT_CHECK([cat 2.packets], [0], []) +@@ -8594,8 +8623,8 @@ dst_ip=`ip_to_hex 10 0 0 1` + dns_reply=1 + test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data + +-# NXT_RESUMEs should be 4. +-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 5. ++OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets + cat 2.expected | cut -c -48 > expout +@@ -8616,8 +8645,8 @@ dst_ip=`ip_to_hex 10 0 0 1` + dns_reply=1 + test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data + +-# NXT_RESUMEs should be 5. +-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 6. ++OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets + cat 2.expected | cut -c -48 > expout +@@ -8638,8 +8667,8 @@ dst_ip=`ip_to_hex 10 0 0 1` + dns_reply=0 + test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data + +-# NXT_RESUMEs should be 6. +-OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 7. ++OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets + AT_CHECK([cat 2.packets], [0], []) +@@ -8656,8 +8685,8 @@ dst_ip=`ip_to_hex 10 0 0 1` + dns_reply=0 + test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data + +-# NXT_RESUMEs should be 7. +-OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 8. ++OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets + AT_CHECK([cat 2.packets], [0], []) +@@ -8676,8 +8705,8 @@ dst_ip=`ip_to_hex 10 0 0 1` + dns_reply=1 + test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data + +-# NXT_RESUMEs should be 8. +-OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 9. ++OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets + cat 1.expected | cut -c -48 > expout +@@ -8698,8 +8727,8 @@ dst_ip=aef00000000000000000000000000001 + dns_reply=1 + test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data + +-# NXT_RESUMEs should be 9. +-OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) ++# NXT_RESUMEs should be 10 ++OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets + # Skipping the UDP checksum. diff --git a/SOURCES/0001-Disable-conjunction-by-force-cross-product-for-all-t.patch b/SOURCES/0001-Disable-conjunction-by-force-cross-product-for-all-t.patch new file mode 100644 index 0000000..0f42a02 --- /dev/null +++ b/SOURCES/0001-Disable-conjunction-by-force-cross-product-for-all-t.patch @@ -0,0 +1,1383 @@ +From 55f3558950476c557ac595a273db7f9b735f69b5 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Fri, 27 Sep 2019 17:23:57 +0530 +Subject: [PATCH] Disable conjunction by force cross product for all the + fields. + +With this ovn-controller will not generate conjunction flows. +There are issues with the conjunction flows generated by ovn-controller. +Please see the commit 298701dbc996 for more information. + +Acked-by: Han Zhou +Signed-off-by: Numan Siddique + +(cherry-picked from ovn commit 6f914327a55aaab11507db0911b08d695e17ce2a) +--- + ovn/TODO.rst | 10 + + ovn/lib/expr.c | 20 +- + tests/ovn.at | 1244 +++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 1204 insertions(+), 70 deletions(-) + +diff --git a/ovn/TODO.rst b/ovn/TODO.rst +index 33489174f..8fcdf0396 100644 +--- a/ovn/TODO.rst ++++ b/ovn/TODO.rst +@@ -145,3 +145,13 @@ OVN To-do List + * Support FTP ALGs. + + * Support reject action. ++ ++* Conjunction: Conjunction is disabled in OVN. This needs to be revisisted ++ to enable conjunction again after addressing the issues related to it. ++ Like, if there are multiple ACLs with overlapping Conjunction matches, ++ conjunction flows are not added properly. ++ Eg. match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} && ++ tcp.dst >= 800 && tcp.dst <= 900) actions=drop ++ ++ match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} && ++ tcp.dst >= 1000 && tcp.dst <= 2000) actions=allow +diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c +index c0871e1e8..64ea0aafa 100644 +--- a/ovn/lib/expr.c ++++ b/ovn/lib/expr.c +@@ -32,6 +32,24 @@ + + VLOG_DEFINE_THIS_MODULE(expr); + ++/* Right now conjunction flows generated by ovn-controller ++ * has issues. If there are multiple flows with the same ++ * match for different conjunctions, ovn-controller doesn't ++ * handle it properly. ++ * Eg. ++ * match 1 - ip4.src == {IP1, IP2} && tcp.dst >=500 && tcp.src <=600 ++ * action - drop ++ * ++ * match 2 - ip4.src == {IP1, IP2} && tcp.dst >=700 && tcp.src <=800 ++ * action - allow. ++ * ++ * To handle this issue temporarily force crossproduct so that conjunction ++ * flows are not generated. ++ * ++ * Remove this once fixed. ++ * */ ++static bool force_crossproduct = true; ++ + static struct expr *parse_and_annotate(const char *s, + const struct shash *symtab, + struct ovs_list *nesting, +@@ -2633,7 +2651,7 @@ expr_normalize_and(struct expr *expr) + + ovs_assert(sub->type == EXPR_T_OR); + const struct expr_symbol *symbol = expr_get_unique_symbol(sub); +- if (!symbol || symbol->must_crossproduct) { ++ if (!symbol || force_crossproduct || symbol->must_crossproduct ) { + struct expr *or = expr_create_andor(EXPR_T_OR); + struct expr *k; + +diff --git a/tests/ovn.at b/tests/ovn.at +index e4e70456b..3f7e06cf5 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -580,16 +580,14 @@ tcp,reg14=0x6,tp_dst=500 + tcp,reg14=0x6,tp_dst=501 + ]) + AT_CHECK([expr_to_flow 'outport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.src == {400, 401} && tcp.dst == {500, 501}'], [0], [dnl +-conj_id=1,tcp,reg15=0x5 +-conj_id=2,tcp,reg15=0x6 +-tcp,reg15=0x5,tp_dst=500: conjunction(1, 0/2) +-tcp,reg15=0x5,tp_dst=501: conjunction(1, 0/2) +-tcp,reg15=0x5,tp_src=400: conjunction(1, 1/2) +-tcp,reg15=0x5,tp_src=401: conjunction(1, 1/2) +-tcp,reg15=0x6,tp_dst=500: conjunction(2, 0/2) +-tcp,reg15=0x6,tp_dst=501: conjunction(2, 0/2) +-tcp,reg15=0x6,tp_src=400: conjunction(2, 1/2) +-tcp,reg15=0x6,tp_src=401: conjunction(2, 1/2) ++tcp,reg15=0x5,tp_src=400,tp_dst=500 ++tcp,reg15=0x5,tp_src=400,tp_dst=501 ++tcp,reg15=0x5,tp_src=401,tp_dst=500 ++tcp,reg15=0x5,tp_src=401,tp_dst=501 ++tcp,reg15=0x6,tp_src=400,tp_dst=500 ++tcp,reg15=0x6,tp_src=400,tp_dst=501 ++tcp,reg15=0x6,tp_src=401,tp_dst=500 ++tcp,reg15=0x6,tp_src=401,tp_dst=501 + ]) + AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl + (no flows) +@@ -711,22 +709,27 @@ reg15=0x11 + ]) + AT_CLEANUP + +-AT_SETUP([ovn -- converting expressions to flows -- conjunction]) +-AT_KEYWORDS([conjunction]) ++AT_SETUP([ovn -- converting expressions to flows -- no conjunction]) ++AT_KEYWORDS([no conjunction]) + expr_to_flow () { + echo "$1" | ovstest test-ovn expr-to-flows | sort + } + ++# conjunction is disabled in OVN until some of the issues ++# related to conjunction flows are fixed. ++# expr-to-flows should not generate any conjunction flows. + lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \ + ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3}" + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl +-conj_id=1,ip +-ip,nw_dst=20.0.0.1: conjunction(1, 0/2) +-ip,nw_dst=20.0.0.2: conjunction(1, 0/2) +-ip,nw_dst=20.0.0.3: conjunction(1, 0/2) +-ip,nw_src=10.0.0.1: conjunction(1, 1/2) +-ip,nw_src=10.0.0.2: conjunction(1, 1/2) +-ip,nw_src=10.0.0.3: conjunction(1, 1/2) ++ip,nw_src=10.0.0.1,nw_dst=20.0.0.1 ++ip,nw_src=10.0.0.1,nw_dst=20.0.0.2 ++ip,nw_src=10.0.0.1,nw_dst=20.0.0.3 ++ip,nw_src=10.0.0.2,nw_dst=20.0.0.1 ++ip,nw_src=10.0.0.2,nw_dst=20.0.0.2 ++ip,nw_src=10.0.0.2,nw_dst=20.0.0.3 ++ip,nw_src=10.0.0.3,nw_dst=20.0.0.1 ++ip,nw_src=10.0.0.3,nw_dst=20.0.0.2 ++ip,nw_src=10.0.0.3,nw_dst=20.0.0.3 + ]) + + lflow="ip && (!ct.est || (ct.est && ct_label.blocked == 1))" +@@ -740,12 +743,12 @@ ct_state=-est+trk,ipv6 + lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \ + ip4.dst == {20.0.0.1, 20.0.0.2}" + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl +-conj_id=1,ip +-ip,nw_dst=20.0.0.1: conjunction(1, 0/2) +-ip,nw_dst=20.0.0.2: conjunction(1, 0/2) +-ip,nw_src=10.0.0.1: conjunction(1, 1/2) +-ip,nw_src=10.0.0.2: conjunction(1, 1/2) +-ip,nw_src=10.0.0.3: conjunction(1, 1/2) ++ip,nw_src=10.0.0.1,nw_dst=20.0.0.1 ++ip,nw_src=10.0.0.1,nw_dst=20.0.0.2 ++ip,nw_src=10.0.0.2,nw_dst=20.0.0.1 ++ip,nw_src=10.0.0.2,nw_dst=20.0.0.2 ++ip,nw_src=10.0.0.3,nw_dst=20.0.0.1 ++ip,nw_src=10.0.0.3,nw_dst=20.0.0.2 + ]) + + lflow="ip4 && ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \ +@@ -753,19 +756,60 @@ ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3} && \ + tcp.dst >= 1000 && tcp.dst <= 1010" + + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl +-conj_id=1,tcp +-tcp,nw_dst=20.0.0.1: conjunction(1, 0/3) +-tcp,nw_dst=20.0.0.2: conjunction(1, 0/3) +-tcp,nw_dst=20.0.0.3: conjunction(1, 0/3) +-tcp,nw_src=10.0.0.1: conjunction(1, 1/3) +-tcp,nw_src=10.0.0.2: conjunction(1, 1/3) +-tcp,nw_src=10.0.0.3: conjunction(1, 1/3) +-tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/3) +-tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/3) +-tcp,tp_dst=0x3f0/0xfffe: conjunction(1, 2/3) +-tcp,tp_dst=1000: conjunction(1, 2/3) +-tcp,tp_dst=1001: conjunction(1, 2/3) +-tcp,tp_dst=1010: conjunction(1, 2/3) ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1000 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1001 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1010 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1000 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1001 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1010 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1000 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1001 ++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1010 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1000 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1001 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1010 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1000 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1001 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1010 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1000 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1001 ++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1010 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1000 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1001 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1010 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1000 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1001 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1010 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1000 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1001 ++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1010 + ]) + + lflow="ip4 && ip4.src == {10.0.0.4, 10.0.0.5, 10.0.0.6} && \ +@@ -774,41 +818,1101 @@ tcp.dst <= 2000 && tcp.src >=1000 && tcp.src <= 2000) \ + || ip4.dst == 20.0.0.5 || ip4.dst == 20.0.0.6)" + + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl +-conj_id=1,tcp + ip,nw_src=10.0.0.4,nw_dst=20.0.0.5 + ip,nw_src=10.0.0.4,nw_dst=20.0.0.6 + ip,nw_src=10.0.0.5,nw_dst=20.0.0.5 + ip,nw_src=10.0.0.5,nw_dst=20.0.0.6 + ip,nw_src=10.0.0.6,nw_dst=20.0.0.5 + ip,nw_src=10.0.0.6,nw_dst=20.0.0.6 +-tcp,nw_dst=20.0.0.4: conjunction(1, 0/4) +-tcp,nw_dst=20.0.0.7: conjunction(1, 0/4) +-tcp,nw_dst=20.0.0.8: conjunction(1, 0/4) +-tcp,nw_src=10.0.0.4: conjunction(1, 1/4) +-tcp,nw_src=10.0.0.5: conjunction(1, 1/4) +-tcp,nw_src=10.0.0.6: conjunction(1, 1/4) +-tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/4) +-tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/4) +-tcp,tp_dst=0x3f0/0xfff0: conjunction(1, 2/4) +-tcp,tp_dst=0x400/0xfe00: conjunction(1, 2/4) +-tcp,tp_dst=0x600/0xff00: conjunction(1, 2/4) +-tcp,tp_dst=0x700/0xff80: conjunction(1, 2/4) +-tcp,tp_dst=0x780/0xffc0: conjunction(1, 2/4) +-tcp,tp_dst=0x7c0/0xfff0: conjunction(1, 2/4) +-tcp,tp_dst=1000: conjunction(1, 2/4) +-tcp,tp_dst=1001: conjunction(1, 2/4) +-tcp,tp_dst=2000: conjunction(1, 2/4) +-tcp,tp_src=0x3ea/0xfffe: conjunction(1, 3/4) +-tcp,tp_src=0x3ec/0xfffc: conjunction(1, 3/4) +-tcp,tp_src=0x3f0/0xfff0: conjunction(1, 3/4) +-tcp,tp_src=0x400/0xfe00: conjunction(1, 3/4) +-tcp,tp_src=0x600/0xff00: conjunction(1, 3/4) +-tcp,tp_src=0x700/0xff80: conjunction(1, 3/4) +-tcp,tp_src=0x780/0xffc0: conjunction(1, 3/4) +-tcp,tp_src=0x7c0/0xfff0: conjunction(1, 3/4) +-tcp,tp_src=1000: conjunction(1, 3/4) +-tcp,tp_src=1001: conjunction(1, 3/4) +-tcp,tp_src=2000: conjunction(1, 3/4) ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001 ++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000 + ]) + AT_CLEANUP + +@@ -12267,9 +13371,11 @@ AT_CHECK([cat 2.packets], [0], [expout]) + # priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(3,1/2) + # priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(3,1/2) + +-OVS_WAIT_UNTIL([test 12 = `as hv1 ovs-ofctl dump-flows br-int | \ ++# conjunction is disabled in OVN until the issues related to it are ++# fixed. ++OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ + grep conjunction | wc -l`]) +-OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \ ++OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ + grep conj_id | wc -l`]) + + as hv1 ovs-ofctl dump-flows br-int +-- +2.21.0 + diff --git a/SOURCES/0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch b/SOURCES/0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch new file mode 100644 index 0000000..8c96d96 --- /dev/null +++ b/SOURCES/0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch @@ -0,0 +1,99 @@ +From 308df38edabc40221b9ce293e73ea4ac71eb965e Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Tue, 5 Nov 2019 23:11:56 +0530 +Subject: [PATCH ovn] Fix ha chassis failover issues for stale ha chassis + entries + +If ha chassis rows of an HA chassis group become stale i.e the HA_Chassis.chassis +column is empty (because ovn-controller is not running in that chassis) +except one row and when ha_chassis_group_is_active() +is called on that ovn-controller, then it returns false. Ideally it should +become active since its the only active chassis. This patch fixes this issue. + +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1762777 +Reported-by: Daniel Alvarez + +Acked-by: Dumitru Ceara +Signed-off-by: Numan Siddique +--- + ovn/controller/ha-chassis.c | 25 +++++++++++++++++++++++++ + tests/ovn.at | 20 +++++++++++++++++++- + 2 files changed, 44 insertions(+), 1 deletion(-) + +diff --git a/ovn/controller/ha-chassis.c b/ovn/controller/ha-chassis.c +index 6d9426a..d6ec7b6 100644 +--- a/ovn/controller/ha-chassis.c ++++ b/ovn/controller/ha-chassis.c +@@ -142,6 +142,27 @@ ha_chassis_destroy_ordered(struct ha_chassis_ordered *ordered_ha_ch) + } + } + ++/* Returns true if there is only one active ha chassis in the chassis group ++ * (i.e HA_Chassis.chassis column is set) and that active ha chassis is ++ * local chassis. ++ * Returns false otherwise. */ ++static bool ++is_local_chassis_only_candidate(const struct sbrec_ha_chassis_group *ha_ch_grp, ++ const struct sbrec_chassis *local_chassis) ++{ ++ size_t n_active_ha_chassis = 0; ++ bool local_chassis_present = false; ++ for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) { ++ if (ha_ch_grp->ha_chassis[i]->chassis) { ++ n_active_ha_chassis++; ++ if (ha_ch_grp->ha_chassis[i]->chassis == local_chassis) { ++ local_chassis_present = true; ++ } ++ } ++ } ++ ++ return (local_chassis_present && n_active_ha_chassis == 1); ++} + + /* Returns true if the local_chassis is the master of + * the HA chassis group, false otherwise. */ +@@ -159,6 +180,10 @@ ha_chassis_group_is_active( + return (ha_ch_grp->ha_chassis[0]->chassis == local_chassis); + } + ++ if (is_local_chassis_only_candidate(ha_ch_grp, local_chassis)) { ++ return true; ++ } ++ + if (sset_is_empty(active_tunnels)) { + /* If active tunnel sset is empty, it means it has lost + * connectivity with other chassis. */ +diff --git a/tests/ovn.at b/tests/ovn.at +index 410f4b5..cb7903d 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -13413,7 +13413,25 @@ OVS_WAIT_UNTIL( + logical_port=ls1-lp_ext1` + test "$chassis" = "$hv1_uuid"]) + +-OVN_CLEANUP([hv1],[hv2],[hv3]) ++# Stop ovn-controllers on hv1 and hv3. ++as hv1 ovs-appctl -t ovn-controller exit ++as hv3 ovs-appctl -t ovn-controller exit ++ ++# hv2 should be master and claim ls1-lp_ext1 ++OVS_WAIT_UNTIL( ++ [chassis=`ovn-sbctl --bare --columns chassis find port_binding \ ++logical_port=ls1-lp_ext1` ++ test "$chassis" = "$hv2_uuid"]) ++ ++as hv1 ++OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as hv3 ++OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++OVN_CLEANUP([hv2]) + AT_CLEANUP + + AT_SETUP([ovn -- Address Set Incremental Processing]) +-- +1.8.3.1 + diff --git a/SOURCES/0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch b/SOURCES/0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch new file mode 100644 index 0000000..e0be69e --- /dev/null +++ b/SOURCES/0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch @@ -0,0 +1,61 @@ +From 65d601cd755051fcf38b707b1283e29152aa0dcb Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Mon, 11 Nov 2019 12:26:40 +0530 +Subject: [PATCH] Fix testsuite 85 - "ensure one gw controller restart in HA + doesn't bounce the master ok". + +This testsuite is failing frequently in travis CI and locally when tests are run with "-j5". + +The test case deletes the chassis row for chassis 'gw2' and expects that this +doesn't cause the chassisresident port on the master ('gw1') to not bounce as 'gw1' +has higher priority than gw2. But since the ha chassis group has only 2 chassis, +'gw2' can claim the port momentarily when 'gw2' chassis row is recreated until +BFD session with 'gw1' is not established. + +This patch changes the test assertion approach and makes sure that 'gw1' is the +owner of the chassisresident port eventually. + +Acked-by: Dumitru Ceara +Tested-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + tests/ovn.at | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/tests/ovn.at b/tests/ovn.at +index 3e429e347..5fc2de2d3 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -11033,12 +11033,26 @@ ovn-nbctl --wait=hv --timeout=3 sync + # doesn't have the same effect because "name" is conserved, and the + # Chassis entry is not replaced. + +-> gw1/ovn-controller.log +- + gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2) + ovn-sbctl destroy Chassis $gw2_chassis + +-OVS_WAIT_UNTIL([test 0 = `grep -c "Releasing lport" gw1/ovn-controller.log`]) ++# Wait for the gw2_chassis row is recreated. ++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns=_uuid find Chassis name=gw2 | wc -l`]) ++ ++gw1_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw1) ++gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2) ++ ++# When gw2 chassis row is destroyed, it gets recreated. There ++# is a small window in which gw2 may claim the cr-outside port if ++# it has not established bfd tunnel with gw1. ++# So make sure that, cr-outside is claimed by gw1 finally. ++OVS_WAIT_WHILE( ++ [cr_outside_ch=`ovn-sbctl --bare --columns=chassis find Port_binding logical_port=cr-outside` ++ test $cr_outside_ch = $gw2_chassis]) ++ ++OVS_WAIT_UNTIL( ++ [cr_outside_ch=`ovn-sbctl --bare --columns=chassis find Port_binding logical_port=cr-outside` ++ test $cr_outside_ch = $gw1_chassis]) + + OVN_CLEANUP([gw1],[gw2],[hv1]) + +-- +2.23.0 + diff --git a/SOURCES/0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch b/SOURCES/0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch new file mode 100644 index 0000000..90430b1 --- /dev/null +++ b/SOURCES/0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch @@ -0,0 +1,153 @@ +From d15bb6f025e14fc38415124b0794de40c40c69a2 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Wed, 16 Oct 2019 22:14:45 +0530 +Subject: [PATCH] Fix virtual port binding when the parents are scheduled in + the same chassis + +If a virtual port has 2 parents and if both of them are scheduled +on the same chassis, then virtual port binding doesn't work. + +For virtual port binding we have the below logical flows: +inport == p1 && !is_chassis_resident("vip-port") && arp .. actions=bind_vport(vip-port); next; +inport == p2 && !is_chassis_resident("vip-port") && arp .. actions=bind_vport(vip-port); next; + +Since we have !is_chassis_resident, as soon as p1 binds the port, the corresponding OF flows +for both the logical flows above are deleted. Because of this, when p2 becomes the +parent of vip-port it can't bind it. + +This patch fixes this issue by removing this condition. + +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1762341 +Acked-by: Dumitru Ceara +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 2 +- + ovn/northd/ovn-northd.c | 3 +-- + tests/ovn.at | 39 ++++++++++++++++++++++++++++++------- + 3 files changed, 34 insertions(+), 10 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 38e6ecec5..ad84a5783 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -580,7 +580,7 @@ + column with the match +

+
+-inport == P && !is_chassis_resident(V) && ((arp.op == 1 && arp.spa == VIP && arp.tpa == VIP) || (arp.op == 2 && arp.spa == VIP))
++inport == P && && ((arp.op == 1 && arp.spa == VIP && arp.tpa == VIP) || (arp.op == 2 && arp.spa == VIP))
+         
+ +

+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 84b2a9ff1..c506f22f4 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -5255,11 +5255,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + + ds_clear(&match); + ds_put_format(&match, "inport == \"%s\" && " +- "!is_chassis_resident(%s) && " + "((arp.op == 1 && arp.spa == %s && " + "arp.tpa == %s) || (arp.op == 2 && " + "arp.spa == %s))", +- vparent, op->json_key, virtual_ip, virtual_ip, ++ vparent, virtual_ip, virtual_ip, + virtual_ip); + ds_clear(&actions); + ds_put_format(&actions, +diff --git a/tests/ovn.at b/tests/ovn.at +index e38d14cdf..dad6c2d39 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -14503,7 +14503,7 @@ ovn-nbctl lsp-set-addresses sw0-vir "50:54:00:00:00:10 10.0.0.10" + ovn-nbctl lsp-set-port-security sw0-vir "50:54:00:00:00:10 10.0.0.10" + ovn-nbctl lsp-set-type sw0-vir virtual + ovn-nbctl set logical_switch_port sw0-vir options:virtual-ip=10.0.0.10 +-ovn-nbctl set logical_switch_port sw0-vir options:virtual-parents=sw0-p1,sw0-p2 ++ovn-nbctl set logical_switch_port sw0-vir options:virtual-parents=sw0-p1,sw0-p2,sw0-p3 + + ovn-nbctl lsp-add sw0 sw0-p1 + ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3" +@@ -14515,7 +14515,7 @@ ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4 10.0.0.10" + + ovn-nbctl lsp-add sw0 sw0-p3 + ovn-nbctl lsp-set-addresses sw0-p3 "50:54:00:00:00:05 10.0.0.5" +-ovn-nbctl lsp-set-port-security sw0-p3 "50:54:00:00:00:05 10.0.0.5" ++ovn-nbctl lsp-set-port-security sw0-p3 "50:54:00:00:00:05 10.0.0.5 10.0.0.10" + + # Create the second logical switch with one port + ovn-nbctl ls-add sw1 +@@ -14546,8 +14546,9 @@ ovn-nbctl --wait=hv sync + ovn-sbctl dump-flows sw0 | grep ls_in_arp_rsp | grep bind_vport > lflows.txt + + AT_CHECK([cat lflows.txt], [0], [dnl +- table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p1" && !is_chassis_resident("sw0-vir") && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) +- table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p2" && !is_chassis_resident("sw0-vir") && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) ++ table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p1" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) ++ table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p2" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) ++ table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p3" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) + ]) + + ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ +@@ -14595,6 +14596,29 @@ AT_CHECK([cat lflows.txt], [0], [dnl + table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) + ]) + ++# From sw0-p3 send GARP for 10.0.0.10. hv1 should claim sw0-vir ++# and sw0-p3 should be its virtual_parent. ++eth_src=505400000005 ++eth_dst=ffffffffffff ++spa=$(ip_to_hex 10 0 0 10) ++tpa=$(ip_to_hex 10 0 0 10) ++send_garp 1 2 $eth_src $eth_dst $spa $tpa ++ ++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \ ++logical_port=sw0-vir) = x$hv1_ch_uuid], [0], []) ++ ++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \ ++logical_port=sw0-vir) = xsw0-p3]) ++ ++ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ ++> lflows.txt ++ ++# There should be an arp resolve flow to resolve the virtual_ip with the ++# sw0-p2's MAC. ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:05; next;) ++]) ++ + # send the garp from sw0-p2 (in hv2). hv2 should claim sw0-vir + # and sw0-p2 shpuld be its virtual_parent. + eth_src=505400000004 +@@ -14613,7 +14637,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ + > lflows.txt + + # There should be an arp resolve flow to resolve the virtual_ip with the +-# sw0-p2's MAC. ++# sw0-p3's MAC. + AT_CHECK([cat lflows.txt], [0], [dnl + table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) + ]) +@@ -14658,7 +14682,7 @@ AT_CHECK([cat lflows.txt], [0], [dnl + ]) + + # Now send arp reply from sw0-p2. hv2 should claim sw0-vir +-# and sw0-p2 shpuld be its virtual_parent. ++# and sw0-p2 should be its virtual_parent. + eth_src=505400000004 + eth_dst=ffffffffffff + spa=$(ip_to_hex 10 0 0 10) +@@ -14701,7 +14725,8 @@ ovn-nbctl --wait=hv set logical_switch_port sw0-vir options:virtual-ip=10.0.0.10 + ovn-sbctl dump-flows sw0 | grep ls_in_arp_rsp | grep bind_vport > lflows.txt + + AT_CHECK([cat lflows.txt], [0], [dnl +- table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p1" && !is_chassis_resident("sw0-vir") && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) ++ table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p1" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) ++ table=11(ls_in_arp_rsp ), priority=100 , match=(inport == "sw0-p3" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;) + ]) + + ovn-nbctl --wait=hv remove logical_switch_port sw0-vir options virtual-parents +-- +2.23.0 + diff --git a/SOURCES/0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch b/SOURCES/0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch new file mode 100644 index 0000000..dc256e0 --- /dev/null +++ b/SOURCES/0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch @@ -0,0 +1,146 @@ +From 3c6763fdbc1c8b2d12d52c8fe28914ba55cdef6a Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Sun, 1 Sep 2019 22:36:15 +0530 +Subject: [PATCH] Handle GARP reply packets from provider networks only on + gateway chassis + +Suppose there is a provider network (with localnet port) and it is +connected to a logical router via a distributed gateway router port. +When an external switch sends GARP request packets, these packets +enter the br-int (via the patch port from the provider bridge) and +the action - put_arp is applied in the router pipeline only on the +gateway chassis where the distributed gateway router port is scheduled. +Other chassis even if they receive don't apply this action. This functionality +was added by the commit [1]. + +But that is not the case with GARP reply packets sent by the external +switch. ovn-controllers running on all the chassis configured with +ovn-bridge-mappings receive these packets as packet-ins because +of put_arp action. This results in unnecessary processing of these +packets only to be ignored. pinctrl thread wakes up the main ovn-controller +thread wasting few CPU cycles. It is enough for the gateway chassis to +handle these packets where the distributed router gateway port is scheduled. + +This patch achieves the same - similar to GARP request packets. The below +logical flows are added + - table=1 (lr_in_ip_input), priority=92, + match=(inport == "lrp" && + !is_chassis_resident("cr-lrp") && eth.bcast && + arp.op == 2 && arp.spa == PROVIDER_NETWORK_CIDR), action=(drop;) + + - table=1 (lr_in_ip_input), priority=92 + match=(inport == "lr0-public" && is_chassis_resident("cr-lr0-public") + && eth.bcast && arp.op == 2 && arp.spa == PROVIDER_NETWORK_CIDR), + action=(put_arp(inport, arp.spa, arp.sha);) + +This patch doesn't affect the arp replies from the overlay networks in the +router pipeline. This only handles GARP replies from the provider networks. + +In the present master this is not much of any issue even if ovn-controller +wakes up, thanks to incremental processing engine. But in the older +versions - 2.11, 2.10 and 2.9, this results in complete flow calculations +resulting in much more CPU cyles. This patch will definitely help in saving +these CPU cyles if backported. + +[1] - 3dd9febe953f("ovn-northd: Support learning neighbor from ARP request.") + +(cherry-picked from ovn commit 2b4e4d7399f10a8cc9f6873c3820860f6f736ad3) +CC: Han ZHou +Acked-by: Han ZHou +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 31 +++++++++++++++++++++++++++---- + ovn/northd/ovn-northd.c | 35 +++++++++++++++++++++++++++++++++++ + 2 files changed, 62 insertions(+), 4 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 6d2fbe349..ad98951f0 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1404,10 +1404,33 @@ arp.sha = external_mac; + + +

  • +- ARP reply handling. This flow uses ARP replies to populate the +- logical router's ARP table. A priority-90 flow with match arp.op +- == 2 has actions put_arp(inport, arp.spa, +- arp.sha);. ++

    ++ ARP reply handling. Following flows are added to handle ARP replies. ++

    ++ ++

    ++ For each distributed gateway logical router port a priority-92 flow ++ with match inport == P && ++ is_chassis_resident(cr-P) && eth.bcast && ++ arp.op == 2 && arp.spa == I with the ++ action put_arp(inport, arp.spa, arp.sha); so that the ++ resident gateway chassis can learn the GARP reply, where ++ P is the distributed gateway router port name, ++ I is the logical router port's network address. ++

    ++ ++

    ++ For each distributed gateway logical router port a priority-92 flow ++ with match inport == P && ++ !is_chassis_resident(cr-P) && eth.bcast && ++ arp.op == 2 && arp.spa == I with the action ++ drop; so that other chassis drop this packet. ++

    ++ ++

    ++ A priority-90 flow with match arp.op == 2 has actions ++ put_arp(inport, arp.spa, arp.sha);. ++

    +
  • + +
  • +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 6c6de2afd..20133ab41 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6398,6 +6398,41 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + } + ++ /* Handle GARP reply packets received on a distributed router gateway ++ * port. GARP reply broadcast packets could be sent by external ++ * switches. We don't want them to be handled by all the ++ * ovn-controllers if they receive it. So add a priority-92 flow to ++ * apply the put_arp action on a redirect chassis and drop it on ++ * other chassis. ++ * Note that we are already adding a priority-90 logical flow in the ++ * table S_ROUTER_IN_IP_INPUT to apply the put_arp action if ++ * arp.op == 2. ++ * */ ++ if (op->od->l3dgw_port && op == op->od->l3dgw_port ++ && op->od->l3redirect_port) { ++ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { ++ ds_clear(&match); ++ ds_put_format(&match, ++ "inport == %s && is_chassis_resident(%s) && " ++ "eth.bcast && arp.op == 2 && arp.spa == %s/%u", ++ op->json_key, op->od->l3redirect_port->json_key, ++ op->lrp_networks.ipv4_addrs[i].network_s, ++ op->lrp_networks.ipv4_addrs[i].plen); ++ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92, ++ ds_cstr(&match), ++ "put_arp(inport, arp.spa, arp.sha);"); ++ ds_clear(&match); ++ ds_put_format(&match, ++ "inport == %s && !is_chassis_resident(%s) && " ++ "eth.bcast && arp.op == 2 && arp.spa == %s/%u", ++ op->json_key, op->od->l3redirect_port->json_key, ++ op->lrp_networks.ipv4_addrs[i].network_s, ++ op->lrp_networks.ipv4_addrs[i].plen); ++ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92, ++ ds_cstr(&match), "drop;"); ++ } ++ } ++ + /* A set to hold all load-balancer vips that need ARP responses. */ + struct sset all_ips = SSET_INITIALIZER(&all_ips); + int addr_family; +-- +2.21.0 + diff --git a/SOURCES/0001-Honour-router_preference-for-solicited-RA.patch b/SOURCES/0001-Honour-router_preference-for-solicited-RA.patch new file mode 100644 index 0000000..dc3982a --- /dev/null +++ b/SOURCES/0001-Honour-router_preference-for-solicited-RA.patch @@ -0,0 +1,198 @@ +From 114ca5e976d46bb7285c993879a2837146701386 Mon Sep 17 00:00:00 2001 +Message-Id: <114ca5e976d46bb7285c993879a2837146701386.1593090759.git.lorenzo.bianconi@redhat.com> +From: Gabriele Cerami +Date: Sat, 13 Jun 2020 10:20:23 +0100 +Subject: [PATCH] Honour router_preference for solicited RA + +Replies to router solicitation follow a different flow than periodic RA. +This flow currently does not honour the router_preference configuration. + +This patch modifies the flow to honour the flag, and send +router-preference indications in the reply RA following RFC4191 +specifications + +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1804576 +Signed-off-by: Gabriele Cerami +Signed-off-by: Numan Siddique +Signed-off-by: Lorenzo Bianconi +--- + AUTHORS.rst | 1 + + ovn/lib/actions.c | 29 +++++++++++++++++++++++++++-- + ovn/lib/ovn-l7.h | 5 +++++ + ovn/northd/ovn-northd.c | 6 ++++++ + tests/ovn.at | 26 ++++++++++++++++---------- + 5 files changed, 55 insertions(+), 12 deletions(-) + +--- a/AUTHORS.rst ++++ b/AUTHORS.rst +@@ -145,6 +145,7 @@ Frédéric Tobias Christ fch + Frode Nordahl frode.nordahl@gmail.com + FUJITA Tomonori fujita.tomonori@lab.ntt.co.jp + Gabe Beged-Dov gabe@begeddov.com ++Gabriele Cerami gcerami@redhat.com + Gaetano Catalli gaetano.catalli@gmail.com + Gal Sagie gal.sagie@gmail.com + Genevieve LEsperance glesperance@pivotal.io +--- a/ovn/lib/actions.c ++++ b/ovn/lib/actions.c +@@ -2298,6 +2298,12 @@ parse_put_nd_ra_opts(struct action_conte + } + break; + ++ case ND_RA_FLAG_PRF: ++ ok = (c->string && (!strcmp(c->string, "MEDIUM") || ++ !strcmp(c->string, "HIGH") || ++ !strcmp(c->string, "LOW"))); ++ break; ++ + case ND_OPT_SOURCE_LINKADDR: + ok = c->format == LEX_F_ETHERNET; + slla_present = true; +@@ -2352,9 +2358,22 @@ encode_put_nd_ra_option(const struct ovn + { + struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra); + if (!strcmp(c->string, "dhcpv6_stateful")) { +- ra->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG; ++ ra->mo_flags |= IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG; + } else if (!strcmp(c->string, "dhcpv6_stateless")) { +- ra->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG; ++ ra->mo_flags |= IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG; ++ } ++ break; ++ } ++ ++ case ND_RA_FLAG_PRF: ++ { ++ struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra); ++ if (!strcmp(c->string, "LOW")) { ++ ra->mo_flags |= IPV6_ND_RA_OPT_PRF_LOW; ++ } else if (!strcmp(c->string, "HIGH")) { ++ ra->mo_flags |= IPV6_ND_RA_OPT_PRF_HIGH; ++ } else { ++ ra->mo_flags |= IPV6_ND_RA_OPT_PRF_NORMAL; + } + break; + } +@@ -2435,6 +2454,12 @@ encode_PUT_ND_RA_OPTS(const struct ovnac + encode_put_nd_ra_option(o, ofpacts, ra_offset); + } + ++ /* RFC4191 section 2.2 */ ++ struct ovs_ra_msg *new_ra = ofpbuf_at(ofpacts, ra_offset, sizeof *new_ra); ++ if (ntohs(new_ra->router_lifetime) == 0) { ++ new_ra->mo_flags &= IPV6_ND_RA_OPT_PRF_RESET_MASK; ++ } ++ + encode_finish_controller_op(oc_offset, ofpacts); + } + +--- a/ovn/lib/ovn-l7.h ++++ b/ovn/lib/ovn-l7.h +@@ -295,6 +295,9 @@ nd_ra_opts_destroy(struct hmap *nd_ra_op + + + #define ND_RA_FLAG_ADDR_MODE 0 ++/* all small numbers seems to be all already taken but nothing guarantees this ++ * code will not be assigned by IANA to another option */ ++#define ND_RA_FLAG_PRF 255 + + + /* Default values of various IPv6 Neighbor Discovery protocol options and +@@ -316,11 +319,13 @@ nd_ra_opts_destroy(struct hmap *nd_ra_op + #define IPV6_ND_RA_OPT_PRF_NORMAL 0x00 + #define IPV6_ND_RA_OPT_PRF_HIGH 0x08 + #define IPV6_ND_RA_OPT_PRF_LOW 0x18 ++#define IPV6_ND_RA_OPT_PRF_RESET_MASK 0xe7 + + static inline void + nd_ra_opts_init(struct hmap *nd_ra_opts) + { + nd_ra_opt_add(nd_ra_opts, "addr_mode", ND_RA_FLAG_ADDR_MODE, "str"); ++ nd_ra_opt_add(nd_ra_opts, "router_preference", ND_RA_FLAG_PRF, "str"); + nd_ra_opt_add(nd_ra_opts, "slla", ND_OPT_SOURCE_LINKADDR, "mac"); + nd_ra_opt_add(nd_ra_opts, "prefix", ND_OPT_PREFIX_INFORMATION, "ipv6"); + nd_ra_opt_add(nd_ra_opts, "mtu", ND_OPT_MTU, "uint32"); +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -8551,6 +8551,12 @@ build_lrouter_flows(struct hmap *datapat + ds_put_format(&actions, ", mtu = %u", mtu); + } + ++ const char *prf = smap_get_def( ++ &op->nbrp->ipv6_ra_configs, "router_preference", "MEDIUM"); ++ if (strcmp(prf, "MEDIUM")) { ++ ds_put_format(&actions, ", router_preference = \"%s\"", prf); ++ } ++ + bool add_rs_response_flow = false; + + for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -1326,14 +1326,14 @@ log(severity=notice); + Syntax error at `;' expecting verdict. + + # put_nd_ra_opts +-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64, slla = ae:01:02:03:04:05); +- encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.00.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause) ++reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, router_preference = "HIGH", prefix = aef0::/64, slla = ae:01:02:03:04:05); ++ encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.08.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause) + has prereqs ip6 +-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", slla = ae:01:02:03:04:10, mtu = 1450); ++reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", router_preference = "MEDIUM", slla = ae:01:02:03:04:10, mtu = 1450); + encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.80.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.10.05.01.00.00.00.00.05.aa,pause) + has prereqs ip6 +-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", slla = ae:01:02:03:04:06, prefix = aef0::/64); +- encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.40.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00,pause) ++reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", router_preference = "LOW", slla = ae:01:02:03:04:06, prefix = aef0::/64); ++ encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.58.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00,pause) + has prereqs ip6 + reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64); + slla option not present +@@ -10075,13 +10075,16 @@ reset_pcap_file hv1-vif1 hv1/vif1 + reset_pcap_file hv1-vif2 hv1/vif2 + reset_pcap_file hv1-vif3 hv1/vif3 + +-# Set the MTU to 1500 ++# Set the MTU to 1500, send_periodic to false, preference to LOW + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:mtu=1500 ++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:send_periodic="false" ++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="LOW" + + # Make sure that ovn-controller has installed the corresponding OF Flow. + OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`]) + +-addr_mode=00 ++# addr_mode byte also includes router preference information ++addr_mode=18 + default_prefix_option_config=030440c0ffffffffffffffff00000000 + src_mac=fa163e000003 + src_lla=fe80000000000000f8163efffe000003 +@@ -10106,12 +10109,14 @@ reset_pcap_file hv1-vif1 hv1/vif1 + reset_pcap_file hv1-vif2 hv1/vif2 + reset_pcap_file hv1-vif3 hv1/vif3 + +-# Set the address mode to dhcpv6_stateful ++# Set the address mode to dhcpv6_stateful, router_preference to HIGH + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateful ++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="HIGH" + # Make sure that ovn-controller has installed the corresponding OF Flow. + OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`]) + +-addr_mode=80 ++# addr_mode byte also includes router preference information ++addr_mode=88 + default_prefix_option_config=03044080ffffffffffffffff00000000 + src_mac=fa163e000004 + src_lla=fe80000000000000f8163efffe000004 +@@ -10136,8 +10141,9 @@ reset_pcap_file hv1-vif1 hv1/vif1 + reset_pcap_file hv1-vif2 hv1/vif2 + reset_pcap_file hv1-vif3 hv1/vif3 + +-# Set the address mode to dhcpv6_stateless ++# Set the address mode to dhcpv6_stateless, reset router preference to default + ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateless ++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="MEDIUM" + # Make sure that ovn-controller has installed the corresponding OF Flow. + OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`]) + diff --git a/SOURCES/0001-Make-is_switch-in-lflow.c-a-util-function.patch b/SOURCES/0001-Make-is_switch-in-lflow.c-a-util-function.patch new file mode 100644 index 0000000..be4358e --- /dev/null +++ b/SOURCES/0001-Make-is_switch-in-lflow.c-a-util-function.patch @@ -0,0 +1,94 @@ +From 44a2c882067553a069c1b294644ebbda50d326c8 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Wed, 22 Jan 2020 17:58:23 +0530 +Subject: [PATCH 1/4] Make is_switch() in lflow.c a util function + +This patch renames is_switch() to datapath_is_switch() and moves to +lib/ovn-util.c. + +Upcoming patch will make use of it. + +Acked-by: Han Zhou +Signed-off-by: Numan Siddique + +(cherry-picked from upstream master commit 280429aaa380da43820961a552fe5e18e07578e5) + +Change-Id: I3091105a6cb56857ee2752749c5910f0c9aaa867 +--- + ovn/controller/lflow.c | 11 ++--------- + ovn/lib/ovn-util.c | 6 ++++++ + ovn/lib/ovn-util.h | 3 ++- + 3 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c +index 9dbce91d1..b38a3f38a 100644 +--- a/ovn/controller/lflow.c ++++ b/ovn/controller/lflow.c +@@ -128,13 +128,6 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name) + } + } + +-static bool +-is_switch(const struct sbrec_datapath_binding *ldp) +-{ +- return smap_get(&ldp->external_ids, "logical-switch") != NULL; +- +-} +- + void + lflow_resource_init(struct lflow_resource_ref *lfrr) + { +@@ -682,7 +675,7 @@ consider_logical_flow( + struct ovnact_encode_params ep = { + .lookup_port = lookup_port_cb, + .aux = &aux, +- .is_switch = is_switch(ldp), ++ .is_switch = datapath_is_switch(ldp), + .group_table = group_table, + .meter_table = meter_table, + .lflow_uuid = lflow->header_.uuid, +@@ -706,7 +699,7 @@ consider_logical_flow( + if (m->match.wc.masks.conj_id) { + m->match.flow.conj_id += *conj_id_ofs; + } +- if (is_switch(ldp)) { ++ if (datapath_is_switch(ldp)) { + unsigned int reg_index + = (ingress ? MFF_LOG_INPORT : MFF_LOG_OUTPORT) - MFF_REG0; + int64_t port_id = m->match.flow.regs[reg_index]; +diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c +index 91ec3d62e..9b6efdfc6 100644 +--- a/ovn/lib/ovn-util.c ++++ b/ovn/lib/ovn-util.c +@@ -405,3 +405,9 @@ ovn_logical_flow_hash(const struct uuid *logical_datapath, + hash = hash_string(match, hash); + return hash_string(actions, hash); + } ++ ++bool ++datapath_is_switch(const struct sbrec_datapath_binding *ldp) ++{ ++ return smap_get(&ldp->external_ids, "logical-switch") != NULL; ++} +diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h +index 8461db505..f49f91862 100644 +--- a/ovn/lib/ovn-util.h ++++ b/ovn/lib/ovn-util.h +@@ -23,6 +23,7 @@ struct sbrec_logical_flow; + struct uuid; + struct eth_addr; + struct sbrec_port_binding; ++struct sbrec_datapath_binding; + + struct ipv4_netaddr { + ovs_be32 addr; /* 192.168.10.123 */ +@@ -85,5 +86,5 @@ uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath, + uint8_t table_id, const char *pipeline, + uint16_t priority, + const char *match, const char *actions); +- ++bool datapath_is_switch(const struct sbrec_datapath_binding *); + #endif +-- +2.26.2 + diff --git a/SOURCES/0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch b/SOURCES/0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch new file mode 100644 index 0000000..11f3486 --- /dev/null +++ b/SOURCES/0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch @@ -0,0 +1,55 @@ +From e6975c1475c5de28f3b2d61a683472defefa0d9a Mon Sep 17 00:00:00 2001 +From: Michele Baldessari +Date: Wed, 14 Aug 2019 10:39:13 +0200 +Subject: [PATCH] Make pidfile_is_running more robust against empty pidfiles + +In some of our destructive testing of ovn-dbs inside containers managed +by pacemaker we reached a situation where /var/run/openvswitch had +empty .pid files. The current code does not deal well with them +and pidfile_is_running() returns true in such a case and this confuses +the OCF resource agent. + +- Before this change: +Inside a container run: + killall ovsdb-server; + echo -n '' > /var/run/openvswitch/ovnnb_db.pid; echo -n '' > /var/run/openvswitch/ovnsb_db.pid + +We will observe that the cluster is unable to ever recover because +it believes the ovn processes to be running when they really aren't and +eventually just fails: + podman container set: ovn-dbs-bundle [192.168.24.1:8787/rhosp15/openstack-ovn-northd:pcmklatest] + ovn-dbs-bundle-0 (ocf::ovn:ovndb-servers): Master controller-0 + ovn-dbs-bundle-1 (ocf::ovn:ovndb-servers): Stopped controller-1 + ovn-dbs-bundle-2 (ocf::ovn:ovndb-servers): Slave controller-2 + +- After this change the cluster is able to recover from this state and +correctly start the resource: + podman container set: ovn-dbs-bundle [192.168.24.1:8787/rhosp15/openstack-ovn-northd:pcmklatest] + ovn-dbs-bundle-0 (ocf::ovn:ovndb-servers): Master controller-0 + ovn-dbs-bundle-1 (ocf::ovn:ovndb-servers): Slave controller-1 + ovn-dbs-bundle-2 (ocf::ovn:ovndb-servers): Slave controller-2 + +Signed-off-by: Michele Baldessari +Signed-off-by: Numan Siddique + +(cherry picked from ovn commit 94a13b76bc914a698e4756b70045fa9a371140d1) +--- + ovn/utilities/ovn-ctl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ovn/utilities/ovn-ctl b/ovn/utilities/ovn-ctl +index 7e5cd469c..65f03e28d 100755 +--- a/ovn/utilities/ovn-ctl ++++ b/ovn/utilities/ovn-ctl +@@ -35,7 +35,7 @@ ovn_northd_db_conf_file="$etcdir/ovn-northd-db-params.conf" + + pidfile_is_running () { + pidfile=$1 +- test -e "$pidfile" && pid=`cat "$pidfile"` && pid_exists "$pid" ++ test -e "$pidfile" && [ -s "$pidfile" ] && pid=`cat "$pidfile"` && pid_exists "$pid" + } >/dev/null 2>&1 + + stop_nb_ovsdb() { +-- +2.21.0 + diff --git a/SOURCES/0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch b/SOURCES/0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch new file mode 100644 index 0000000..9c1cf03 --- /dev/null +++ b/SOURCES/0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch @@ -0,0 +1,517 @@ +From 7d45e142d6d1d964ff76b66db78d5e47267fe6b2 Mon Sep 17 00:00:00 2001 +From: Ankur Sharma +Date: Sat, 17 Aug 2019 00:36:43 +0000 +Subject: [PATCH 01/12] OVN: Do not replace router port mac on gateway chassis. + +With 795d7f24ce0e2ed5454e193a059451d237289542 we have added +support for E-W routing on vlan backed networks by replacing +router port macs with chassis macs. + +This replacement of router port mac need NOT be done on +gateway chassis for following reasons: + +a. For N-S traffic, gateway chassis will respond to ARP +for the router port (to which it is attached) and +traffic will be using router port mac as destination mac. + +b. Chassis redirect port is a centralized version of distributed +router port, hence we need not replace its mac with chassis mac +on the resident chassis. + +This patch addresses the same. + +Signed-off-by: Ankur Sharma +Signed-off-by: Numan Siddique + +(cherry-picked from upstream ovn commit 26cdf840108495316f2b53b17b8d3810489e52cb) + +Change-Id: I2e0c712595209fb49ae6e5b90a6b0bf096509dc7 +--- + ovn/controller/lport.c | 20 +++ + ovn/controller/lport.h | 6 + + ovn/controller/physical.c | 18 ++- + ovn/controller/pinctrl.c | 20 +-- + tests/ovn.at | 320 ++++++++++++++++++++++++++++++++++++++ + 5 files changed, 363 insertions(+), 21 deletions(-) + +diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c +index cc5c5fbb2..3cf54d16f 100644 +--- a/ovn/controller/lport.c ++++ b/ovn/controller/lport.c +@@ -17,6 +17,7 @@ + + #include "lib/sset.h" + #include "lport.h" ++#include "ha-chassis.h" + #include "hash.h" + #include "openvswitch/vlog.h" + #include "ovn/lib/ovn-sb-idl.h" +@@ -64,6 +65,25 @@ lport_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, + return retval; + } + ++bool ++lport_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name, ++ const struct sbrec_chassis *chassis, ++ const struct sset *active_tunnels, ++ const char *port_name) ++{ ++ const struct sbrec_port_binding *pb ++ = lport_lookup_by_name(sbrec_port_binding_by_name, port_name); ++ if (!pb || !pb->chassis) { ++ return false; ++ } ++ if (strcmp(pb->type, "chassisredirect")) { ++ return pb->chassis == chassis; ++ } else { ++ return ha_chassis_group_is_active(pb->ha_chassis_group, ++ active_tunnels, chassis); ++ } ++} ++ + const struct sbrec_datapath_binding * + datapath_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, + uint64_t dp_key) +diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h +index 7dcd5bee0..89e3b85eb 100644 +--- a/ovn/controller/lport.h ++++ b/ovn/controller/lport.h +@@ -23,6 +23,7 @@ struct sbrec_chassis; + struct sbrec_datapath_binding; + struct sbrec_multicast_group; + struct sbrec_port_binding; ++struct sset; + + + /* Database indexes. +@@ -49,4 +50,9 @@ const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name( + struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath, + const struct sbrec_datapath_binding *, const char *name); + ++bool ++lport_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name, ++ const struct sbrec_chassis *chassis, ++ const struct sset *active_tunnels, ++ const char *port_name); + #endif /* ovn/lport.h */ +diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c +index 7ad3e6f8f..425110d0b 100644 +--- a/ovn/controller/physical.c ++++ b/ovn/controller/physical.c +@@ -228,9 +228,12 @@ get_zone_ids(const struct sbrec_port_binding *binding, + } + + static void +-put_replace_router_port_mac_flows(const struct ++put_replace_router_port_mac_flows(struct ovsdb_idl_index ++ *sbrec_port_binding_by_name, ++ const struct + sbrec_port_binding *localnet_port, + const struct sbrec_chassis *chassis, ++ const struct sset *active_tunnels, + const struct hmap *local_datapaths, + struct ofpbuf *ofpacts_p, + ofp_port_t ofport, +@@ -270,6 +273,16 @@ put_replace_router_port_mac_flows(const struct + struct eth_addr router_port_mac; + struct match match; + struct ofpact_mac *replace_mac; ++ char *cr_peer_name = xasprintf("cr-%s", rport_binding->logical_port); ++ if (lport_is_chassis_resident(sbrec_port_binding_by_name, ++ chassis, active_tunnels, ++ cr_peer_name)) { ++ /* If a router port's chassisredirect port is ++ * resident on this chassis, then we need not do mac replace. */ ++ free(cr_peer_name); ++ continue; ++ } ++ free(cr_peer_name); + + /* Table 65, priority 150. + * ======================= +@@ -787,7 +800,8 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, + &match, ofpacts_p, &binding->header_.uuid); + + if (!strcmp(binding->type, "localnet")) { +- put_replace_router_port_mac_flows(binding, chassis, ++ put_replace_router_port_mac_flows(sbrec_port_binding_by_name, ++ binding, chassis, active_tunnels, + local_datapaths, ofpacts_p, + ofport, flow_table); + } +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 25b18789e..c6dd69fb3 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -3950,24 +3950,6 @@ get_localnet_vifs_l3gwports( + sbrec_port_binding_index_destroy_row(target); + } + +-static bool +-pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name, +- const struct sbrec_chassis *chassis, +- const struct sset *active_tunnels, +- const char *port_name) +-{ +- const struct sbrec_port_binding *pb +- = lport_lookup_by_name(sbrec_port_binding_by_name, port_name); +- if (!pb || !pb->chassis) { +- return false; +- } +- if (strcmp(pb->type, "chassisredirect")) { +- return pb->chassis == chassis; +- } else { +- return ha_chassis_group_is_active(pb->ha_chassis_group, +- active_tunnels, chassis); +- } +-} + + /* Extracts the mac, IPv4 and IPv6 addresses, and logical port from + * 'addresses' which should be of the format 'MAC [IP1 IP2 ..] +@@ -4048,7 +4030,7 @@ consider_nat_address(struct ovsdb_idl_index *sbrec_port_binding_by_name, + char *lport = NULL; + if (!extract_addresses_with_port(nat_address, laddrs, &lport) + || (!lport && !strcmp(pb->type, "patch")) +- || (lport && !pinctrl_is_chassis_resident( ++ || (lport && !lport_is_chassis_resident( + sbrec_port_binding_by_name, chassis, + active_tunnels, lport))) { + destroy_lport_addresses(laddrs); +diff --git a/tests/ovn.at b/tests/ovn.at +index 335ccd8c5..b7ee7beed 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -29,6 +29,22 @@ m4_define([OVN_CHECK_PACKETS], + [ovn_check_packets__ "$1" "$2" + AT_CHECK([sort $rcv_text], [0], [expout])]) + ++m4_define([OVN_CHECK_PACKETS_REMOVE_BROADCAST], ++ [ovn_check_packets__ () { ++ echo "checking packets in $1 against $2:" ++ rcv_pcap=$1 ++ exp_text=$2 ++ exp_n=`wc -l < "$exp_text"` ++ OVS_WAIT_UNTIL( ++ [$PYTHON "$top_srcdir/ovs/utilities/ovs-pcap.in" $rcv_pcap > $rcv_text ++ sed -i '/ffffffffffff/d' $rcv_text ++ rcv_n=`wc -l < "$rcv_text"` ++ echo "rcv_n=$rcv_n exp_n=$exp_n" ++ test $rcv_n -ge $exp_n]) ++ sort $exp_text > expout ++ } ++]) ++ + AT_BANNER([OVN components]) + + AT_SETUP([ovn -- lexer]) +@@ -15976,3 +15992,307 @@ OVS_WAIT_UNTIL([ + + OVN_CLEANUP([hv1]) + AT_CLEANUP ++ ++ ++AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S ARP handling]) ++ovn_start ++ ++# In this test cases we create 3 switches, all connected to same ++# physical network (through br-phys on each HV). LS1 and LS2 have ++# 1 VIF each. Each HV has 1 VIF port. The first digit ++# of VIF port name indicates the hypervisor it is bound to, e.g. ++# lp23 means VIF 3 on hv2. ++# ++# All the switches are connected to a logical router "router". ++# ++# Each switch's VLAN tag and their logical switch ports are: ++# - ls1: ++# - tagged with VLAN 101 ++# - ports: lp11 ++# - ls2: ++# - tagged with VLAN 201 ++# - ports: lp22 ++# - ls-underlay: ++# - tagged with VLAN 1000 ++# Note: a localnet port is created for each switch to connect to ++# physical network. ++ ++for i in 1 2; do ++ ls_name=ls$i ++ ovn-nbctl ls-add $ls_name ++ ln_port_name=ln$i ++ if test $i -eq 1; then ++ ovn-nbctl lsp-add $ls_name $ln_port_name "" 101 ++ elif test $i -eq 2; then ++ ovn-nbctl lsp-add $ls_name $ln_port_name "" 201 ++ fi ++ ovn-nbctl lsp-set-addresses $ln_port_name unknown ++ ovn-nbctl lsp-set-type $ln_port_name localnet ++ ovn-nbctl lsp-set-options $ln_port_name network_name=phys ++done ++ ++# lsp_to_ls LSP ++# ++# Prints the name of the logical switch that contains LSP. ++lsp_to_ls () { ++ case $1 in dnl ( ++ lp?[[11]]) echo ls1 ;; dnl ( ++ lp?[[12]]) echo ls2 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_hv () { ++ case $1 in dnl ( ++ vif[[1]]?) echo hv1 ;; dnl ( ++ vif[[2]]?) echo hv2 ;; dnl ( ++ vif?[[north]]?) echo hv4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++net_add n1 ++for i in 1 2; do ++ sim_add hv$i ++ as hv$i ++ ovs-vsctl add-br br-phys ++ ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++ ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i" ++ ovn_attach n1 br-phys 192.168.0.$i ++ ++ ovs-vsctl add-port br-int vif$i$i -- \ ++ set Interface vif$i$i external-ids:iface-id=lp$i$i \ ++ options:tx_pcap=hv$i/vif$i$i-tx.pcap \ ++ options:rxq_pcap=hv$i/vif$i$i-rx.pcap \ ++ ofport-request=$i$i ++ ++ lsp_name=lp$i$i ++ ls_name=$(lsp_to_ls $lsp_name) ++ ++ ovn-nbctl lsp-add $ls_name $lsp_name ++ ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i" ++ ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i ++ ++ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup]) ++ ++done ++ ++ovn-nbctl ls-add ls-underlay ++ovn-nbctl lsp-add ls-underlay ln3 "" 1000 ++ovn-nbctl lsp-set-addresses ln3 unknown ++ovn-nbctl lsp-set-type ln3 localnet ++ovn-nbctl lsp-set-options ln3 network_name=phys ++ ++ovn-nbctl ls-add ls-north ++ovn-nbctl lsp-add ls-north ln4 "" 1000 ++ovn-nbctl lsp-set-addresses ln4 unknown ++ovn-nbctl lsp-set-type ln4 localnet ++ovn-nbctl lsp-set-options ln4 network_name=phys ++ ++# Add a VM on ls-north ++ovn-nbctl lsp-add ls-north lp-north ++ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10" ++ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11 ++ ++# Add 3rd hypervisor ++sim_add hv3 ++as hv3 ovs-vsctl add-br br-phys ++as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++as hv3 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33" ++as hv3 ovn_attach n1 br-phys 192.168.0.3 ++ ++# Add 4th hypervisor ++sim_add hv4 ++as hv4 ovs-vsctl add-br br-phys ++as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++as hv4 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44" ++as hv4 ovn_attach n1 br-phys 192.168.0.4 ++ ++as hv4 ovs-vsctl add-port br-int vif-north -- \ ++ set Interface vif-north external-ids:iface-id=lp-north \ ++ options:tx_pcap=hv4/vif-north-tx.pcap \ ++ options:rxq_pcap=hv4/vif-north-rx.pcap \ ++ ofport-request=44 ++ ++ovn-nbctl lr-add router ++ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24 ++ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24 ++ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24 ++ ++ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router \ ++ options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router ++ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router \ ++ options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router ++ovn-nbctl lsp-add ls-underlay underlay-to-router -- set Logical_Switch_Port \ ++ underlay-to-router type=router \ ++ options:router-port=router-to-underlay \ ++ -- lsp-set-addresses underlay-to-router router ++ ++ ++OVN_POPULATE_ARP ++ ++# lsp_to_ls LSP ++# ++# Prints the name of the logical switch that contains LSP. ++lsp_to_ls () { ++ case $1 in dnl ( ++ lp?[[11]]) echo ls1 ;; dnl ( ++ lp?[[12]]) echo ls2 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_ls () { ++ case $1 in dnl ( ++ vif?[[11]]) echo ls1 ;; dnl ( ++ vif?[[12]]) echo ls2 ;; dnl ( ++ vif-north) echo ls-north ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++hv_to_num () { ++ case $1 in dnl ( ++ hv1) echo 1 ;; dnl ( ++ hv2) echo 2 ;; dnl ( ++ hv3) echo 3 ;; dnl ( ++ hv4) echo 4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_num () { ++ case $1 in dnl ( ++ vif22) echo 22 ;; dnl ( ++ vif21) echo 21 ;; dnl ( ++ vif11) echo 11 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_hv () { ++ case $1 in dnl ( ++ vif[[1]]?) echo hv1 ;; dnl ( ++ vif[[2]]?) echo hv2 ;; dnl ( ++ vif-north) echo hv4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_lrp () { ++ echo router-to-`vif_to_ls $1` ++} ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++# test_arp INPORT SHA SPA TPA [REPLY_HA] ++# ++# Causes a packet to be received on INPORT. The packet is an ARP ++# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then ++# it should be the hardware address of the target to expect to receive in an ++# ARP reply; otherwise no reply is expected. ++# ++# INPORT is an logical switch port number, e.g. 11 for vif11. ++# SHA and REPLY_HA are each 12 hex digits. ++# SPA and TPA are each 8 hex digits. ++test_arp() { ++ local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 ++ local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} ++ hv=`vif_to_hv $inport` ++ as $hv ovs-appctl netdev-dummy/receive $inport $request ++ ++ if test X$reply_ha = X; then ++ # Expect to receive the broadcast ARP on the other logical switch ports ++ # if no reply is expected. ++ local i j ++ for i in 1 2 3; do ++ for j in 1 2 3; do ++ if test $i$j != $inport; then ++ echo $request >> $i$j.expected ++ fi ++ done ++ done ++ else ++ # Expect to receive the reply, if any. ++ local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa} ++ local reply_vid=${sha}${reply_ha}810003e808060001080006040002${reply_ha}${tpa}${sha}${spa} ++ echo $reply_vid >> ${inport}_vid.expected ++ echo $reply >> $inport.expected ++ fi ++} ++ ++sip=`ip_to_hex 172 31 0 10` ++tip=`ip_to_hex 172 31 0 1` ++ ++# Set a hypervisor as gateway chassis, for router port 172.31.0.1 ++ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3 ++ovn-nbctl --wait=sb sync ++ ++# Dump a bunch of info helpful for debugging if there's a failure. ++ ++echo "------ OVN dump ------" ++ovn-nbctl show ++ovn-sbctl show ++ovn-sbctl list port_binding ++ovn-sbctl list mac_binding ++ ++echo "------ hv1 dump ------" ++as hv1 ovs-vsctl show ++as hv1 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv2 dump ------" ++as hv2 ovs-vsctl show ++as hv2 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv3 dump ------" ++as hv3 ovs-vsctl show ++as hv3 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv4 dump ------" ++as hv4 ovs-vsctl show ++as hv4 ovs-vsctl list Open_Vswitch ++ ++OVS_WAIT_UNTIL([test x`ovn-sbctl --bare --columns chassis find port_binding logical_port=cr-router-to-underlay | wc -l` = x1]) ++ ++test_arp vif-north f0f000000011 $sip $tip 000001010207 ++ ++# Confirm that vif-north gets a single ARP reply ++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected]) ++ ++# Confirm that only redirect chassis allowed arp resolution. ++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv3/br-phys_n1-tx.pcap], [vif-north_vid.expected]) ++ ++# Confirm that other OVN chassis did not generate ARP reply. ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap > hv1/br-phys_n1-tx.packets ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap > hv2/br-phys_n1-tx.packets ++ ++AT_CHECK([grep 000001010207 hv1/br-phys_n1-tx.packets | wc -l], [0], [[0 ++]]) ++AT_CHECK([grep 000001010207 hv2/br-phys_n1-tx.packets | wc -l], [0], [[0 ++]]) ++ ++echo "----------- Post Traffic hv1 dump -----------" ++as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int ++as hv1 ovs-appctl fdb/show br-phys ++ ++echo "----------- Post Traffic hv2 dump -----------" ++as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int ++as hv2 ovs-appctl fdb/show br-phys ++ ++echo "----------- Post Traffic hv3 dump -----------" ++as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int ++as hv3 ovs-appctl fdb/show br-phys ++ ++echo "----------- Post Traffic hv4 dump -----------" ++as hv4 ovs-ofctl -O OpenFlow13 dump-flows br-int ++as hv4 ovs-appctl fdb/show br-phys ++ ++OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) ++ ++AT_CLEANUP +-- +2.23.0 + diff --git a/SOURCES/0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch b/SOURCES/0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch new file mode 100644 index 0000000..296a80a --- /dev/null +++ b/SOURCES/0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch @@ -0,0 +1,34 @@ +From 1a3e6dfb5e2fd5bbb625f637792f91a02767ff3b Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Mon, 30 Sep 2019 18:45:20 +0530 +Subject: [PATCH] Partially revert "Exclude inport and outport symbol tables + from conjunction." + +This partially revers the commit - 298701dbc99645700be41680a43d049cb061847a +as the commit [1] disables the conjunction. + +We still need the changes to the tests/ovn.at file. + +CC: Han Zhou +Acked-by: Han Zhou +Signed-off-by: Numan Siddique +--- + lib/expr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c +index 64ea0aafa..9b9b6bcca 100644 +--- a/ovn/lib/expr.c ++++ b/ovn/lib/expr.c +@@ -1517,7 +1517,7 @@ expr_symtab_add_string(struct shash *symtab, const char *name, + const struct mf_field *field = mf_from_id(id); + struct expr_symbol *symbol; + +- symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, true, ++ symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false, + field->writable); + symbol->field = field; + return symbol; +-- +2.23.0 + diff --git a/SOURCES/0001-Prevent-erroneous-duplicate-IP-address-messages.patch b/SOURCES/0001-Prevent-erroneous-duplicate-IP-address-messages.patch new file mode 100644 index 0000000..e83f975 --- /dev/null +++ b/SOURCES/0001-Prevent-erroneous-duplicate-IP-address-messages.patch @@ -0,0 +1,50 @@ +From 21c29d5b0ca10f36dc623866de74b8bc51def714 Mon Sep 17 00:00:00 2001 +From: Mark Michelson +Date: Thu, 8 Aug 2019 17:00:05 -0400 +Subject: [PATCH] Prevent erroneous duplicate IP address messages. + +When using dynamic address assignment for logical switches, OVN reserves +the first address in the subnet for the attached router port to use. + +In commit 488d153ee87841c042af05bc0eb8b5481aaa98cf, the IPAM code was +modified to add assigned router port addresses to IPAM. The use case for +this was when a switch was joined to multiple routers, and all router +addresses were dynamically assigned. + +However, that commit also made it so that when a router rightly claimed +the first address in the subnet, ovn-northd would issue a warning about +a duplicate IP address being set. This change fixes the issue by adding +a special case so that we don't add the router's IP address to IPAM if +it is the first address in the subnet. This prevents the warning message +from appearing. + +Signed-off-by: Mark Michelson +Acked-by: Numan Siddique +Acked-by: Han ZHou +--- + northd/ovn-northd.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 78246506c..4163b8e7f 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -1256,7 +1256,14 @@ ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op) + + for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) { + uint32_t ip = ntohl(lrp_networks.ipv4_addrs[i].addr); +- ipam_insert_ip(op->peer->od, ip); ++ /* If the router has the first IP address of the subnet, don't add ++ * it to IPAM. We already added this when we initialized IPAM for ++ * the datapath. This will just result in an erroneous message ++ * about a duplicate IP address. ++ */ ++ if (ip != op->peer->od->ipam_info.start_ipv4) { ++ ipam_insert_ip(op->peer->od, ip); ++ } + } + + destroy_lport_addresses(&lrp_networks); +-- +2.14.5 + diff --git a/SOURCES/0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch b/SOURCES/0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch new file mode 100644 index 0000000..23df75c --- /dev/null +++ b/SOURCES/0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch @@ -0,0 +1,68 @@ +From 93ce545cbe5699814713913eff94a2b3dec7d7f7 Mon Sep 17 00:00:00 2001 +From: Timothy Redaelli +Date: Thu, 16 Jan 2020 15:25:13 +0100 +Subject: [PATCH] RHEL-only: Add compatibility with openvswitch2.11 < 2.11.0-43 + +This is done by adding + EnvironmentFile=/etc/openvswitch/default.conf + EnvironmentFile=-/etc/sysconfig/openvswitch +to the service files. + +Signed-off-by: Timothy Redaelli +--- + rhel/usr_lib_systemd_system_ovn-controller-vtep.service | 4 +++- + rhel/usr_lib_systemd_system_ovn-controller.service | 4 +++- + rhel/usr_lib_systemd_system_ovn-northd.service | 4 +++- + 3 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/rhel/usr_lib_systemd_system_ovn-controller-vtep.service b/rhel/usr_lib_systemd_system_ovn-controller-vtep.service +index b1e239f57..852cd1955 100644 +--- a/rhel/usr_lib_systemd_system_ovn-controller-vtep.service ++++ b/rhel/usr_lib_systemd_system_ovn-controller-vtep.service +@@ -38,8 +38,10 @@ PIDFile=/var/run/openvswitch/ovn-controller-vtep.pid + Restart=on-failure + Environment=OVN_DB=unix:%t/openvswitch/ovnsb_db.sock + Environment=VTEP_DB=unix:%t/openvswitch/db.sock ++EnvironmentFile=/etc/openvswitch/default.conf ++EnvironmentFile=-/etc/sysconfig/openvswitch + EnvironmentFile=-/etc/sysconfig/ovn-controller-vtep +-EnvironmentFile=/run/openvswitch.useropts ++EnvironmentFile=-/run/openvswitch.useropts + ExecStart=/usr/share/openvswitch/scripts/ovn-ctl \ + --db-sb-sock=${OVN_DB} --db-sock=${VTEP_DB} \ + --ovn-user=${OVS_USER_ID} \ +diff --git a/rhel/usr_lib_systemd_system_ovn-controller.service b/rhel/usr_lib_systemd_system_ovn-controller.service +index 335cd5a52..f0df89331 100644 +--- a/rhel/usr_lib_systemd_system_ovn-controller.service ++++ b/rhel/usr_lib_systemd_system_ovn-controller.service +@@ -23,8 +23,10 @@ After=openvswitch.service + Type=forking + PIDFile=/var/run/openvswitch/ovn-controller.pid + Restart=on-failure ++EnvironmentFile=/etc/openvswitch/default.conf ++EnvironmentFile=-/etc/sysconfig/openvswitch + EnvironmentFile=-/etc/sysconfig/ovn-controller +-EnvironmentFile=/run/openvswitch.useropts ++EnvironmentFile=-/run/openvswitch.useropts + ExecStart=/usr/share/openvswitch/scripts/ovn-ctl --no-monitor \ + --ovn-user=${OVS_USER_ID} \ + start_controller $OVN_CONTROLLER_OPTS +diff --git a/rhel/usr_lib_systemd_system_ovn-northd.service b/rhel/usr_lib_systemd_system_ovn-northd.service +index ea8c191e3..82f3c4cfc 100644 +--- a/rhel/usr_lib_systemd_system_ovn-northd.service ++++ b/rhel/usr_lib_systemd_system_ovn-northd.service +@@ -23,8 +23,10 @@ After=openvswitch.service + Type=oneshot + RemainAfterExit=yes + Environment=OVS_RUNDIR=%t/openvswitch OVS_DBDIR=/var/lib/openvswitch ++EnvironmentFile=/etc/openvswitch/default.conf ++EnvironmentFile=-/etc/sysconfig/openvswitch + EnvironmentFile=-/etc/sysconfig/ovn-northd +-EnvironmentFile=/run/openvswitch.useropts ++EnvironmentFile=-/run/openvswitch.useropts + ExecStartPre=-/usr/bin/chown -R ${OVS_USER_ID} ${OVS_DBDIR} + ExecStart=/usr/share/openvswitch/scripts/ovn-ctl \ + --ovs-user=${OVS_USER_ID} --ovn-user=${OVS_USER_ID} \ +-- +2.24.1 + diff --git a/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch b/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch new file mode 100644 index 0000000..0b3e1fb --- /dev/null +++ b/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch @@ -0,0 +1,73 @@ +From 37affe59c85fa9be1cacf8a6f0681b6327ca36e0 Mon Sep 17 00:00:00 2001 +Message-Id: <37affe59c85fa9be1cacf8a6f0681b6327ca36e0.1594290284.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Mon, 27 Apr 2020 17:45:20 +0200 +Subject: [PATCH] Rely on unique name for ovn qos meters + +ovn currently identifies qos meters according to the rate and burst values +configured. Doing so 2 meters on the same hv assigned to 2 different logical +switch ports and configured with the same values for rate and burst will be +mapped to the same ovs kernel mater and will share the bandwidth. +Fix this behavior making qos meter name unique + +Tested-By: Maciej Jozefczyk +Acked-by: Han Zhou +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/ofctrl.c | 2 +- + ovn/lib/actions.c | 11 ++++++----- + tests/ovn.at | 10 ++++++++++ + 3 files changed, 17 insertions(+), 6 deletions(-) + +--- a/ovn/controller/ofctrl.c ++++ b/ovn/controller/ofctrl.c +@@ -969,7 +969,7 @@ add_meter_string(struct ovn_extend_table + enum ofputil_protocol usable_protocols; + char *meter_string = xasprintf("meter=%"PRIu32",%s", + m_desired->table_id, +- &m_desired->name[9]); ++ &m_desired->name[52]); + char *error = parse_ofp_meter_mod_str(&mm, meter_string, OFPMC13_ADD, + &usable_protocols); + if (!error) { +--- a/ovn/lib/actions.c ++++ b/ovn/lib/actions.c +@@ -2670,12 +2670,13 @@ encode_SET_METER(const struct ovnact_set + * describes the meter itself. */ + char *name; + if (cl->burst) { +- name = xasprintf("__string: kbps burst stats bands=type=drop " +- "rate=%"PRId64" burst_size=%"PRId64"", cl->rate, +- cl->burst); ++ name = xasprintf("__string: uuid "UUID_FMT" kbps burst stats " ++ "bands=type=drop rate=%"PRId64" burst_size=%"PRId64, ++ UUID_ARGS(&ep->lflow_uuid), cl->rate, cl->burst); + } else { +- name = xasprintf("__string: kbps stats bands=type=drop " +- "rate=%"PRId64"", cl->rate); ++ name = xasprintf("__string: uuid "UUID_FMT" kbps stats " ++ "bands=type=drop rate=%"PRId64, ++ UUID_ARGS(&ep->lflow_uuid), cl->rate); + } + + table_id = ovn_extend_table_assign_id(ep->meter_table, name, +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -7685,6 +7685,16 @@ AT_CHECK([as hv ovs-ofctl dump-flows br- + AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep rate=11123 | wc -l], [0], [0 + ]) + ++# Check multiple qos meters ++ovn-nbctl qos-del lsw0 ++ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp1" && is_chassis_resident("lp1")' rate=100000 burst=100000 ++ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp2" && is_chassis_resident("lp2")' rate=100000 burst=100000 ++ovn-nbctl qos-add lsw0 to-lport 1002 'inport=="lp1" && is_chassis_resident("lp1")' rate=100001 burst=100001 ++ovn-nbctl qos-add lsw0 to-lport 1002 'inport=="lp2" && is_chassis_resident("lp2")' rate=100001 burst=100001 ++ ++AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter | wc -l], [0], [4 ++]) ++ + OVN_CLEANUP([hv]) + AT_CLEANUP + diff --git a/SOURCES/0001-Revert-conjunctive-match-removal-patches.patch b/SOURCES/0001-Revert-conjunctive-match-removal-patches.patch new file mode 100644 index 0000000..cc911b3 --- /dev/null +++ b/SOURCES/0001-Revert-conjunctive-match-removal-patches.patch @@ -0,0 +1,1383 @@ +From b9d4ba4f2fca05e1af38f091f755ba1d2429042e Mon Sep 17 00:00:00 2001 +From: Mark Michelson +Date: Fri, 25 Oct 2019 09:06:46 -0400 +Subject: [PATCH 1/2] Revert conjunctive match removal patches. + +This partially reverts commits 6f914327a55aaab11507db0911b08d695e17ce2a +and 298701dbc99645700be41680a43d049cb061847a. This restores the behavior +of making conjunctive matches, and it restores the tests to their state. + +The one thing it does not revert is the addition of the +force_cross_product variable in expr.c. This variable will be used in a +later commit in this series. Instead of removing it, we just set it +"false" to restore previous behavior. + +With this commit, the conjunctive match misbehavior described in commit +298701dbc99645700be41680a43d049cb061847a is restored. However, this will +be fixed in an upcoming commit in this series. + +Signed-off-by: Mark Michelson +--- + ovn/TODO.rst | 10 - + ovn/lib/expr.c | 9 +- + tests/ovn.at | 1250 +++------------------------------------------------------- + 3 files changed, 61 insertions(+), 1208 deletions(-) + +diff --git a/ovn/TODO.rst b/ovn/TODO.rst +index ed55ea236..943d9bf81 100644 +--- a/ovn/TODO.rst ++++ b/ovn/TODO.rst +@@ -145,13 +145,3 @@ OVN To-do List + * Support FTP ALGs. + + * Support reject action. +- +-* Conjunction: Conjunction is disabled in OVN. This needs to be revisisted +- to enable conjunction again after addressing the issues related to it. +- Like, if there are multiple ACLs with overlapping Conjunction matches, +- conjunction flows are not added properly. +- Eg. match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} && +- tcp.dst >= 800 && tcp.dst <= 900) actions=drop +- +- match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} && +- tcp.dst >= 1000 && tcp.dst <= 2000) actions=allow +diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c +index 9b9b6bcca..71de61543 100644 +--- a/ovn/lib/expr.c ++++ b/ovn/lib/expr.c +@@ -42,13 +42,8 @@ VLOG_DEFINE_THIS_MODULE(expr); + * + * match 2 - ip4.src == {IP1, IP2} && tcp.dst >=700 && tcp.src <=800 + * action - allow. +- * +- * To handle this issue temporarily force crossproduct so that conjunction +- * flows are not generated. +- * +- * Remove this once fixed. +- * */ +-static bool force_crossproduct = true; ++ */ ++static bool force_crossproduct = false; + + static struct expr *parse_and_annotate(const char *s, + const struct shash *symtab, +diff --git a/tests/ovn.at b/tests/ovn.at +index 22b272a60..641a646fc 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -589,22 +589,6 @@ ip,reg14=0x6 + ipv6,reg14=0x5 + ipv6,reg14=0x6 + ]) +-AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.dst == {500, 501}'], [0], [dnl +-tcp,reg14=0x5,tp_dst=500 +-tcp,reg14=0x5,tp_dst=501 +-tcp,reg14=0x6,tp_dst=500 +-tcp,reg14=0x6,tp_dst=501 +-]) +-AT_CHECK([expr_to_flow 'outport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.src == {400, 401} && tcp.dst == {500, 501}'], [0], [dnl +-tcp,reg15=0x5,tp_src=400,tp_dst=500 +-tcp,reg15=0x5,tp_src=400,tp_dst=501 +-tcp,reg15=0x5,tp_src=401,tp_dst=500 +-tcp,reg15=0x5,tp_src=401,tp_dst=501 +-tcp,reg15=0x6,tp_src=400,tp_dst=500 +-tcp,reg15=0x6,tp_src=400,tp_dst=501 +-tcp,reg15=0x6,tp_src=401,tp_dst=500 +-tcp,reg15=0x6,tp_src=401,tp_dst=501 +-]) + AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl + (no flows) + ]) +@@ -709,14 +693,6 @@ reg15=0x11 + reg15=0x12 + reg15=0x13 + ]) +-AT_CHECK([expr_to_flow 'outport == @pg1 && ip4.src == {10.0.0.4, 10.0.0.5}'], [0], [dnl +-ip,reg15=0x11,nw_src=10.0.0.4 +-ip,reg15=0x11,nw_src=10.0.0.5 +-ip,reg15=0x12,nw_src=10.0.0.4 +-ip,reg15=0x12,nw_src=10.0.0.5 +-ip,reg15=0x13,nw_src=10.0.0.4 +-ip,reg15=0x13,nw_src=10.0.0.5 +-]) + AT_CHECK([expr_to_flow 'outport == {@pg_empty}'], [0], [dnl + (no flows) + ]) +@@ -725,27 +701,22 @@ reg15=0x11 + ]) + AT_CLEANUP + +-AT_SETUP([ovn -- converting expressions to flows -- no conjunction]) +-AT_KEYWORDS([no conjunction]) ++AT_SETUP([ovn -- converting expressions to flows -- conjunction]) ++AT_KEYWORDS([conjunction]) + expr_to_flow () { + echo "$1" | ovstest test-ovn expr-to-flows | sort + } + +-# conjunction is disabled in OVN until some of the issues +-# related to conjunction flows are fixed. +-# expr-to-flows should not generate any conjunction flows. + lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \ + ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3}" + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl +-ip,nw_src=10.0.0.1,nw_dst=20.0.0.1 +-ip,nw_src=10.0.0.1,nw_dst=20.0.0.2 +-ip,nw_src=10.0.0.1,nw_dst=20.0.0.3 +-ip,nw_src=10.0.0.2,nw_dst=20.0.0.1 +-ip,nw_src=10.0.0.2,nw_dst=20.0.0.2 +-ip,nw_src=10.0.0.2,nw_dst=20.0.0.3 +-ip,nw_src=10.0.0.3,nw_dst=20.0.0.1 +-ip,nw_src=10.0.0.3,nw_dst=20.0.0.2 +-ip,nw_src=10.0.0.3,nw_dst=20.0.0.3 ++conj_id=1,ip ++ip,nw_dst=20.0.0.1: conjunction(1, 0/2) ++ip,nw_dst=20.0.0.2: conjunction(1, 0/2) ++ip,nw_dst=20.0.0.3: conjunction(1, 0/2) ++ip,nw_src=10.0.0.1: conjunction(1, 1/2) ++ip,nw_src=10.0.0.2: conjunction(1, 1/2) ++ip,nw_src=10.0.0.3: conjunction(1, 1/2) + ]) + + lflow="ip && (!ct.est || (ct.est && ct_label.blocked == 1))" +@@ -759,12 +730,12 @@ ct_state=-est+trk,ipv6 + lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \ + ip4.dst == {20.0.0.1, 20.0.0.2}" + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl +-ip,nw_src=10.0.0.1,nw_dst=20.0.0.1 +-ip,nw_src=10.0.0.1,nw_dst=20.0.0.2 +-ip,nw_src=10.0.0.2,nw_dst=20.0.0.1 +-ip,nw_src=10.0.0.2,nw_dst=20.0.0.2 +-ip,nw_src=10.0.0.3,nw_dst=20.0.0.1 +-ip,nw_src=10.0.0.3,nw_dst=20.0.0.2 ++conj_id=1,ip ++ip,nw_dst=20.0.0.1: conjunction(1, 0/2) ++ip,nw_dst=20.0.0.2: conjunction(1, 0/2) ++ip,nw_src=10.0.0.1: conjunction(1, 1/2) ++ip,nw_src=10.0.0.2: conjunction(1, 1/2) ++ip,nw_src=10.0.0.3: conjunction(1, 1/2) + ]) + + lflow="ip4 && ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \ +@@ -772,60 +743,19 @@ ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3} && \ + tcp.dst >= 1000 && tcp.dst <= 1010" + + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1000 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1001 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1010 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1000 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1001 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1010 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1000 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1001 +-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1010 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1000 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1001 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1010 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1000 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1001 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1010 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1000 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1001 +-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1010 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1000 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1001 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1010 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1000 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1001 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1010 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1000 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1001 +-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1010 ++conj_id=1,tcp ++tcp,nw_dst=20.0.0.1: conjunction(1, 0/3) ++tcp,nw_dst=20.0.0.2: conjunction(1, 0/3) ++tcp,nw_dst=20.0.0.3: conjunction(1, 0/3) ++tcp,nw_src=10.0.0.1: conjunction(1, 1/3) ++tcp,nw_src=10.0.0.2: conjunction(1, 1/3) ++tcp,nw_src=10.0.0.3: conjunction(1, 1/3) ++tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/3) ++tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/3) ++tcp,tp_dst=0x3f0/0xfffe: conjunction(1, 2/3) ++tcp,tp_dst=1000: conjunction(1, 2/3) ++tcp,tp_dst=1001: conjunction(1, 2/3) ++tcp,tp_dst=1010: conjunction(1, 2/3) + ]) + + lflow="ip4 && ip4.src == {10.0.0.4, 10.0.0.5, 10.0.0.6} && \ +@@ -834,1101 +764,41 @@ tcp.dst <= 2000 && tcp.src >=1000 && tcp.src <= 2000) \ + || ip4.dst == 20.0.0.5 || ip4.dst == 20.0.0.6)" + + AT_CHECK([expr_to_flow "$lflow"], [0], [dnl ++conj_id=1,tcp + ip,nw_src=10.0.0.4,nw_dst=20.0.0.5 + ip,nw_src=10.0.0.4,nw_dst=20.0.0.6 + ip,nw_src=10.0.0.5,nw_dst=20.0.0.5 + ip,nw_src=10.0.0.5,nw_dst=20.0.0.6 + ip,nw_src=10.0.0.6,nw_dst=20.0.0.5 + ip,nw_src=10.0.0.6,nw_dst=20.0.0.6 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001 +-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000 ++tcp,nw_dst=20.0.0.4: conjunction(1, 0/4) ++tcp,nw_dst=20.0.0.7: conjunction(1, 0/4) ++tcp,nw_dst=20.0.0.8: conjunction(1, 0/4) ++tcp,nw_src=10.0.0.4: conjunction(1, 1/4) ++tcp,nw_src=10.0.0.5: conjunction(1, 1/4) ++tcp,nw_src=10.0.0.6: conjunction(1, 1/4) ++tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/4) ++tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/4) ++tcp,tp_dst=0x3f0/0xfff0: conjunction(1, 2/4) ++tcp,tp_dst=0x400/0xfe00: conjunction(1, 2/4) ++tcp,tp_dst=0x600/0xff00: conjunction(1, 2/4) ++tcp,tp_dst=0x700/0xff80: conjunction(1, 2/4) ++tcp,tp_dst=0x780/0xffc0: conjunction(1, 2/4) ++tcp,tp_dst=0x7c0/0xfff0: conjunction(1, 2/4) ++tcp,tp_dst=1000: conjunction(1, 2/4) ++tcp,tp_dst=1001: conjunction(1, 2/4) ++tcp,tp_dst=2000: conjunction(1, 2/4) ++tcp,tp_src=0x3ea/0xfffe: conjunction(1, 3/4) ++tcp,tp_src=0x3ec/0xfffc: conjunction(1, 3/4) ++tcp,tp_src=0x3f0/0xfff0: conjunction(1, 3/4) ++tcp,tp_src=0x400/0xfe00: conjunction(1, 3/4) ++tcp,tp_src=0x600/0xff00: conjunction(1, 3/4) ++tcp,tp_src=0x700/0xff80: conjunction(1, 3/4) ++tcp,tp_src=0x780/0xffc0: conjunction(1, 3/4) ++tcp,tp_src=0x7c0/0xfff0: conjunction(1, 3/4) ++tcp,tp_src=1000: conjunction(1, 3/4) ++tcp,tp_src=1001: conjunction(1, 3/4) ++tcp,tp_src=2000: conjunction(1, 3/4) + ]) + AT_CLEANUP + +@@ -13443,11 +12313,9 @@ AT_CHECK([cat 2.packets], [0], [expout]) + # priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(3,1/2) + # priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(3,1/2) + +-# conjunction is disabled in OVN until the issues related to it are +-# fixed. +-OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ ++OVS_WAIT_UNTIL([test 12 = `as hv1 ovs-ofctl dump-flows br-int | \ + grep conjunction | wc -l`]) +-OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \ ++OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \ + grep conj_id | wc -l`]) + + as hv1 ovs-ofctl dump-flows br-int +-- +2.14.5 + diff --git a/SOURCES/0001-controller-grant-cap_net_admin-to-ovn-controller.patch b/SOURCES/0001-controller-grant-cap_net_admin-to-ovn-controller.patch new file mode 100644 index 0000000..efd6582 --- /dev/null +++ b/SOURCES/0001-controller-grant-cap_net_admin-to-ovn-controller.patch @@ -0,0 +1,28 @@ +From b179dc03af829443c2e11b4ee1ade456baa00af8 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Lorenzo Bianconi +Date: Thu, 27 Feb 2020 11:34:17 +0100 +Subject: [PATCH] controller: grant cap_net_admin to ovn-controller + +ovn-controller is currently running as non-root so it is not allowed to +configure system networking breaking ovn QoS support. Fix the issue +granting CAP_NET_ADMIN capability to ovn-controller process + +Tested-by: Ying Xu +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Mark Michelson +--- + ovn/controller/ovn-controller.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -1756,7 +1756,7 @@ main(int argc, char *argv[]) + char *ovs_remote = parse_options(argc, argv); + fatal_ignore_sigpipe(); + +- daemonize_start(false); ++ daemonize_start(true); + + retval = unixctl_server_create(NULL, &unixctl); + if (retval) { diff --git a/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch b/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch new file mode 100644 index 0000000..d082027 --- /dev/null +++ b/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch @@ -0,0 +1,105 @@ +From 7f60417ae6c7438565a21d5aee0bb8ae0b3a9b68 Mon Sep 17 00:00:00 2001 +Message-Id: <7f60417ae6c7438565a21d5aee0bb8ae0b3a9b68.1585835882.git.me@lorenzobianconi.net> +From: Lorenzo Bianconi +Date: Tue, 24 Mar 2020 20:33:27 +0100 +Subject: [PATCH] controller: use LLA IPv6 address as NS source address + +Use router LLA IPv6 address as IPv6 source address for Neighbor +Solicitation packets + +Fixes: c0bf32d72 ("Manage ARP process locally in a DVR scenario") +Change-Id: Iafa26f4b3c20e181bd5b54a357d468ce61b589b6 +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +Signed-off-by: Lorenzo Bianconi +--- + ovn/controller/pinctrl.c | 4 +++- + tests/ovn.at | 15 +++++++++------ + 2 files changed, 12 insertions(+), 7 deletions(-) + +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -4363,9 +4363,11 @@ pinctrl_handle_nd_ns(struct rconn *swcon + + uint64_t packet_stub[128 / 8]; + struct dp_packet packet; ++ struct in6_addr ipv6_src; + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); + +- compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src, ++ in6_generate_lla(ip_flow->dl_src, &ipv6_src); ++ compose_nd_ns(&packet, ip_flow->dl_src, &ipv6_src, + &ip_flow->ipv6_dst); + + /* Reload previous packet metadata and set actions from userdata. */ +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -11225,13 +11225,13 @@ options:rxq_pcap=${pcap_file}-rx.pcap + # This function sends ipv6 packet + test_ipv6() { + local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 +- local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8 ++ local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8 nd_src_ip=$9 + + local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip} + packet=${packet}8000000000000000 + + src_mac=000002010204 +- expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${src_ip} ++ expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${nd_src_ip} + expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000 + expected_packet=${expected_packet}${nd_target}0101${src_mac} + +@@ -11243,6 +11243,7 @@ test_ipv6() { + src_mac=506400000002 + dst_mac=00000000af01 + src_ip=aef0000000000000526400fffe000002 ++nd_src_ip=fe80000000000000020002fffe010204 + dst_ip=20010db800010000020002fffe010205 + dst_mcast_mac=3333ff010205 + mcast_node_ip=ff0200000000000000000001ff010205 +@@ -11250,7 +11251,7 @@ nd_target=20010db800010000020002fffe0102 + # Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet + # should be received by the ports attached to br-phys. + test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \ +-$mcast_node_ip $nd_target ++$mcast_node_ip $nd_target $nd_src_ip + + OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)]) + OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)]) +@@ -11283,7 +11284,7 @@ dst_mcast_mac=3333ff011305 + mcast_node_ip=ff0200000000000000000001ff011305 + nd_target=20010db800010000020002fffe011305 + test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \ +-$mcast_node_ip $nd_target ++$mcast_node_ip $nd_target $nd_src_ip + + OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)]) + OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)]) +@@ -14005,7 +14006,7 @@ send_na() { + get_nd() { + local eth_src=$1 src_ip=$2 dst_ip=$3 ta=$4 + local ip6_hdr=6000000000203aff${src_ip}${dst_ip} +- request=3333ff000010${eth_src}86dd${ip6_hdr}8700357600000000${ta}0101${eth_src} ++ request=3333ff000010${eth_src}86dd${ip6_hdr}870051f400000000${ta}0101${eth_src} + + echo $request + } +@@ -14068,6 +14069,8 @@ router_mac1=000002010203 + router_ip=$(ip_to_hex 172 16 1 1) + router_ip6=20020000000000000000000000000001 + ++nd_src_ip6=fe80000000000000020002fffe010203 ++ + dst_mac=001122334455 + dst_ip=$(ip_to_hex 172 16 1 10) + dst_ip6=20020000000000000000000000000010 +@@ -14085,7 +14088,7 @@ nd_ip=ff0200000000000000000001ff000010 + ip6_hdr=6000000000083afe${src_ip6}${dst_ip6} + + send_icmp6_packet 1 1 $src_mac $router_mac0 $src_ip6 $dst_ip6 +-echo $(get_nd $router_mac1 $src_ip6 $nd_ip $dst_ip6) >> expected ++echo $(get_nd $router_mac1 $nd_src_ip6 $nd_ip $dst_ip6) >> expected + echo "${dst_mac}${router_mac1}86dd${ip6_hdr}8000dcb662f00001" >> expected + send_na 2 1 $dst_mac $router_mac1 $dst_ip6 $router_ip6 + diff --git a/SOURCES/0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch b/SOURCES/0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch new file mode 100644 index 0000000..4567c6b --- /dev/null +++ b/SOURCES/0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch @@ -0,0 +1,49 @@ +From 4820216aa4bcadec2b77340ca7016a9d96a44b9a Mon Sep 17 00:00:00 2001 +From: Han Zhou +Date: Mon, 21 Oct 2019 18:10:13 -0700 +Subject: [PATCH ovn] lflow.c: Fix memory leak of + lflow_ref_list_node->ref_name. + +The ref_name is copied in lflow_resource_add(), but forgot to free in +lflow_resource_destroy_lflow(). It can be fixed by freeing it in +lflow_resource_destroy_lflow(). However, this field is never really +used, so just delete it from lflow_ref_list_node, together with the +"type" field. + +Fixes: d2aa2c7cafead ("ovn-controller: Maintain resource references for logical flows.") +Acked-by: Numan Siddique +Signed-off-by: Han Zhou +--- + ovn/controller/lflow.c | 2 -- + ovn/controller/lflow.h | 2 -- + 2 files changed, 4 deletions(-) + +diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c +index e3ed20c..24e3a52 100644 +--- a/ovn/controller/lflow.c ++++ b/ovn/controller/lflow.c +@@ -230,8 +230,6 @@ lflow_resource_add(struct lflow_resource_ref *lfrr, enum ref_type type, + } + + struct lflow_ref_list_node *lrln = xzalloc(sizeof *lrln); +- lrln->type = type; +- lrln->ref_name = xstrdup(ref_name); + lrln->lflow_uuid = *lflow_uuid; + ovs_list_push_back(&rlfn->ref_lflow_head, &lrln->ref_list); + ovs_list_push_back(&lfrn->lflow_ref_head, &lrln->lflow_list); +diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h +index 0572668..abdc55e 100644 +--- a/ovn/controller/lflow.h ++++ b/ovn/controller/lflow.h +@@ -80,8 +80,6 @@ enum ref_type { + struct lflow_ref_list_node { + struct ovs_list lflow_list; /* list for same lflow */ + struct ovs_list ref_list; /* list for same ref */ +- enum ref_type type; +- char *ref_name; + struct uuid lflow_uuid; + }; + +-- +1.8.3.1 + diff --git a/SOURCES/0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch b/SOURCES/0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch new file mode 100644 index 0000000..0e3b408 --- /dev/null +++ b/SOURCES/0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch @@ -0,0 +1,69 @@ +From d1b6e6838f0b69088122c125dd435899dbb1ec28 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Fri, 6 Dec 2019 15:11:44 -0500 +Subject: [PATCH ovn 1/4] nbctl: Log the source of duplicate IP addresses + +When doing an lsp-add, ovn-nbctl will ensure that there is not another +port on the same logical switch with the same IP address. I'm seeing +this error occur with ovn-kubernetes, and I would find it helpful to +see which port it thinks had the duplicate address, because it's not +obvious what's happening. + +Signed-off-by: Russell Bryant +Acked-by: Han Zhou + +(cherry picked from upstream commit e34cd53a66dac5a371744459ca51b8df9eeea3c4) +--- + ovn/utilities/ovn-nbctl.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c +index 1a1d83e..3d2df13 100644 +--- a/ovn/utilities/ovn-nbctl.c ++++ b/ovn/utilities/ovn-nbctl.c +@@ -1497,13 +1497,16 @@ nbctl_lsp_get_tag(struct ctl_context *ctx) + + static char * + lsp_contains_duplicate_ip(struct lport_addresses *laddrs1, +- struct lport_addresses *laddrs2) ++ struct lport_addresses *laddrs2, ++ const struct nbrec_logical_switch_port *lsp_test) + { + for (size_t i = 0; i < laddrs1->n_ipv4_addrs; i++) { + for (size_t j = 0; j < laddrs2->n_ipv4_addrs; j++) { + if (laddrs1->ipv4_addrs[i].addr == laddrs2->ipv4_addrs[j].addr) { +- return xasprintf("duplicate IPv4 address %s", +- laddrs1->ipv4_addrs[i].addr_s); ++ return xasprintf("duplicate IPv4 address '%s' found on " ++ "logical switch port '%s'", ++ laddrs1->ipv4_addrs[i].addr_s, ++ lsp_test->name); + } + } + } +@@ -1512,8 +1515,10 @@ lsp_contains_duplicate_ip(struct lport_addresses *laddrs1, + for (size_t j = 0; j < laddrs2->n_ipv6_addrs; j++) { + if (IN6_ARE_ADDR_EQUAL(&laddrs1->ipv6_addrs[i].addr, + &laddrs2->ipv6_addrs[j].addr)) { +- return xasprintf("duplicate IPv6 address %s", +- laddrs1->ipv6_addrs[i].addr_s); ++ return xasprintf("duplicate IPv6 address '%s' found on " ++ "logical switch port '%s'", ++ laddrs1->ipv6_addrs[i].addr_s, ++ lsp_test->name); + } + } + } +@@ -1544,7 +1549,8 @@ lsp_contains_duplicates(const struct nbrec_logical_switch *ls, + addr = lsp_test->dynamic_addresses; + } + if (extract_lsp_addresses(addr, &laddrs_test)) { +- sub_error = lsp_contains_duplicate_ip(&laddrs, &laddrs_test); ++ sub_error = lsp_contains_duplicate_ip(&laddrs, &laddrs_test, ++ lsp_test); + destroy_lport_addresses(&laddrs_test); + if (sub_error) { + goto err_out; +-- +1.8.3.1 + diff --git a/SOURCES/0001-northd-Allow-64-after-ipv6_prefix.patch b/SOURCES/0001-northd-Allow-64-after-ipv6_prefix.patch new file mode 100644 index 0000000..4fd590b --- /dev/null +++ b/SOURCES/0001-northd-Allow-64-after-ipv6_prefix.patch @@ -0,0 +1,89 @@ +From fbcef69a367112ff8012d0d0caec4320331f3641 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Wed, 19 Feb 2020 10:48:18 -0500 +Subject: [PATCH] northd: Allow /64 after ipv6_prefix + +We recently hit a bug in ovn-kubernetes, where I accidentally added +/64 at the end of ipv6_prefix, to match the format we used for the +subnet option for IPv4. This was not allowed. + +This patch update ovn-northd to take the ipv6_prefix either with or +without a trailing "/64". It still enforces a /64 CIDR prefix length. + +A test case was updated to ensure that a prefix with "/64" is now +accepted. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique + +(cherry-picked from upstream commit d425050eb95c2b0ab5312d79fd0d6bd4a7d6d6e8) + +Change-Id: I29b65254ad20a91945fad77083afc149cadc4f60 +--- + ovn/northd/ovn-northd.c | 33 +++++++++++++++++++++++++++++++-- + tests/ovn.at | 4 +++- + 2 files changed, 34 insertions(+), 3 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 1d21f8c34..c225dd2e5 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -713,8 +713,37 @@ init_ipam_info_for_datapath(struct ovn_datapath *od) + const char *ipv6_prefix = smap_get(&od->nbs->other_config, "ipv6_prefix"); + + if (ipv6_prefix) { +- od->ipam_info.ipv6_prefix_set = ipv6_parse( +- ipv6_prefix, &od->ipam_info.ipv6_prefix); ++ if (strstr(ipv6_prefix, "/")) { ++ /* If a prefix length was specified, it must be 64. */ ++ struct in6_addr mask; ++ char *error ++ = ipv6_parse_masked(ipv6_prefix, ++ &od->ipam_info.ipv6_prefix, &mask); ++ if (error) { ++ static struct vlog_rate_limit rl ++ = VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s: %s", ++ ipv6_prefix, error); ++ free(error); ++ } else { ++ if (ipv6_count_cidr_bits(&mask) == 64) { ++ od->ipam_info.ipv6_prefix_set = true; ++ } else { ++ static struct vlog_rate_limit rl ++ = VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s: must be /64", ++ ipv6_prefix); ++ } ++ } ++ } else { ++ od->ipam_info.ipv6_prefix_set = ipv6_parse( ++ ipv6_prefix, &od->ipam_info.ipv6_prefix); ++ if (!od->ipam_info.ipv6_prefix_set) { ++ static struct vlog_rate_limit rl ++ = VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s", ipv6_prefix); ++ } ++ } + } + + if (!subnet_str) { +diff --git a/tests/ovn.at b/tests/ovn.at +index c06337147..2e1c33237 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -12292,8 +12292,10 @@ ovn-nbctl set Logical_Switch ls1 \ + other_config:subnet=10.1.0.0/24 other_config:ipv6_prefix="2001:db8:1::" + ovn-nbctl set Logical_Switch ls2 \ + other_config:subnet=10.2.0.0/24 other_config:ipv6_prefix="2001:db8:2::" ++ ++# A prefix length may be specified, but only if it is /64. + ovn-nbctl set Logical_Switch ls3 \ +- other_config:subnet=10.3.0.0/24 other_config:ipv6_prefix="2001:db8:3::" ++ other_config:subnet=10.3.0.0/24 other_config:ipv6_prefix="2001:db8:3::/64" + + ovn-nbctl lsp-add ls1 lp1 + ovn-nbctl lsp-add ls2 lp2 +-- +2.26.2 + diff --git a/SOURCES/0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch b/SOURCES/0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch new file mode 100644 index 0000000..1913c04 --- /dev/null +++ b/SOURCES/0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch @@ -0,0 +1,68 @@ +From 3dde59a7b7f0bb634fa5943b185b1cf1d4ed6907 Mon Sep 17 00:00:00 2001 +Message-Id: <3dde59a7b7f0bb634fa5943b185b1cf1d4ed6907.1594372314.git.lorenzo.bianconi@redhat.com> +From: Mark Michelson +Date: Tue, 2 Jun 2020 09:06:32 -0400 +Subject: [PATCH] northd: Fix IPAM IPv4 start address calculation. + +IPAM assumes the other_config:subnet of the logical switch is a network +address, which can result in unusual address assignments. + +As an example, consider the following configuration: + +ovn-nbctl set logical_switch ls other_config:subnet=172.16.1.254/29 + +172.16.1.254 is not a network address of a /29 network, but ovn-northd +doesn't care. ovn-northd starts IP address allocation at 172.16.1.254, with 7 +assignable addresses in the subnet. The first address (172.16.1.255) is +reserved for router port use. The first IP addresses to a logical switch +port is 172.16.2.0, then 172.16.2.1, and so on. + +This patch changes the behavior by using the provided netmask to change +the starting IP address to the network address of the subnet. In the +previous example, the provided 172.16.1.254/29 would be converted +internally to 172.16.1.248/29 . Therefore, the first IP address +allocated to a switch port would be 172.16.1.250. Further allocations would +continue up until 172.16.1.254. + +Reported at: https://bugzilla.redhat.com/show_bug.cgi?id=1823287 + +Acked-by: Numan Siddique +Signed-off-by: Mark Michelson +Signed-off-by: Numan Siddique +Signed-off-by: Lorenzo Bianconi +--- + ovn/northd/ovn-northd.c | 2 +- + tests/ovn.at | 11 +++++++++++ + 2 files changed, 12 insertions(+), 1 deletion(-) + +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -764,7 +764,7 @@ init_ipam_info_for_datapath(struct ovn_d + return; + } + +- od->ipam_info.start_ipv4 = ntohl(subnet) + 1; ++ od->ipam_info.start_ipv4 = ntohl(subnet & mask) + 1; + od->ipam_info.total_ipv4s = ~ntohl(mask); + od->ipam_info.allocated_ipv4s = + bitmap_allocate(od->ipam_info.total_ipv4s); +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -6636,6 +6636,17 @@ AT_CHECK([ovn-nbctl get Logical-Switch-P + ["00:11:22:a8:6e:0b 192.168.110.10 ae01::2" + ]) + ++# Configure subnet using address from middle of the subnet and ensure ++# address is allocated from the beginning. ++ ++ovn-nbctl ls-add sw11 ++ovn-nbctl --wait=sb set Logical-Switch sw11 other_config:subnet=172.16.1.254/29 ++ovn-nbctl --wait=sb lsp-add sw11 p103 -- lsp-set-addresses p103 "22:33:44:55:66:77 dynamic" ++ ++AT_CHECK([ovn-nbctl get Logical-Switch-Port p103 dynamic_addresses], [0], ++ ["22:33:44:55:66:77 172.16.1.250" ++]) ++ + as ovn-sb + OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + diff --git a/SOURCES/0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch b/SOURCES/0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch new file mode 100644 index 0000000..0cbafcc --- /dev/null +++ b/SOURCES/0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch @@ -0,0 +1,31 @@ +From 56e3168c643953c789ca55b352623b08f8c97a7d Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Fri, 25 Oct 2019 16:06:14 -0400 +Subject: [PATCH 1/5] northd: Fix table ID for IPv6 router ingress. + +I noticed that this table number was outdated. This is now table 3. +There are a few other sections of code for this table that were all +correctly referencing table 3. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique +--- + ovn/northd/ovn-northd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 0e463e0a8..7cfeb60be 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -7046,7 +7046,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + free(snat_ips); + } + +- /* Logical router ingress table 1: IP Input for IPv6. */ ++ /* Logical router ingress table 3: IP Input for IPv6. */ + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbrp) { + continue; +-- +2.23.0 + diff --git a/SOURCES/0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch b/SOURCES/0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch new file mode 100644 index 0000000..e5f8b0f --- /dev/null +++ b/SOURCES/0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch @@ -0,0 +1,42 @@ +From 84704b5236952e3a7c191b4b23f5af574f5840d9 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Mon, 18 Nov 2019 20:27:12 -0500 +Subject: [PATCH 1/2] northd: Match IPv4 or IPv6 for MAC resolution + +While debugging some problems in a cluster using ovn-kubernetes, I +noticed that we're creating two conflicting logical flows. These two +flows only matched on the destination MAC address. It was not +deterministic whether you'd hit the IPv4 (ARP) or IPv6 (NS) version. + +This change adds an ip4 or ip6 match to each flow as appropriate. + +Signed-off-by: Russell Bryant +--- + ovn/northd/ovn-northd.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index cb12be3ca..bcadcca3d 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -8661,7 +8661,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + } + + ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, +- "eth.dst == 00:00:00:00:00:00", ++ "eth.dst == 00:00:00:00:00:00 && ip4", + "arp { " + "eth.dst = ff:ff:ff:ff:ff:ff; " + "arp.spa = reg1; " +@@ -8670,7 +8670,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + "output; " + "};"); + ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, +- "eth.dst == 00:00:00:00:00:00", ++ "eth.dst == 00:00:00:00:00:00 && ip6", + "nd_ns { " + "nd.target = xxreg0; " + "output; " +-- +2.23.0 + diff --git a/SOURCES/0001-northd-introduce-build_empty_lb_event_flow-routine.patch b/SOURCES/0001-northd-introduce-build_empty_lb_event_flow-routine.patch new file mode 100644 index 0000000..4d09829 --- /dev/null +++ b/SOURCES/0001-northd-introduce-build_empty_lb_event_flow-routine.patch @@ -0,0 +1,92 @@ +From 3624362cb395b6bc90f6fd69e12988979db95b7e Mon Sep 17 00:00:00 2001 +Message-Id: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2019 16:22:51 +0200 +Subject: [PATCH ovn 1/3] northd: introduce build_empty_lb_event_flow routine + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Mark Michelson +Acked-by: Mark Michelson +--- + ovn/northd/ovn-northd.c | 63 ++++++++++++++++++++++++++------------------- + 1 file changed, 37 insertions(+), 26 deletions(-) + +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -3882,6 +3882,41 @@ ls_has_dns_records(const struct nbrec_lo + } + + static void ++build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows, ++ struct smap_node *node, char *ip_address, ++ struct nbrec_load_balancer *lb, uint16_t port, ++ int addr_family, int pl) ++{ ++ if (!controller_event_en || node->value[0]) { ++ return; ++ } ++ ++ struct ds match = DS_EMPTY_INITIALIZER; ++ char *action; ++ ++ if (addr_family == AF_INET) { ++ ds_put_format(&match, "ip4.dst == %s && %s", ++ ip_address, lb->protocol); ++ } else { ++ ds_put_format(&match, "ip6.dst == %s && %s", ++ ip_address, lb->protocol); ++ } ++ if (port) { ++ ds_put_format(&match, " && %s.dst == %u", lb->protocol, ++ port); ++ } ++ action = xasprintf("trigger_event(event = \"%s\", " ++ "vip = \"%s\", protocol = \"%s\", " ++ "load_balancer = \"" UUID_FMT "\");", ++ event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS), ++ node->key, lb->protocol, ++ UUID_ARGS(&lb->header_.uuid)); ++ ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action); ++ ds_destroy(&match); ++ free(action); ++} ++ ++static void + build_pre_lb(struct ovn_datapath *od, struct hmap *lflows) + { + /* Do not send ND packets to conntrack */ +@@ -3918,32 +3953,8 @@ build_pre_lb(struct ovn_datapath *od, st + sset_add(&all_ips, ip_address); + } + +- if (controller_event_en && !node->value[0]) { +- struct ds match = DS_EMPTY_INITIALIZER; +- char *action; +- +- if (addr_family == AF_INET) { +- ds_put_format(&match, "ip4.dst == %s && %s", +- ip_address, lb->protocol); +- } else { +- ds_put_format(&match, "ip6.dst == %s && %s", +- ip_address, lb->protocol); +- } +- if (port) { +- ds_put_format(&match, " && %s.dst == %u", lb->protocol, +- port); +- } +- action = xasprintf("trigger_event(event = \"%s\", " +- "vip = \"%s\", protocol = \"%s\", " +- "load_balancer = \"" UUID_FMT "\");", +- event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS), +- node->key, lb->protocol, +- UUID_ARGS(&lb->header_.uuid)); +- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 120, +- ds_cstr(&match), action); +- ds_destroy(&match); +- free(action); +- } ++ build_empty_lb_event_flow(od, lflows, node, ip_address, lb, ++ port, addr_family, S_SWITCH_IN_PRE_LB); + + free(ip_address); + diff --git a/SOURCES/0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch b/SOURCES/0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch new file mode 100644 index 0000000..4a588c9 --- /dev/null +++ b/SOURCES/0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch @@ -0,0 +1,122 @@ +From db78e4fbe040a2e2a1b849dfdea4c494fe98af26 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Sat, 14 Sep 2019 01:56:43 +0530 +Subject: [PATCH] ovn: Exclude inport and outport symbol tables from + conjunction + +If there are multiple ACLs associated with a port group and they +match on a range of some field, then ovn-controller doesn't install +the flows properly and this results in broken ACL functionality. + +For example, if there is a port group - pg1 with logical ports - [p1, p2] +and if there are below ACLs (only match condition is shown) + +1 - outport == @pg1 && ip4 && tcp.dst >= 500 && tcp.dst <= 501 +2 - outport == @pg1 && ip4 && tcp.dst >= 600 && tcp.dst <= 601 + +The first ACL will result in the below OF flows + +1. conj_id=1,tcp +2. tcp,reg15=0x11: conjunction(1, 1/2) +3. tcp,reg15=0x12: conjunction(1, 1/2) +5. tcp,tp_dst=500: conjunction(1, 2/2) +6. tcp,tp_dst=501: conjunction(1, 2/2) + +The second ACL will result in the below OF flows +7. conj_id=2,tcp +8. tcp,reg15=0x11: conjunction(2, 1/2) +9. tcp,reg15=0x12: conjunction(2, 1/2) +11. tcp,tp_dst=600: conjunction(2, 2/2) +12. tcp,tp_dst=601: conjunction(2, 3/2) + +The OF flows (2) and (8) have the exact match but with different action. +This results in only one of the flows getting installed. The same goes +for the flows (3) and (9). And this completely breaks the ACL functionality +for such scenarios. + +In order to fix this issue, this patch excludes the 'inport' and 'outport' symbols +from conjunction. With this patch we will have the below flows. + +tcp,reg15=0x11,tp_dst=500 +tcp,reg15=0x11,tp_dst=501 +tcp,reg15=0x12,tp_dst=500 +tcp,reg15=0x12,tp_dst=501 +tcp,reg15=0x13,tp_dst=500 +tcp,reg15=0x13,tp_dst=501 +tcp,reg15=0x11,tp_dst=600 +tcp,reg15=0x11,tp_dst=601 +tcp,reg15=0x12,tp_dst=600 +tcp,reg15=0x12,tp_dst=601 +tcp,reg15=0x13,tp_dst=600 +tcp,reg15=0x13,tp_dst=601 + +Acked-by: Mark Michelson +Acked-by: Daniel Alvarez +Signed-off-by: Numan Siddique + +(cherry-picked from ovn commit 298701dbc99645700be41680a43d049cb061847a) +--- + ovn/lib/expr.c | 2 +- + tests/ovn.at | 26 ++++++++++++++++++++++++++ + 2 files changed, 27 insertions(+), 1 deletion(-) + +diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c +index e4c650f7c..c0871e1e8 100644 +--- a/ovn/lib/expr.c ++++ b/ovn/lib/expr.c +@@ -1499,7 +1499,7 @@ expr_symtab_add_string(struct shash *symtab, const char *name, + const struct mf_field *field = mf_from_id(id); + struct expr_symbol *symbol; + +- symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false, ++ symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, true, + field->writable); + symbol->field = field; + return symbol; +diff --git a/tests/ovn.at b/tests/ovn.at +index 2361524ff..54aa19bb2 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -573,6 +573,24 @@ ip,reg14=0x6 + ipv6,reg14=0x5 + ipv6,reg14=0x6 + ]) ++AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.dst == {500, 501}'], [0], [dnl ++tcp,reg14=0x5,tp_dst=500 ++tcp,reg14=0x5,tp_dst=501 ++tcp,reg14=0x6,tp_dst=500 ++tcp,reg14=0x6,tp_dst=501 ++]) ++AT_CHECK([expr_to_flow 'outport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.src == {400, 401} && tcp.dst == {500, 501}'], [0], [dnl ++conj_id=1,tcp,reg15=0x5 ++conj_id=2,tcp,reg15=0x6 ++tcp,reg15=0x5,tp_dst=500: conjunction(1, 0/2) ++tcp,reg15=0x5,tp_dst=501: conjunction(1, 0/2) ++tcp,reg15=0x5,tp_src=400: conjunction(1, 1/2) ++tcp,reg15=0x5,tp_src=401: conjunction(1, 1/2) ++tcp,reg15=0x6,tp_dst=500: conjunction(2, 0/2) ++tcp,reg15=0x6,tp_dst=501: conjunction(2, 0/2) ++tcp,reg15=0x6,tp_src=400: conjunction(2, 1/2) ++tcp,reg15=0x6,tp_src=401: conjunction(2, 1/2) ++]) + AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl + (no flows) + ]) +@@ -677,6 +695,14 @@ reg15=0x11 + reg15=0x12 + reg15=0x13 + ]) ++AT_CHECK([expr_to_flow 'outport == @pg1 && ip4.src == {10.0.0.4, 10.0.0.5}'], [0], [dnl ++ip,reg15=0x11,nw_src=10.0.0.4 ++ip,reg15=0x11,nw_src=10.0.0.5 ++ip,reg15=0x12,nw_src=10.0.0.4 ++ip,reg15=0x12,nw_src=10.0.0.5 ++ip,reg15=0x13,nw_src=10.0.0.4 ++ip,reg15=0x13,nw_src=10.0.0.5 ++]) + AT_CHECK([expr_to_flow 'outport == {@pg_empty}'], [0], [dnl + (no flows) + ]) +-- +2.21.0 + diff --git a/SOURCES/0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch b/SOURCES/0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch new file mode 100644 index 0000000..46dba6f --- /dev/null +++ b/SOURCES/0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch @@ -0,0 +1,92 @@ +From 996e1077aa5ddf4731aba5805e99c59168eab3f9 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Mon, 2 Dec 2019 19:02:52 +0100 +Subject: [PATCH ovn] ovn-controller: Add command to trigger an I-P full + recompute. + +Incremental processing tries to minimize the number of times +ovn-controller has to fully reprocess the contents of the southbound +database. However, if a bug in the I-P code causes ovn-controller to +end up in an inconsistent state, we have no easy way to force a full +recalculation of the openflow entries. + +This commit adds a new command to ovn-controller, "recompute", which +allows users to force a full recompute of the database. It can be +triggered by the user in the following way: + +ovn-appctl -t ovn-controller recompute + +Reviewed-by: Daniel Alvarez +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit c66e0cee15ef6716ff2c092dce3168c92975bbf1) +--- + ovn/controller/ovn-controller.8.xml | 14 ++++++++++++++ + ovn/controller/ovn-controller.c | 14 ++++++++++++++ + 2 files changed, 28 insertions(+) + +diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml +index 780625f..a226802 100644 +--- a/ovn/controller/ovn-controller.8.xml ++++ b/ovn/controller/ovn-controller.8.xml +@@ -450,6 +450,20 @@ +
    + Show OVN SBDB connection status for the chassis. +
    ++ ++
    recompute
    ++
    ++

    ++ Trigger a full compute iteration in ovn-controller based ++ on the contents of the Southbound database and local OVS database. ++

    ++

    ++ This command is intended to use only in the event of a bug in the ++ incremental processing engine in ovn-controller to avoid ++ inconsistent states. It should therefore be used with care as full ++ recomputes are cpu intensive. ++

    ++
    + +

    + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index d7eb9d0..e864101 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -73,6 +73,7 @@ static unixctl_cb_func meter_table_list; + static unixctl_cb_func group_table_list; + static unixctl_cb_func inject_pkt; + static unixctl_cb_func ovn_controller_conn_show; ++static unixctl_cb_func engine_recompute_cmd; + + #define DEFAULT_BRIDGE_NAME "br-int" + #define DEFAULT_PROBE_INTERVAL_MSEC 5000 +@@ -1895,6 +1896,9 @@ main(int argc, char *argv[]) + unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt, + &pending_pkt); + ++ unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd, ++ NULL); ++ + uint64_t engine_run_id = 0; + uint64_t old_engine_run_id = 0; + bool engine_run_done = true; +@@ -2391,3 +2395,13 @@ ovn_controller_conn_show(struct unixctl_conn *conn, int argc OVS_UNUSED, + } + unixctl_command_reply(conn, result); + } ++ ++static void ++engine_recompute_cmd(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, ++ const char *argv[] OVS_UNUSED, void *arg OVS_UNUSED) ++{ ++ VLOG_INFO("User triggered force recompute."); ++ engine_set_force_recompute(true); ++ poll_immediate_wake(); ++ unixctl_command_reply(conn, NULL); ++} +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch b/SOURCES/0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch new file mode 100644 index 0000000..9316f0f --- /dev/null +++ b/SOURCES/0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch @@ -0,0 +1,233 @@ +From 07e82c86d4f6540b701ce43b0c39859a957ab820 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Mon, 2 Dec 2019 14:20:32 +0100 +Subject: [PATCH ovn] ovn-controller: Add missing port group lflow + references. + +The commit that adds incremental processing for port-group changes +doesn't store logical flow references for port groups. If a port group +is updated (e.g., a port is added) no logical flow recalculation will be +performed. + +To fix this, when parsing the flow expression also store the referenced +port groups and bind them to the logical flows that depend on them. If +the port group is updated then the logical flows referring them will +also be reinstalled. + +Reported-by: Daniel Alvarez +Reported-at: https://bugzilla.redhat.com/1778164 +CC: Han Zhou +Fixes: 978f5e90af0a ("ovn-controller: Incremental processing for port-group changes.") +Tested-By: Daniel Alvarez +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit bbcac48d443e98cbe47d3941f7e192c9c3443cb5) +--- + include/ovn/expr.h | 4 +++- + ovn/controller/lflow.c | 9 ++++++++- + ovn/lib/actions.c | 4 ++-- + ovn/lib/expr.c | 24 +++++++++++++++++------- + ovn/utilities/ovn-trace.c | 2 +- + tests/test-ovn.c | 8 ++++---- + 6 files changed, 35 insertions(+), 16 deletions(-) + +diff --git a/include/ovn/expr.h b/include/ovn/expr.h +index 22f633e..21bf51c 100644 +--- a/include/ovn/expr.h ++++ b/include/ovn/expr.h +@@ -390,11 +390,13 @@ void expr_print(const struct expr *); + struct expr *expr_parse(struct lexer *, const struct shash *symtab, + const struct shash *addr_sets, + const struct shash *port_groups, +- struct sset *addr_sets_ref); ++ struct sset *addr_sets_ref, ++ struct sset *port_groups_ref); + struct expr *expr_parse_string(const char *, const struct shash *symtab, + const struct shash *addr_sets, + const struct shash *port_groups, + struct sset *addr_sets_ref, ++ struct sset *port_groups_ref, + char **errorp); + + struct expr *expr_clone(struct expr *); +diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c +index 9dda76f..9dbce91 100644 +--- a/ovn/controller/lflow.c ++++ b/ovn/controller/lflow.c +@@ -616,14 +616,21 @@ consider_logical_flow( + struct expr *expr; + + struct sset addr_sets_ref = SSET_INITIALIZER(&addr_sets_ref); ++ struct sset port_groups_ref = SSET_INITIALIZER(&port_groups_ref); + expr = expr_parse_string(lflow->match, &symtab, addr_sets, port_groups, +- &addr_sets_ref, &error); ++ &addr_sets_ref, &port_groups_ref, &error); + const char *addr_set_name; + SSET_FOR_EACH (addr_set_name, &addr_sets_ref) { + lflow_resource_add(lfrr, REF_TYPE_ADDRSET, addr_set_name, + &lflow->header_.uuid); + } ++ const char *port_group_name; ++ SSET_FOR_EACH (port_group_name, &port_groups_ref) { ++ lflow_resource_add(lfrr, REF_TYPE_PORTGROUP, port_group_name, ++ &lflow->header_.uuid); ++ } + sset_destroy(&addr_sets_ref); ++ sset_destroy(&port_groups_ref); + + if (!error) { + if (prereqs) { +diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c +index 7857f65..82a9334 100644 +--- a/ovn/lib/actions.c ++++ b/ovn/lib/actions.c +@@ -240,8 +240,8 @@ add_prerequisite(struct action_context *ctx, const char *prerequisite) + struct expr *expr; + char *error; + +- expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL, NULL, +- &error); ++ expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL, ++ NULL, NULL, &error); + ovs_assert(!error); + ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr); + } +diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c +index ca72075..4af1cb2 100644 +--- a/ovn/lib/expr.c ++++ b/ovn/lib/expr.c +@@ -480,7 +480,8 @@ struct expr_context { + const struct shash *symtab; /* Symbol table. */ + const struct shash *addr_sets; /* Address set table. */ + const struct shash *port_groups; /* Port group table. */ +- struct sset *addr_sets_ref; /* The set of address set referenced. */ ++ struct sset *addr_sets_ref; /* The set of address set referenced. */ ++ struct sset *port_groups_ref; /* The set of port groups referenced. */ + bool not; /* True inside odd number of NOT operators. */ + unsigned int paren_depth; /* Depth of nested parentheses. */ + }; +@@ -782,6 +783,10 @@ static bool + parse_port_group(struct expr_context *ctx, struct expr_constant_set *cs, + size_t *allocated_values) + { ++ if (ctx->port_groups_ref) { ++ sset_add(ctx->port_groups_ref, ctx->lexer->token.s); ++ } ++ + struct expr_constant_set *port_group + = (ctx->port_groups + ? shash_find_data(ctx->port_groups, ctx->lexer->token.s) +@@ -1296,13 +1301,15 @@ struct expr * + expr_parse(struct lexer *lexer, const struct shash *symtab, + const struct shash *addr_sets, + const struct shash *port_groups, +- struct sset *addr_sets_ref) ++ struct sset *addr_sets_ref, ++ struct sset *port_groups_ref) + { + struct expr_context ctx = { .lexer = lexer, + .symtab = symtab, + .addr_sets = addr_sets, + .port_groups = port_groups, +- .addr_sets_ref = addr_sets_ref }; ++ .addr_sets_ref = addr_sets_ref, ++ .port_groups_ref = port_groups_ref }; + return lexer->error ? NULL : expr_parse__(&ctx); + } + +@@ -1317,6 +1324,7 @@ expr_parse_string(const char *s, const struct shash *symtab, + const struct shash *addr_sets, + const struct shash *port_groups, + struct sset *addr_sets_ref, ++ struct sset *port_groups_ref, + char **errorp) + { + struct lexer lexer; +@@ -1324,7 +1332,7 @@ expr_parse_string(const char *s, const struct shash *symtab, + lexer_init(&lexer, s); + lexer_get(&lexer); + struct expr *expr = expr_parse(&lexer, symtab, addr_sets, port_groups, +- addr_sets_ref); ++ addr_sets_ref, port_groups_ref); + lexer_force_end(&lexer); + *errorp = lexer_steal_error(&lexer); + if (*errorp) { +@@ -1550,7 +1558,8 @@ expr_get_level(const struct expr *expr) + static enum expr_level + expr_parse_level(const char *s, const struct shash *symtab, char **errorp) + { +- struct expr *expr = expr_parse_string(s, symtab, NULL, NULL, NULL, errorp); ++ struct expr *expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL, ++ errorp); + enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL; + expr_destroy(expr); + return level; +@@ -1721,7 +1730,7 @@ parse_and_annotate(const char *s, const struct shash *symtab, + char *error; + struct expr *expr; + +- expr = expr_parse_string(s, symtab, NULL, NULL, NULL, &error); ++ expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL, &error); + if (expr) { + expr = expr_annotate_(expr, symtab, nesting, &error); + } +@@ -3445,7 +3454,8 @@ expr_parse_microflow(const char *s, const struct shash *symtab, + lexer_init(&lexer, s); + lexer_get(&lexer); + +- struct expr *e = expr_parse(&lexer, symtab, addr_sets, port_groups, NULL); ++ struct expr *e = expr_parse(&lexer, symtab, addr_sets, port_groups, ++ NULL, NULL); + lexer_force_end(&lexer); + + if (e) { +diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c +index 7ed4a38..05d7113 100644 +--- a/ovn/utilities/ovn-trace.c ++++ b/ovn/utilities/ovn-trace.c +@@ -866,7 +866,7 @@ read_flows(void) + char *error; + struct expr *match; + match = expr_parse_string(sblf->match, &symtab, &address_sets, +- &port_groups, NULL, &error); ++ &port_groups, NULL, NULL, &error); + if (error) { + VLOG_WARN("%s: parsing expression failed (%s)", + sblf->match, error); +diff --git a/tests/test-ovn.c b/tests/test-ovn.c +index a7adc1c..b6eea4d 100644 +--- a/tests/test-ovn.c ++++ b/tests/test-ovn.c +@@ -289,7 +289,7 @@ test_parse_expr__(int steps) + char *error; + + expr = expr_parse_string(ds_cstr(&input), &symtab, &addr_sets, +- &port_groups, NULL, &error); ++ &port_groups, NULL, NULL, &error); + if (!error && steps > 0) { + expr = expr_annotate(expr, &symtab, &error); + } +@@ -413,8 +413,8 @@ test_evaluate_expr(struct ovs_cmdl_context *ctx) + while (!ds_get_test_line(&input, stdin)) { + struct expr *expr; + +- expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, NULL, NULL, +- &error); ++ expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, NULL, ++ NULL, NULL, &error); + if (!error) { + expr = expr_annotate(expr, &symtab, &error); + } +@@ -889,7 +889,7 @@ test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab, + + char *error; + modified = expr_parse_string(ds_cstr(&s), symtab, NULL, +- NULL, NULL, &error); ++ NULL, NULL, NULL, &error); + if (error) { + fprintf(stderr, "%s fails to parse (%s)\n", + ds_cstr(&s), error); +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-controller-Consider-non-virtual-ports-first-when.patch b/SOURCES/0001-ovn-controller-Consider-non-virtual-ports-first-when.patch new file mode 100644 index 0000000..59d748f --- /dev/null +++ b/SOURCES/0001-ovn-controller-Consider-non-virtual-ports-first-when.patch @@ -0,0 +1,178 @@ +From 726f0c5749f6ba3e147397c768854db515f5899a Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Fri, 22 Nov 2019 15:22:25 +0100 +Subject: [PATCH ovn] ovn-controller: Consider non-virtual ports first when + updating bindings. + +There's no guarantee SBREC_PORT_BINDING_TABLE_FOR_EACH will first +return the non-virtual ports and then virtual ports. In the case when a +virtual port is processed before its virtual_parent, +consider_local_datapath might not release it in the current +ovn-controller iteration even though the virtual_parent gets released. + +Right now this doesn't trigger any functionality issue because releasing +the virtual_parent triggers a change in the SB DB which will wake up +ovn-controller and trigger a new computation which will also update the +virtual port. + +However, this is suboptimal, and we can notice that often ovn-controller +gets the SB update notification before the "transaction successful" +notification. In such cases the incremental engine doesn't run +(ovnsb_idl_txn == NULL) and a full recompute is scheduled for the next +run. By batching the two SB updates in a single transaction we +lower the risk of this situation happening. + +CC: Numan Siddique +Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'") +Signed-off-by: Dumitru Ceara +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique + +(cherry picked from upstream commit 5309099ec38cf41f4e41f1929c408741a3146dac) +--- + ovn/controller/binding.c | 97 ++++++++++++++++++++++++++++++++++-------------- + 1 file changed, 69 insertions(+), 28 deletions(-) + +diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c +index 3adbfbe..1ae9151 100644 +--- a/ovn/controller/binding.c ++++ b/ovn/controller/binding.c +@@ -461,7 +461,12 @@ is_our_chassis(const struct sbrec_chassis *chassis_rec, + return our_chassis; + } + +-static void ++/* Updates 'binding_rec' and if the port binding is local also updates the ++ * local datapaths and ports. ++ * Updates and returns the array of local virtual ports that will require ++ * additional processing. ++ */ ++static const struct sbrec_port_binding ** + consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct ovsdb_idl_txn *ovs_idl_txn, + struct ovsdb_idl_index *sbrec_datapath_binding_by_key, +@@ -474,7 +479,9 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct hmap *local_datapaths, + struct shash *lport_to_iface, + struct sset *local_lports, +- struct sset *local_lport_ids) ++ struct sset *local_lport_ids, ++ const struct sbrec_port_binding **vpbs, ++ size_t *n_vpbs, size_t *n_allocated_vpbs) + { + const struct ovsrec_interface *iface_rec + = shash_find_data(lport_to_iface, binding_rec->logical_port); +@@ -576,22 +583,11 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn, + } + } else if (binding_rec->chassis == chassis_rec) { + if (!strcmp(binding_rec->type, "virtual")) { +- /* pinctrl module takes care of binding the ports +- * of type 'virtual'. +- * Release such ports if their virtual parents are no +- * longer claimed by this chassis. */ +- const struct sbrec_port_binding *parent +- = lport_lookup_by_name(sbrec_port_binding_by_name, +- binding_rec->virtual_parent); +- if (!parent || parent->chassis != chassis_rec) { +- VLOG_INFO("Releasing lport %s from this chassis.", +- binding_rec->logical_port); +- if (binding_rec->encap) { +- sbrec_port_binding_set_encap(binding_rec, NULL); +- } +- sbrec_port_binding_set_chassis(binding_rec, NULL); +- sbrec_port_binding_set_virtual_parent(binding_rec, NULL); ++ if (*n_vpbs == *n_allocated_vpbs) { ++ vpbs = x2nrealloc(vpbs, n_allocated_vpbs, sizeof *vpbs); + } ++ vpbs[(*n_vpbs)] = binding_rec; ++ (*n_vpbs)++; + } else { + VLOG_INFO("Releasing lport %s from this chassis.", + binding_rec->logical_port); +@@ -610,6 +606,30 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn, + vif_chassis); + } + } ++ return vpbs; ++} ++ ++static void ++consider_local_virtual_port(struct ovsdb_idl_index *sbrec_port_binding_by_name, ++ const struct sbrec_chassis *chassis_rec, ++ const struct sbrec_port_binding *binding_rec) ++{ ++ /* pinctrl module takes care of binding the ports of type 'virtual'. ++ * Release such ports if their virtual parents are no longer claimed by ++ * this chassis. ++ */ ++ const struct sbrec_port_binding *parent = ++ lport_lookup_by_name(sbrec_port_binding_by_name, ++ binding_rec->virtual_parent); ++ if (!parent || parent->chassis != chassis_rec) { ++ VLOG_INFO("Releasing lport %s from this chassis.", ++ binding_rec->logical_port); ++ if (binding_rec->encap) { ++ sbrec_port_binding_set_encap(binding_rec, NULL); ++ } ++ sbrec_port_binding_set_chassis(binding_rec, NULL); ++ sbrec_port_binding_set_virtual_parent(binding_rec, NULL); ++ } + } + + static void +@@ -707,20 +727,41 @@ binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn, + &egress_ifaces); + } + ++ /* Array to store pointers to local virtual ports. It is populated by ++ * consider_local_datapath. ++ */ ++ const struct sbrec_port_binding **vpbs = NULL; ++ size_t n_vpbs = 0; ++ size_t n_allocated_vpbs = 0; ++ + /* Run through each binding record to see if it is resident on this + * chassis and update the binding accordingly. This includes both +- * directly connected logical ports and children of those ports. */ ++ * directly connected logical ports and children of those ports. ++ * Virtual ports are just added to vpbs array and will be processed ++ * later. This is special case for virtual ports is needed in order to ++ * make sure we update the virtual_parent port bindings first. ++ */ + SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) { +- consider_local_datapath(ovnsb_idl_txn, ovs_idl_txn, +- sbrec_datapath_binding_by_key, +- sbrec_port_binding_by_datapath, +- sbrec_port_binding_by_name, +- active_tunnels, chassis_rec, binding_rec, +- sset_is_empty(&egress_ifaces) ? NULL : +- &qos_map, local_datapaths, &lport_to_iface, +- local_lports, local_lport_ids); +- +- } ++ vpbs = ++ consider_local_datapath(ovnsb_idl_txn, ovs_idl_txn, ++ sbrec_datapath_binding_by_key, ++ sbrec_port_binding_by_datapath, ++ sbrec_port_binding_by_name, ++ active_tunnels, chassis_rec, binding_rec, ++ sset_is_empty(&egress_ifaces) ? NULL : ++ &qos_map, local_datapaths, &lport_to_iface, ++ local_lports, local_lport_ids, ++ vpbs, &n_vpbs, &n_allocated_vpbs); ++ } ++ ++ /* Now also update the virtual ports in case their parent ports were ++ * updated above. ++ */ ++ for (size_t i = 0; i < n_vpbs; i++) { ++ consider_local_virtual_port(sbrec_port_binding_by_name, chassis_rec, ++ vpbs[i]); ++ } ++ free(vpbs); + + add_ovs_bridge_mappings(ovs_table, bridge_table, &bridge_mappings); + +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-controller-Don-t-monitor-connection-table-column.patch b/SOURCES/0001-ovn-controller-Don-t-monitor-connection-table-column.patch new file mode 100644 index 0000000..f1a544a --- /dev/null +++ b/SOURCES/0001-ovn-controller-Don-t-monitor-connection-table-column.patch @@ -0,0 +1,57 @@ +From 179483f38d04680464b5b0972d21ac64654157c7 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Tue, 24 Dec 2019 18:03:20 +0530 +Subject: [PATCH 1/2] ovn-controller: Don't monitor connection table columns + +ovn-controller doesn't need to know any changes to the connection +table row. This patch omits alerts for the Connection table +columns. + +In a large scale deployment like 1000 chassis, this can cause lot of +CPU cycle wastages as ovsdb-server has to send out updates to all +the ovn-controller connections. + +Acked-by: Dumitru Ceara +Signed-off-by: Numan Siddique + +(cherry-picked from upstream commit c80e014d4aaeb47f5967ed8730306fe3c16841c9) + +Change-Id: I5ee2ca975fba7e36c0a6e87213edff64e479874a +--- + ovn/controller/ovn-controller.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index c8470ce4e..ec6f15783 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -1828,7 +1828,6 @@ main(int argc, char *argv[]) + ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_sb_global_col_external_ids); + ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_external_ids); + ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_port_binding_col_external_ids); +- ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_external_ids); + ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_ssl_col_external_ids); + ovsdb_idl_omit(ovnsb_idl_loop.idl, + &sbrec_gateway_chassis_col_external_ids); +@@ -1836,6 +1835,18 @@ main(int argc, char *argv[]) + ovsdb_idl_omit(ovnsb_idl_loop.idl, + &sbrec_ha_chassis_group_col_external_ids); + ++ /* We don't want to monitor Connection table at all. So omit all the ++ * columns. */ ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_external_ids); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_inactivity_probe); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_is_connected); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_max_backoff); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_other_config); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_read_only); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_role); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_status); ++ ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_target); ++ + update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL); + + stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS); +-- +2.24.1 + diff --git a/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch b/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch new file mode 100644 index 0000000..c059281 --- /dev/null +++ b/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch @@ -0,0 +1,104 @@ +From 6c0f9dbf5d22fe7fdbe9bf2a05d2bed00881cff8 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Tue, 31 Mar 2020 13:47:04 +0200 +Subject: [PATCH] ovn-controller: Fix potential segfault with "virtual" + port bindings. + +Even though ovn-controller tries to set port_binding->chassis to NULL +every time port_binding->virtual_parent is set to NULL for bindings of +type="virtual", there's no way to enforce that an operator doesn't +manually clear the "virtual_parent" column in the Southbound database. + +In such scenario ovn-controller would crash because of trying to +dereference the NULL port_binding->virtual_parent column. + +Add an extra check and release "virtual" port bindings that have +"virtual_parent" NULL. + +Reported-at: https://bugzilla.redhat.com/1818844 +CC: Numan Siddique +Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'") +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique +(cherry picked from upstream commit 5b3e9879be2b6c9b07ed5c9e073f1c24080a49f7) + +Change-Id: Icced69c2871d43316ec06f71f78024939c863225 +--- + ovn/controller/binding.c | 26 +++++++++++++++----------- + tests/ovn.at | 18 ++++++++++++++++++ + 2 files changed, 33 insertions(+), 11 deletions(-) + +diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c +index 1ae9151..bf7b5ff 100644 +--- a/ovn/controller/binding.c ++++ b/ovn/controller/binding.c +@@ -614,22 +614,26 @@ consider_local_virtual_port(struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_chassis *chassis_rec, + const struct sbrec_port_binding *binding_rec) + { ++ if (binding_rec->virtual_parent) { ++ const struct sbrec_port_binding *parent = ++ lport_lookup_by_name(sbrec_port_binding_by_name, ++ binding_rec->virtual_parent); ++ if (parent && parent->chassis == chassis_rec) { ++ return; ++ } ++ } ++ + /* pinctrl module takes care of binding the ports of type 'virtual'. + * Release such ports if their virtual parents are no longer claimed by + * this chassis. + */ +- const struct sbrec_port_binding *parent = +- lport_lookup_by_name(sbrec_port_binding_by_name, +- binding_rec->virtual_parent); +- if (!parent || parent->chassis != chassis_rec) { +- VLOG_INFO("Releasing lport %s from this chassis.", +- binding_rec->logical_port); +- if (binding_rec->encap) { +- sbrec_port_binding_set_encap(binding_rec, NULL); +- } +- sbrec_port_binding_set_chassis(binding_rec, NULL); +- sbrec_port_binding_set_virtual_parent(binding_rec, NULL); ++ VLOG_INFO("Releasing lport %s from this chassis.", ++ binding_rec->logical_port); ++ if (binding_rec->encap) { ++ sbrec_port_binding_set_encap(binding_rec, NULL); + } ++ sbrec_port_binding_set_chassis(binding_rec, NULL); ++ sbrec_port_binding_set_virtual_parent(binding_rec, NULL); + } + + static void +diff --git a/tests/ovn.at b/tests/ovn.at +index 0cd0985..94e5ecd 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -14755,6 +14755,24 @@ AT_CHECK([cat lflows.txt], [0], [dnl + table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) + ]) + ++# Forcibly clear virtual_parent. ovn-controller should release the binding ++# gracefully. ++pb_uuid=$(ovn-sbctl --bare --columns _uuid find port_binding logical_port=sw0-vir) ++ovn-sbctl clear port_binding $pb_uuid virtual_parent ++ ++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \ ++logical_port=sw0-vir) = x]) ++ ++# From sw0-p0 resend GARP for 10.0.0.10. hv1 should reclaim sw0-vir ++# and sw0-p1 should be its virtual_parent. ++send_garp 1 1 $eth_src $eth_dst $spa $tpa ++ ++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \ ++logical_port=sw0-vir) = x$hv1_ch_uuid], [0], []) ++ ++AT_CHECK([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \ ++logical_port=sw0-vir) = xsw0-p1]) ++ + # From sw0-p3 send GARP for 10.0.0.10. hv1 should claim sw0-vir + # and sw0-p3 should be its virtual_parent. + eth_src=505400000005 +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch b/SOURCES/0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch new file mode 100644 index 0000000..2c5a5e8 --- /dev/null +++ b/SOURCES/0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch @@ -0,0 +1,292 @@ +From 34b015a4b613329641e9aa4adf0bdd002e3e9177 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Fri, 22 Nov 2019 17:13:21 +0100 +Subject: [PATCH ovn 1/4] ovn-controller: Refactor I-P engine_run() tracking. + +This commit simplifies the logic of calling engine_run and engine_need_run in +order to reduce the number of external variables required to track the result +of the last engine execution. + +The engine code is also refactored a bit and the engine_run() function is +split in different functions that handle computing/recomputing a node. + +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit 2a4965c0e187db0c4218556ed9b06f988e88cb62) + +Change-Id: Ib5524493c871794acc166a6cc437b70abd470ed9 +--- + ovn/controller/ovn-controller.c | 33 ++++++----- + ovn/lib/inc-proc-eng.c | 120 ++++++++++++++++++++++++++-------------- + ovn/lib/inc-proc-eng.h | 7 ++- + 3 files changed, 103 insertions(+), 57 deletions(-) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index f2378ab..ad5b067 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -1905,7 +1905,6 @@ main(int argc, char *argv[]) + NULL); + + uint64_t engine_run_id = 0; +- uint64_t old_engine_run_id = 0; + bool engine_run_done = true; + + unsigned int ovs_cond_seqno = UINT_MAX; +@@ -1915,10 +1914,11 @@ main(int argc, char *argv[]) + exiting = false; + restart = false; + while (!exiting) { ++ engine_run_id++; ++ + update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl); + update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl)); + ofctrl_set_probe_interval(get_ofctrl_probe_interval(ovs_idl_loop.idl)); +- old_engine_run_id = engine_run_id; + + struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop); + unsigned int new_ovs_cond_seqno +@@ -2010,12 +2010,12 @@ main(int argc, char *argv[]) + if (engine_run_done) { + engine_set_abort_recompute(true); + engine_run_done = engine_run(&en_flow_output, +- ++engine_run_id); ++ engine_run_id); + } + } else { + engine_set_abort_recompute(false); + engine_run_done = true; +- engine_run(&en_flow_output, ++engine_run_id); ++ engine_run(&en_flow_output, engine_run_id); + } + } + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, +@@ -2060,17 +2060,20 @@ main(int argc, char *argv[]) + } + + } +- if (old_engine_run_id == engine_run_id || !engine_run_done) { +- if (!engine_run_done || engine_need_run(&en_flow_output)) { +- VLOG_DBG("engine did not run, force recompute next time: " +- "br_int %p, chassis %p", br_int, chassis); +- engine_set_force_recompute(true); +- poll_immediate_wake(); +- } else { +- VLOG_DBG("engine did not run, and it was not needed" +- " either: br_int %p, chassis %p", +- br_int, chassis); +- } ++ if (engine_need_run(&en_flow_output, engine_run_id)) { ++ VLOG_DBG("engine did not run, force recompute next time: " ++ "br_int %p, chassis %p", br_int, chassis); ++ engine_set_force_recompute(true); ++ poll_immediate_wake(); ++ } else if (!engine_run_done) { ++ VLOG_DBG("engine was aborted, force recompute next time: " ++ "br_int %p, chassis %p", br_int, chassis); ++ engine_set_force_recompute(true); ++ poll_immediate_wake(); ++ } else if (!engine_has_run(&en_flow_output, engine_run_id)) { ++ VLOG_DBG("engine did not run, and it was not needed" ++ " either: br_int %p, chassis %p", ++ br_int, chassis); + } else { + engine_set_force_recompute(false); + } +diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c +index 1064a08..ff07ad9 100644 +--- a/ovn/lib/inc-proc-eng.c ++++ b/ovn/lib/inc-proc-eng.c +@@ -129,14 +129,68 @@ engine_ovsdb_node_add_index(struct engine_node *node, const char *name, + } + + bool +-engine_run(struct engine_node *node, uint64_t run_id) ++engine_has_run(struct engine_node *node, uint64_t run_id) ++{ ++ return node->run_id == run_id; ++} ++ ++/* Do a full recompute (or at least try). If we're not allowed then ++ * mark the node as "aborted". ++ */ ++static bool ++engine_recompute(struct engine_node *node, bool forced, bool allowed) ++{ ++ VLOG_DBG("node: %s, recompute (%s)", node->name, ++ forced ? "forced" : "triggered"); ++ ++ if (!allowed) { ++ VLOG_DBG("node: %s, recompute aborted", node->name); ++ return false; ++ } ++ ++ node->run(node); ++ VLOG_DBG("node: %s, changed: %d", node->name, node->changed); ++ return true; ++} ++ ++/* Return true if the node could be computed, false otherwise. */ ++static bool ++engine_compute(struct engine_node *node, bool recompute_allowed) ++{ ++ for (size_t i = 0; i < node->n_inputs; i++) { ++ /* If the input node data changed call its change handler. */ ++ if (node->inputs[i].node->changed) { ++ VLOG_DBG("node: %s, handle change for input %s", ++ node->name, node->inputs[i].node->name); ++ ++ /* If the input change can't be handled incrementally, run ++ * the node handler. ++ */ ++ if (!node->inputs[i].change_handler(node)) { ++ VLOG_DBG("node: %s, can't handle change for input %s, " ++ "fall back to recompute", ++ node->name, node->inputs[i].node->name); ++ return engine_recompute(node, false, recompute_allowed); ++ } ++ } ++ } ++ ++ return true; ++} ++ ++bool engine_run(struct engine_node *node, uint64_t run_id) + { + if (node->run_id == run_id) { ++ /* The node was already updated in this run (could be input for ++ * multiple other nodes). Stop processing. ++ */ + return true; + } +- node->run_id = run_id; + ++ /* Initialize the node for this run. */ ++ node->run_id = run_id; + node->changed = false; ++ + if (!node->n_inputs) { + node->run(node); + VLOG_DBG("node: %s, changed: %d", node->name, node->changed); +@@ -150,59 +204,45 @@ engine_run(struct engine_node *node, uint64_t run_id) + } + + bool need_compute = false; +- bool need_recompute = false; + + if (engine_force_recompute) { +- need_recompute = true; +- } else { +- for (size_t i = 0; i < node->n_inputs; i++) { +- if (node->inputs[i].node->changed) { +- need_compute = true; +- if (!node->inputs[i].change_handler) { +- need_recompute = true; +- break; +- } +- } +- } ++ return engine_recompute(node, true, !engine_abort_recompute); + } + +- if (need_recompute) { +- VLOG_DBG("node: %s, recompute (%s)", node->name, +- engine_force_recompute ? "forced" : "triggered"); +- if (engine_abort_recompute) { +- VLOG_DBG("node: %s, recompute aborted", node->name); +- return false; +- } +- node->run(node); +- } else if (need_compute) { +- for (size_t i = 0; i < node->n_inputs; i++) { +- if (node->inputs[i].node->changed) { +- VLOG_DBG("node: %s, handle change for input %s", +- node->name, node->inputs[i].node->name); +- if (!node->inputs[i].change_handler(node)) { +- VLOG_DBG("node: %s, can't handle change for input %s, " +- "fall back to recompute", +- node->name, node->inputs[i].node->name); +- if (engine_abort_recompute) { +- VLOG_DBG("node: %s, recompute aborted", node->name); +- return false; +- } +- node->run(node); +- break; +- } ++ /* If any of the inputs updated data but there is no change_handler, then ++ * recompute the current node too. ++ */ ++ for (size_t i = 0; i < node->n_inputs; i++) { ++ if (node->inputs[i].node->changed) { ++ need_compute = true; ++ ++ /* Trigger a recompute if we don't have a change handler. */ ++ if (!node->inputs[i].change_handler) { ++ return engine_recompute(node, false, !engine_abort_recompute); + } + } + } + ++ if (need_compute) { ++ /* If we couldn't compute the node we either aborted or triggered ++ * a full recompute. In any case, stop processing. ++ */ ++ return engine_compute(node, !engine_abort_recompute); ++ } ++ + VLOG_DBG("node: %s, changed: %d", node->name, node->changed); + return true; + } + + bool +-engine_need_run(struct engine_node *node) ++engine_need_run(struct engine_node *node, uint64_t run_id) + { + size_t i; + ++ if (node->run_id == run_id) { ++ return false; ++ } ++ + if (!node->n_inputs) { + node->run(node); + VLOG_DBG("input node: %s, changed: %d", node->name, node->changed); +@@ -210,7 +250,7 @@ engine_need_run(struct engine_node *node) + } + + for (i = 0; i < node->n_inputs; i++) { +- if (engine_need_run(node->inputs[i].node)) { ++ if (engine_need_run(node->inputs[i].node, run_id)) { + return true; + } + } +diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h +index c3d7b5e..a4151ca 100644 +--- a/ovn/lib/inc-proc-eng.h ++++ b/ovn/lib/inc-proc-eng.h +@@ -130,9 +130,9 @@ bool engine_run(struct engine_node *, uint64_t run_id); + * terminates. */ + void engine_cleanup(struct engine_node *); + +-/* Check if engine needs to run, i.e. any change to be processed. */ ++/* Check if engine needs to run but didn't. */ + bool +-engine_need_run(struct engine_node *); ++engine_need_run(struct engine_node *, uint64_t run_id); + + /* Get the input node with for */ + struct engine_node * engine_get_input(const char *input_name, +@@ -159,6 +159,9 @@ const struct engine_context * engine_get_context(void); + + void engine_set_context(const struct engine_context *); + ++/* Return true if the engine has run for 'node' in the 'run_id' iteration. */ ++bool engine_has_run(struct engine_node *node, uint64_t run_id); ++ + struct ed_ovsdb_index { + const char *name; + struct ovsdb_idl_index *index; +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch b/SOURCES/0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch new file mode 100644 index 0000000..d641bb0 --- /dev/null +++ b/SOURCES/0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch @@ -0,0 +1,105 @@ +From ab959077c3dce4bd856f3d6bf633779a41dccdff Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Mon, 9 Dec 2019 09:12:18 +0100 +Subject: [PATCH ovn] ovn-controller: Run I-P engine even when no SB txn is + available. + +If the last ovn-controller main loop run started a transaction to the +Southbound DB and the transaction is still in progress, ovn-controller +will not call engine_run(). In case there were changes to the DB, +engine_need_run() would return true which would trigger an immediate +forced recompute in the next processing loop iteration. + +However, there are scenarios when updates can be processed incrementally +even if no Southbound DB transaction is available. One example, often +seen in the field, is when the MAC_Binding table is updated. Currently +such an update received while a transaction is still committing would +trigger a forced recompute. + +This patch performs only incremental processing when the SB DB txn +is NULL, which means executing change_handler() methods +only, without calling the run() methods of the I-P engine nodes. +Assuming that some change handlers need to update the DB and that +some don't, if the SB DB txn is NULL: +- if the incoming change doesn't trigger a SB DB update (currently true + for all existing change handlers) then the change handler might + complete without aborting if it doesn't trigger a run() of the node. +- if the incoming change tries to perform a SB DB update, the change + handler should return false (SB DB txn is NULL) triggering the engine + to call run() and abort computation. + +CC: Han Zhou +CC: Numan Siddique +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit e2ab60e3a7c60f3adb8da40e4d1cfeb890d6f80e) + +Change-Id: Ifdc87b925fc3e9c8b3c22d9b853ac27f2480f388 +--- + ovn/controller/ovn-controller.c | 8 ++++++++ + ovn/lib/inc-proc-eng.h | 15 ++++++++++++++- + 2 files changed, 22 insertions(+), 1 deletion(-) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index cb23bc8..c8470ce 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -2059,6 +2059,14 @@ main(int argc, char *argv[]) + } else { + engine_run(true); + } ++ } else { ++ /* Even if there's no SB DB transaction available, ++ * try to run the engine so that we can handle any ++ * incremental changes that don't require a recompute. ++ * If a recompute is required, the engine will abort, ++ * triggerring a full run in the next iteration. ++ */ ++ engine_run(false); + } + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, + time_msec()); +diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h +index 9cc99a3..c62f3bc 100644 +--- a/ovn/lib/inc-proc-eng.h ++++ b/ovn/lib/inc-proc-eng.h +@@ -84,6 +84,10 @@ struct engine_node_input { + * evaluated against all the other inputs. Returns: + * - true: if change can be handled + * - false: if change cannot be handled (indicating full recompute needed) ++ * A change handler can also call engine_get_context() but it must make ++ * sure the txn pointers returned by it are non-NULL. In case the change ++ * handler needs to use the txn pointers returned by engine_get_context(), ++ * and the pointers are NULL, the change handler MUST return false. + */ + bool (*change_handler)(struct engine_node *node, void *data); + }; +@@ -133,6 +137,9 @@ struct engine_node { + + /* Fully processes all inputs of this node and regenerates the data + * of this node. The pointer to the node's data is passed as argument. ++ * 'run' handlers can also call engine_get_context() and the ++ * implementation guarantees that the txn pointers returned ++ * engine_get_context() are not NULL and valid. + */ + void (*run)(struct engine_node *node, void *data); + +@@ -189,7 +196,13 @@ void engine_add_input(struct engine_node *node, struct engine_node *input, + * iteration, and the change can't be tracked across iterations */ + void engine_set_force_recompute(bool val); + +-const struct engine_context * engine_get_context(void); ++/* Return the current engine_context. The values in the context can be NULL ++ * if the engine is run with allow_recompute == false in the current ++ * iteration. ++ * Therefore, it is the responsibility of the caller to check the context ++ * values when called from change handlers. ++ */ ++const struct engine_context *engine_get_context(void); + + void engine_set_context(const struct engine_context *); + +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch b/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch new file mode 100644 index 0000000..83769dd --- /dev/null +++ b/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch @@ -0,0 +1,90 @@ +From 33fae9ee62a0f3c1ff951cde90d7330650681c32 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 2 Apr 2020 10:35:32 +0200 +Subject: [PATCH] ovn-controller: Skip vport bindings done through OVS + external_ids:iface-id. + +Port bindings of type "virtual" should not have an associated OVS port +in the integration bridge. If this is the case, it's a misconfig and +ovn-controller should ignore it. + +If such a situation is detected, ovn-controller will also log a warning +message to inform the user about the wrong configuration. + +Reported-at: https://bugzilla.redhat.com/1818844 +CC: Numan Siddique +Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'") +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique +(cherry picked from upstream commit 523b1f5f45682bd6dd454281a97a09c3f429c457) + +Change-Id: I65979e1f218f02dd24ba8625e516b766a03fd081 +--- + ovn/controller/binding.c | 12 ++++++++++++ + tests/ovn.at | 20 ++++++++++++++++++++ + 2 files changed, 32 insertions(+) + +diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c +index bf7b5ff..c71731a 100644 +--- a/ovn/controller/binding.c ++++ b/ovn/controller/binding.c +@@ -436,6 +436,18 @@ is_our_chassis(const struct sbrec_chassis *chassis_rec, + const struct ovsrec_interface *iface_rec + = shash_find_data(lport_to_iface, binding_rec->logical_port); + ++ /* Ports of type "virtual" should never be explicitly bound to an OVS ++ * port in the integration bridge. If that's the case, ignore the binding ++ * and log a warning. ++ */ ++ if (iface_rec && !strcmp(binding_rec->type, "virtual")) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, ++ "Virtual port %s should not be bound to OVS port %s", ++ binding_rec->logical_port, iface_rec->name); ++ return false; ++ } ++ + bool our_chassis = false; + if (iface_rec + || (binding_rec->parent_port && binding_rec->parent_port[0] && +diff --git a/tests/ovn.at b/tests/ovn.at +index 94e5ecd..d74a8b0 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -14639,6 +14639,11 @@ ovs-vsctl -- add-port br-int hv1-vif2 -- \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap \ + ofport-request=2 ++ovs-vsctl -- add-port br-int hv1-vif3 -- \ ++ set interface hv1-vif3 \ ++ options:tx_pcap=hv1/vif3-tx.pcap \ ++ options:rxq_pcap=hv1/vif3-rx.pcap \ ++ ofport-request=3 + + sim_add hv2 + as hv2 +@@ -14732,6 +14737,21 @@ logical_port=sw0-vir) = x], [0], []) + AT_CHECK([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \ + logical_port=sw0-vir) = x]) + ++# Try to bind sw0-vir directly to an OVS port. This should be ignored by ++# ovn-controller. ++as hv1 ++ovs-vsctl set interface hv1-vif3 external-ids:iface-id=sw0-vir ++ ++AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding \ ++logical_port=sw0-vir) = x], [0], []) ++ ++# Cleanup hv1-vif3. ++as hv1 ++ovs-vsctl del-port hv1-vif3 ++ ++AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding \ ++logical_port=sw0-vir) = x], [0], []) ++ + # From sw0-p0 send GARP for 10.0.0.10. hv1 should claim sw0-vir + # and sw0-p1 should be its virtual_parent. + eth_src=505400000003 +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch b/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch new file mode 100644 index 0000000..d0eb4d4 --- /dev/null +++ b/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch @@ -0,0 +1,169 @@ +From b58d7ac109938ceb88ff4d7197a94c6c2af38c63 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Mon, 17 Feb 2020 11:23:45 +0530 +Subject: [PATCH] ovn-ctl: Provide the option to configure inactive probe from + standby to active. + +Recently ovsdb-server supported an unixctl command - +ovsdb-server/set-active-ovsdb-server-probe-interval to configure inactive probe +interval from standby connection to the active. This patch provides the +option to configure this from ovn-ctl and the pacemaker OVN OCF script. + +Signed-off-by: Numan Siddique +Acked-by: Han Zhou + +(cherry-picked from upstream OVN repo commit 9991e91e7d592cef533b57af4405405ed5b41866) +Conflicts: + ovn/utilities/ovn-ctl + +Change-Id: I83a8f6e8d86836ed31946efc4aad5c07027f6d08 +--- + ovn/utilities/ovn-ctl | 6 ++++++ + ovn/utilities/ovn-ctl.8.xml | 8 ++++++-- + ovn/utilities/ovndb-servers.ocf | 23 +++++++++++++++++++---- + 3 files changed, 31 insertions(+), 6 deletions(-) + +diff --git a/ovn/utilities/ovn-ctl b/ovn/utilities/ovn-ctl +index 65f03e28d..e2c5e5b0e 100755 +--- a/ovn/utilities/ovn-ctl ++++ b/ovn/utilities/ovn-ctl +@@ -63,6 +63,7 @@ demote_ovnnb() { + if test -e $ovnnb_active_conf_file; then + ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/set-active-ovsdb-server `cat $ovnnb_active_conf_file` + ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/connect-active-ovsdb-server ++ ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/set-active-ovsdb-server-probe-interval $DB_NB_PROBE_INTERVAL_TO_ACTIVE + else + echo >&2 "$0: active server details not set" + exit 1 +@@ -77,6 +78,7 @@ demote_ovnsb() { + if test -e $ovnsb_active_conf_file; then + ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/set-active-ovsdb-server `cat $ovnsb_active_conf_file` + ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/connect-active-ovsdb-server ++ ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/set-active-ovsdb-server-probe-interval $DB_SB_PROBE_INTERVAL_TO_ACTIVE + else + echo >&2 "$0: active server details not set" + exit 1 +@@ -454,6 +456,7 @@ set_defaults () { + DB_NB_SYNC_FROM_PROTO=tcp + DB_NB_SYNC_FROM_ADDR= + DB_NB_SYNC_FROM_PORT=6641 ++ DB_NB_PROBE_INTERVAL_TO_ACTIVE=60000 + + DB_SB_SOCK=$OVN_RUNDIR/ovnsb_db.sock + DB_SB_PID=$OVN_RUNDIR/ovnsb_db.pid +@@ -463,6 +466,7 @@ set_defaults () { + DB_SB_SYNC_FROM_PROTO=tcp + DB_SB_SYNC_FROM_ADDR= + DB_SB_SYNC_FROM_PORT=6642 ++ DB_SB_PROBE_INTERVAL_TO_ACTIVE=60000 + + DB_NB_SCHEMA=$datadir/ovn-nb.ovsschema + DB_SB_SCHEMA=$datadir/ovn-sb.ovsschema +@@ -628,10 +632,12 @@ File location options: + --db-nb-sync-from-port=PORT OVN Northbound active db tcp port (default: $DB_NB_SYNC_FROM_PORT) + --db-nb-sync-from-proto=PROTO OVN Northbound active db transport (default: $DB_NB_SYNC_FROM_PROTO) + --db-nb-create-insecure-remote=yes|no Create ptcp OVN Northbound remote (default: $DB_NB_CREATE_INSECURE_REMOTE) ++ --db-nb-probe-interval-to-active Active probe interval from standby to active ovsdb-server remote (default: $DB_NB_PROBE_INTERVAL_TO_ACTIVE) + --db-sb-sync-from-addr=ADDR OVN Southbound active db tcp address (default: $DB_SB_SYNC_FROM_ADDR) + --db-sb-sync-from-port=ADDR OVN Southbound active db tcp port (default: $DB_SB_SYNC_FROM_PORT) + --db-sb-sync-from-proto=PROTO OVN Southbound active db transport (default: $DB_SB_SYNC_FROM_PROTO) + --db-sb-create-insecure-remote=yes|no Create ptcp OVN Southbound remote (default: $DB_SB_CREATE_INSECURE_REMOTE) ++ --db-sb-probe-interval-to-active Active probe interval from standby to active ovsdb-server remote (default: $DB_SB_PROBE_INTERVAL_TO_ACTIVE) + --db-nb-cluster-local-addr=ADDR OVN_Northbound cluster local address \ + (default: $DB_NB_CLUSTER_LOCAL_ADDR) + --db-nb-cluster-local-port=PORT OVN_Northbound cluster local tcp port \ +diff --git a/ovn/utilities/ovn-ctl.8.xml b/ovn/utilities/ovn-ctl.8.xml +index c5294d794..f0b3d6c87 100644 +--- a/ovn/utilities/ovn-ctl.8.xml ++++ b/ovn/utilities/ovn-ctl.8.xml +@@ -94,6 +94,10 @@ +

    --db-sb-cluster-remote-port=PORT NUMBER

    +

    --db-sb-cluster-remote-proto=PROTO (tcp/ssl)

    + ++

    Probe interval options

    ++

    --db-nb-probe-interval-to-active=Time in milliseconds

    ++

    --db-sb-probe-interval-to-active=Time in milliseconds

    ++ +

    Configuration files

    +

    Following are the optional configuration files. If present, it should be located in the etc dir

    + +@@ -150,8 +154,8 @@ +

    Promote and demote ovsdb servers

    +

    # ovn-ctl promote_ovnnb

    +

    # ovn-ctl promote_ovnsb

    +-

    # ovn-ctl --db-nb-sync-from-addr=x.x.x.x --db-nb-sync-from-port=6641 demote_ovnnb

    +-

    # ovn-ctl --db-sb-sync-from-addr=x.x.x.x --db-sb-sync-from-port=6642 demote_ovnsb

    ++

    # ovn-ctl --db-nb-sync-from-addr=x.x.x.x --db-nb-sync-from-port=6641 --db-nb-probe-interval-to-active=60000 demote_ovnnb

    ++

    # ovn-ctl --db-sb-sync-from-addr=x.x.x.x --db-sb-sync-from-port=6642 --db-sb-probe-interval-to-active=60000 demote_ovnsb

    + +

    Creating a clustered db on 3 nodes with IPs x.x.x.x, y.y.y.y and z.z.z.z

    +

    Starting OVN ovsdb servers and ovn-northd on the node with IP x.x.x.x

    +diff --git a/ovn/utilities/ovndb-servers.ocf b/ovn/utilities/ovndb-servers.ocf +index cd4742668..1737b192e 100755 +--- a/ovn/utilities/ovndb-servers.ocf ++++ b/ovn/utilities/ovndb-servers.ocf +@@ -9,6 +9,7 @@ + : ${SB_MASTER_PROTO_DEFAULT="tcp"} + : ${MANAGE_NORTHD_DEFAULT="no"} + : ${INACTIVE_PROBE_DEFAULT="5000"} ++: ${INACTIVE_PROBE_TO_MASTER_DEFAULT="60000"} + : ${LISTEN_ON_MASTER_IP_ONLY_DEFAULT="yes"} + : ${NB_SSL_KEY_DEFAULT="/etc/openvswitch/ovnnb-privkey.pem"} + : ${NB_SSL_CERT_DEFAULT="/etc/openvswitch/ovnnb-cert.pem"} +@@ -27,6 +28,7 @@ SB_MASTER_PORT=${OCF_RESKEY_sb_master_port:-${SB_MASTER_PORT_DEFAULT}} + SB_MASTER_PROTO=${OCF_RESKEY_sb_master_protocol:-${SB_MASTER_PROTO_DEFAULT}} + MANAGE_NORTHD=${OCF_RESKEY_manage_northd:-${MANAGE_NORTHD_DEFAULT}} + INACTIVE_PROBE=${OCF_RESKEY_inactive_probe_interval:-${INACTIVE_PROBE_DEFAULT}} ++INACTIVE_PROBE_TO_MASTER=${OCF_RESKEY_inactive_probe_interval_to_master:-${INACTIVE_PROBE_TO_MASTER_DEFAULT}} + NB_PRIVKEY=${OCF_RESKEY_ovn_nb_db_privkey:-${NB_SSL_KEY_DEFAULT}} + NB_CERT=${OCF_RESKEY_ovn_nb_db_cert:-${NB_SSL_CERT_DEFAULT}} + NB_CACERT=${OCF_RESKEY_ovn_nb_db_cacert:-${NB_SSL_CACERT_DEFAULT}} +@@ -135,6 +137,15 @@ ovsdb_server_metadata() { + + + ++ ++ ++ Inactive probe interval to use for the connection from standby ++ ovsdb-server to master ovsdb-server. ++ ++ Set inactive probe interval to master ++ ++ ++ + + + If set to yes, the OVNDBs will listen on master IP. Otherwise, it will +@@ -266,10 +277,12 @@ inactivity_probe=$INACTIVE_PROBE -- set SB_Global . connections=@conn_uuid + ocf_log debug "ovndb_server: Connecting to the new master ${OCF_RESKEY_CRM_meta_notify_promote_uname}" + ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \ + --db-nb-sync-from-port=${NB_MASTER_PORT} \ +- --db-nb-sync-from-proto=${NB_MASTER_PROTO} ++ --db-nb-sync-from-proto=${NB_MASTER_PROTO} \ ++ --db-nb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER} + ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \ + --db-sb-sync-from-port=${SB_MASTER_PORT} \ +- --db-sb-sync-from-proto=${SB_MASTER_PROTO} ++ --db-sb-sync-from-proto=${SB_MASTER_PROTO} \ ++ --db-sb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER} + fi + } + +@@ -596,10 +609,12 @@ ovsdb_server_demote() { + # being demoted. Sync to the surviving one + ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \ + --db-nb-sync-from-port=${NB_MASTER_PORT} \ +- --db-nb-sync-from-proto=${NB_MASTER_PROTO} ++ --db-nb-sync-from-proto=${NB_MASTER_PROTO} \ ++ --db-nb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER} + ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \ + --db-sb-sync-from-port=${SB_MASTER_PORT} \ +- --db-sb-sync-from-proto=${SB_MASTER_PROTO} ++ --db-sb-sync-from-proto=${SB_MASTER_PROTO} \ ++ --db-sb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER} + + else + # For completeness, should never be called +-- +2.25.1 + diff --git a/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch b/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch new file mode 100644 index 0000000..e9204b7 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch @@ -0,0 +1,194 @@ +From 35a77738b9032480c3983ff93080cd1cada85fad Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Thu, 12 Mar 2020 15:58:38 +0530 +Subject: [PATCH] ovn-northd: Add lflows to by pass the svc monitor packets + from conntrack. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The commit [1] added lflows to by pass the service monitor health check +packets from conntrack. But it missed out adding in the ingress pre_acl +and egress pre_acl of logical switch pipeline. + +This patch adds these missing lflows. It also enhanced the system lb health +check tests to add the acls to test this scenario. + +[1] - bb9f2b9ce56c("ovn-northd: Consider load balancer active backends in router pipeline) +Fixes: bb9f2b9ce56c("ovn-northd: Consider load balancer active backends in router pipeline) + +Change-Id: I591f98dbb945ea63acf0d1f37f0bb09f43e205c7 +Reported-by: Maciej Józefczyk +Acked-by: Dumitru Ceara +Acked-by: Maciej Jozefczyk +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 22 +++++++++++++++++++++- + ovn/northd/ovn-northd.c | 15 ++++++++++++++- + tests/ovn.at | 22 ++++++++++++++++++++++ + tests/system-ovn.at | 22 ++++++++++++++++++++++ + 4 files changed, 79 insertions(+), 2 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index c09e5ff44..f4d6c8f08 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -283,6 +283,16 @@ + priority-110 flow is added to skip over stateful ACLs. +

    + ++

    ++ This table also has a priority-110 flow with the match ++ eth.dst == E for all logical switch ++ datapaths to move traffic to the next table. Where E ++ is the service monitor mac defined in the ++ colum of table. ++

    ++ +

    Ingress Table 4: Pre-LB

    + +

    +@@ -310,7 +320,7 @@ + +

    + This table also has a priority-110 flow with the match +- eth.src == E for all logical switch ++ eth.dst == E for all logical switch + datapaths to move traffic to the next table. Where E + is the service monitor mac defined in the + to-lport traffic. +

    + ++

    ++ This table also has a priority-110 flow with the match ++ eth.src == E for all logical switch ++ datapaths to move traffic to the next table. Where E ++ is the service monitor mac defined in the ++ colum of table. ++

    ++ +

    Egress Table 2: Pre-stateful

    + +

    +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index abe5508c3..c2ac5ce07 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -4502,6 +4502,16 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows) + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 0, "1", "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 0, "1", "next;"); + ++ char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110, svc_check_match, ++ "next;"); ++ free(svc_check_match); ++ ++ svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac); ++ ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110, svc_check_match, ++ "next;"); ++ free(svc_check_match); ++ + /* If there are any stateful ACL rules in this datapath, we must + * send all IP packets through the conntrack action, which handles + * defragmentation, in order to match L4 headers. */ +@@ -4672,9 +4682,12 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, + "nd || nd_rs || nd_ra", "next;"); + + /* Do not send service monitor packets to conntrack. */ +- char *svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac); ++ char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110, + svc_check_match, "next;"); ++ free(svc_check_match); ++ ++ svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110, + svc_check_match, "next;"); + free(svc_check_match); +diff --git a/tests/ovn.at b/tests/ovn.at +index dd94776e6..9fb6e4f98 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -17032,12 +17032,34 @@ ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3" + ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4" + ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4" + ++# Create port group and ACLs for sw0 ports. ++ovn-nbctl pg-add pg0_drop sw0-p1 sw0-p2 ++ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop ++ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop ++ ++ovn-nbctl pg-add pg0 sw0-p1 sw0-p2 ++ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related ++ + # Create the second logical switch with one port + ovn-nbctl ls-add sw1 + ovn-nbctl lsp-add sw1 sw1-p1 + ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3" + ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3" + ++# Create port group and ACLs for sw1 ports. ++ovn-nbctl pg-add pg1_drop sw1-p1 ++ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop ++ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop ++ ++ovn-nbctl pg-add pg1 sw1-p1 ++ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip4" allow-related ++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related ++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related ++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related ++ + # Create a logical router and attach both logical switches + ovn-nbctl lr-add lr0 + ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 +diff --git a/tests/system-ovn.at b/tests/system-ovn.at +index 7d1c65d85..22e9a5926 100644 +--- a/tests/system-ovn.at ++++ b/tests/system-ovn.at +@@ -2552,12 +2552,34 @@ ovn-nbctl lsp-add sw0 sw0-p2 + ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4" + ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4" + ++# Create port group and ACLs for sw0 ports. ++ovn-nbctl pg-add pg0_drop sw0-p1 sw0-p2 ++ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop ++ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop ++ ++ovn-nbctl pg-add pg0 sw0-p1 sw0-p2 ++ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related ++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related ++ + # Create the second logical switch with one port + ovn-nbctl ls-add sw1 + ovn-nbctl lsp-add sw1 sw1-p1 + ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3" + ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3" + ++# Create port group and ACLs for sw1 ports. ++ovn-nbctl pg-add pg1_drop sw1-p1 ++ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop ++ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop ++ ++ovn-nbctl pg-add pg1 sw1-p1 ++ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip4" allow-related ++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related ++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related ++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related ++ + # Create a logical router and attach both logical switches + ovn-nbctl lr-add lr0 + ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 +-- +2.24.1 + diff --git a/SOURCES/0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch b/SOURCES/0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch new file mode 100644 index 0000000..4affee2 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch @@ -0,0 +1,700 @@ +From afab54226927711c719e8012c43333273f581ac7 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Sat, 1 Feb 2020 16:23:43 +0530 +Subject: [PATCH] ovn-northd: Address scale issues with DNAT flows. + +When the commit [1] added Distributed NAT support in OVN, it didn't address +the requirement of making East/West NAT traffic distributed. The E/W NAT +traffic was still centralized. Later a couple of patches [2], addressed this +requirement. But the approach taken in [2] resulted in a lot of logical flows +as number of dnat_and_snat entries increase, as reported in @Reported-at. + +This patch + - reverts the approch taken in [2]. + - removing the flows which does the NAT direct (REGBIT_NAT_REDIRECT) to + the gateway chassis. + - and to solve the E/W centralized NAT it does the following: + * Since for each NAT entry we know the MAC binding to be used for the + external_ip - either the external_mac if set or the MAC of the + distributed gateway router port, this patch adds the flows in the + S_ROUTER_IN_ARP_RESOLVE stage to set the eth.dst to the MAC if the + IP destination is external_ip. + * The existing flows in the S_ROUTER_OUT_EGR_LOOP are now added by additional + match - is_chassis_resident('P') - where 'P' is logical_port of the NAT entry + if set, otherwise it is the chassis resident port of distributed router port. + With this additional match, the packet will be loopbacked to apply the unSNAT/DNAT + rules on the relevant chassis. + +Suppose if a logical port 'P' with IP 'A' has a dnat_and_snat entry with external_mac/logical_port +set, and if the packet's IP destination is one of the DNAT IP - then the packet will be sent out +of the local chassis, since eth.dst is resolved in the S_ROUTER_IN_ARP_RESOLVE stage. +If the external_mac/logical_port is not in NAT entry, then the packet will be redirected to +the gateway chassis. + +With this patch, for the logical resource reported in @Reported-at, the number of logical +flows come down to around 45k from 650k. + +[1] - ceacd9d49316("ovn: distributed NAT flows") + +[2] - 551e3d989557("OVN: fix DVR Floating IP support") + 8244c6b6bd88("OVN: do not distribute traffic for local FIP") + +Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-January/049714.html +Reported-by: Daniel Alvarez Sanchez +Signed-off-by: Numan Siddique +Acked-by: Dumitru Ceara +Tested-By: Daniel Alvarez Sanchez +Acked-By: Daniel Alvarez Sanchez + +(cherry-picked from upstream commit 2dc7869436de32205f60128172196b3a207ab265) +Conflicts: + ovn/northd/ovn-northd.c + +Change-Id: I7684c7f5114ba7c800293e843d5d4b856dedbb96 +--- + ovn/northd/ovn-northd.8.xml | 191 ++++++++------------------ + ovn/northd/ovn-northd.c | 263 +++++------------------------------- + tests/ovn-northd.at | 8 +- + 3 files changed, 98 insertions(+), 364 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index d94d9aef9..a42a67c19 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1487,6 +1487,24 @@ next; +

    + +
      ++
    • ++

      ++ For each NAT entry of a distributed logical router (with ++ distributed gateway router port) of type snat, ++ a priorirty-120 flow with the match inport == P ++ && ip4.src == A advances the packet to ++ the next pipeline, where P is the distributed logical ++ router port and A is the external_ip set ++ in the NAT entry. If A is an IPv6 address, then ++ ip6.src is used for the match. ++

      ++ ++

      ++ The above flow is required to handle the routing of the East/west NAT ++ traffic. ++

      ++
    • ++ +
    • +

      + L3 admission control: A priority-100 flow drops packets that match +@@ -1977,21 +1995,6 @@ icmp6 { + redirect-chassis. +

      + +-

      +- For each configuration in the OVN Northbound database, that asks +- to change the source IP address of a packet from A to +- B, a priority-50 flow matches +- ip && ip4.dst == B or +- ip && ip6.dst == B +- with an action +- REGBIT_NAT_REDIRECT = 1; next;. This flow is for +- east/west traffic to a NAT destination IPv4/IPv6 address. By +- setting the REGBIT_NAT_REDIRECT flag, in the +- ingress table Gateway Redirect this will trigger a +- redirect to the instance of the gateway port on the +- redirect-chassis. +-

      +- +

      + A priority-0 logical flow with match 1 has actions + next;. +@@ -2147,20 +2150,6 @@ icmp6 { + redirect-chassis. +

      + +-

      +- For each configuration in the OVN Northbound database, that asks +- to change the destination IP address of a packet from A to +- B, a priority-50 flow matches ip && +- ip4.dst == B or ip && +- ip6.dst == B with an action +- REGBIT_NAT_REDIRECT = 1; next;. This flow is for +- east/west traffic to a NAT destination IPv4/IPv6 address. By +- setting the REGBIT_NAT_REDIRECT flag, in the +- ingress table Gateway Redirect this will trigger a +- redirect to the instance of the gateway port on the +- redirect-chassis. +-

      +- +

      + A priority-0 logical flow with match 1 has actions + next;. +@@ -2285,54 +2274,6 @@ output; +

      +
    • + +-
    • +-

      +- For distributed logical routers where one of the logical router +- ports specifies a redirect-chassis, a priority-400 +- logical flow for each ip source/destination couple that matches the +- dnat_and_snat NAT rules configured. These flows will +- allow to properly forward traffic to the external connections if +- available and avoid sending it through the tunnel. +- Assuming the two following NAT rules have been configured: +-

      +- +-
      +-external_ip{0,1} = EIP{0,1};
      +-external_mac{0,1} = MAC{0,1};
      +-logical_ip{0,1} = LIP{0,1};
      +-        
      +- +-

      +- the following action will be applied: +-

      +- +-
      +-eth.dst = MAC0;
      +-eth.src = MAC1;
      +-reg0 = ip4.dst; /* xxreg0 = ip6.dst; in the IPv6 case */
      +-reg1 = EIP1; /* xxreg1 in the IPv6 case */
      +-outport = redirect-chassis-port;
      +-REGBIT_DISTRIBUTED_NAT = 1; next;.
      +-        
      +- +-

      +- Morover a priority-400 logical flow is configured for each +- dnat_and_snat NAT rule configured in order to +- not send traffic for local FIP through the overlay tunnels +- but manage it in the local hypervisor +-

      +-
    • +- +-
    • +-

      +- For distributed logical routers where one of the logical router +- ports specifies a redirect-chassis, a priority-300 +- logical flow with match REGBIT_NAT_REDIRECT == 1 has +- actions ip.ttl--; next;. The outport +- will be set later in the Gateway Redirect table. +-

      +-
    • +- +
    • +

      + IPv4 routing table. For each route to IPv4 network N with +@@ -2427,23 +2368,6 @@ next; +

      +
    • + +-
    • +-

      +- For distributed logical routers where one of the logical router +- ports specifies a redirect-chassis, a priority-400 +- logical flow with match REGBIT_DISTRIBUTED_NAT == 1 +- has action next; +-

      +-

      +- For distributed logical routers where one of the logical router +- ports specifies a redirect-chassis, a priority-200 +- logical flow with match REGBIT_NAT_REDIRECT == 1 has +- actions eth.dst = E; next;, where +- E is the ethernet address of the router's distributed +- gateway port. +-

      +-
    • +- +
    • +

      + Static MAC bindings. MAC bindings can be known statically based on +@@ -2518,6 +2442,35 @@ next; +

      +
    • + ++
    • ++

      ++ Static MAC bindings from NAT entries. MAC bindings can also be known ++ for the entries in the NAT table. Below flows are ++ programmed for distributed logical routers i.e with a distributed ++ router port. ++

      ++ ++

      ++ For each row in the NAT table with IPv4 address ++ A in the column of ++ table, a priority-100 ++ flow with the match outport === P && ++ reg0 == A has actions eth.dst = E; ++ next;, where P is the distributed logical router ++ port, E is the Ethernet address if set in the ++ column ++ of table for of type ++ dnat_and_snat, otherwise the Ethernet address of the ++ distributed logical router port. ++

      ++ ++

      ++ For IPv6 NAT entries, same flows are added, but using the register ++ xxreg0 for the match. ++

      ++
    • ++ +
    • +

      + Dynamic MAC bindings. These flows resolve MAC-to-IP bindings +@@ -2640,20 +2593,6 @@ icmp4 { +

      + +
        +-
      • +- A priority-300 logical flow with match +- REGBIT_DISTRIBUTED_NAT == 1 has action +- next; +-
      • +-
      • +- A priority-200 logical flow with match +- REGBIT_NAT_REDIRECT == 1 has actions +- outport = CR; next;, where CR +- is the chassisredirect port representing the instance +- of the logical router distributed gateway port on the +- redirect-chassis. +-
      • +- +
      • + A priority-150 logical flow with match + outport == GW && +@@ -2945,19 +2884,6 @@ nd_ns { + ports specifies a redirect-chassis. +

        + +-

        +- Earlier in the ingress pipeline, some east-west traffic was +- redirected to the chassisredirect port, based on +- flows in the UNSNAT and DNAT ingress +- tables setting the REGBIT_NAT_REDIRECT flag, which +- then triggered a match to a flow in the +- Gateway Redirect ingress table. The intention was +- not to actually send traffic out the distributed gateway port +- instance on the redirect-chassis. This traffic was +- sent to the distributed gateway port instance in order for DNAT +- and/or SNAT processing to be applied. +-

        +- +

        + While UNDNAT and SNAT processing have already occurred by this + point, this traffic needs to be forced through egress loopback on +@@ -2973,23 +2899,20 @@ nd_ns { + +

          +
        • +-

          +- For each dnat_and_snat NAT rule couple in the +- OVN Northbound database on a distributed router, +- a priority-200 logical with match +- ip4.dst == external_ip0 && +- ip4.src == external_ip1, has action +- next; +-

          +- +

          + For each NAT rule in the OVN Northbound database on a + distributed router, a priority-100 logical flow with match + ip4.dst == E && +- outport == GW, where E is the +- external IP address specified in the NAT rule, and GW +- is the logical router distributed gateway port, with the +- following actions: ++ outport == GW && ++ is_chassis_resident(P), where E is the ++ external IP address specified in the NAT rule, GW ++ is the logical router distributed gateway port. For dnat_and_snat ++ NAT rule, P is the logical port specified in the NAT rule. ++ If column of ++ table is NOT set, then ++ P is the chassisredirect port of ++ GW with the following actions: +

          + +
          +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
          +index 58213742d..e2df94f4b 100644
          +--- a/ovn/northd/ovn-northd.c
          ++++ b/ovn/northd/ovn-northd.c
          +@@ -205,17 +205,16 @@ enum ovn_stage {
          + #define REGBIT_ND_RA_OPTS_RESULT "reg0[5]"
          + 
          + /* Register definitions for switches and routers. */
          +-#define REGBIT_NAT_REDIRECT     "reg9[0]"
          ++
          + /* Indicate that this packet has been recirculated using egress
          +  * loopback.  This allows certain checks to be bypassed, such as a
          +  * logical router dropping packets with source IP address equals
          +  * one of the logical router's own IP addresses. */
          +-#define REGBIT_EGRESS_LOOPBACK  "reg9[1]"
          +-#define REGBIT_DISTRIBUTED_NAT  "reg9[2]"
          ++#define REGBIT_EGRESS_LOOPBACK  "reg9[0]"
          + /* Register to store the result of check_pkt_larger action. */
          +-#define REGBIT_PKT_LARGER        "reg9[3]"
          +-#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]"
          +-#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]"
          ++#define REGBIT_PKT_LARGER        "reg9[1]"
          ++#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[2]"
          ++#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[3]"
          + 
          + #define FLAGBIT_NOT_VXLAN "flags[1] == 0"
          + 
          +@@ -6599,128 +6598,6 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
          +     ds_destroy(&actions);
          + }
          + 
          +-static void
          +-add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
          +-{
          +-    struct ds actions = DS_EMPTY_INITIALIZER;
          +-    struct ds match = DS_EMPTY_INITIALIZER;
          +-
          +-    if (!op->od->l3dgw_port) {
          +-        return;
          +-    }
          +-
          +-    if (!op->peer || !op->peer->od->nbs) {
          +-        return;
          +-    }
          +-
          +-    for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
          +-        const struct nbrec_nat *nat = op->od->nbr->nat[i];
          +-        bool found = false;
          +-        struct eth_addr mac;
          +-
          +-        if (strcmp(nat->type, "dnat_and_snat") ||
          +-                !nat->external_mac ||
          +-                !eth_addr_from_string(nat->external_mac, &mac) ||
          +-                !nat->external_ip || !nat->logical_port) {
          +-            continue;
          +-        }
          +-
          +-        const struct ovn_datapath *peer_dp = op->peer->od;
          +-        for (size_t j = 0; j < peer_dp->nbs->n_ports; j++) {
          +-            if (!strcmp(peer_dp->nbs->ports[j]->name, nat->logical_port)) {
          +-                found = true;
          +-                break;
          +-            }
          +-        }
          +-        if (!found) {
          +-            continue;
          +-        }
          +-
          +-        /* Determine if we need to create IPv4 or IPv6 flows */
          +-        ovs_be32 ip;
          +-        struct in6_addr ipv6;
          +-        int family = AF_INET;
          +-        if (!ip_parse(nat->external_ip, &ip) || !ip) {
          +-            family = AF_INET6;
          +-            if (!ipv6_parse(nat->external_ip, &ipv6)) {
          +-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
          +-                VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
          +-                             "for router %s", nat->external_ip, op->key);
          +-                /* We'll create IPv6 flows anyway, but the address
          +-                 * is probably bogus ... */
          +-            }
          +-        }
          +-
          +-        ds_put_format(&match, "inport == %s && "
          +-                      "ip%s.src == %s && ip%s.dst == %s",
          +-                       op->json_key,
          +-                       family == AF_INET ? "4" : "6",
          +-                       nat->logical_ip,
          +-                       family == AF_INET ? "4" : "6",
          +-                       nat->external_ip);
          +-        ds_put_format(&actions, "outport = %s; eth.dst = %s; "
          +-                      REGBIT_DISTRIBUTED_NAT" = 1; "
          +-                      REGBIT_NAT_REDIRECT" = 0; next;",
          +-                      op->od->l3dgw_port->json_key,
          +-                      nat->external_mac);
          +-        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
          +-                      ds_cstr(&match), ds_cstr(&actions));
          +-        ds_clear(&match);
          +-        ds_clear(&actions);
          +-
          +-        for (size_t j = 0; j < op->od->nbr->n_nat; j++) {
          +-            const struct nbrec_nat *nat2 = op->od->nbr->nat[j];
          +-            struct eth_addr mac2;
          +-
          +-            if (nat == nat2 || strcmp(nat2->type, "dnat_and_snat") ||
          +-                    !nat2->external_mac ||
          +-                    !eth_addr_from_string(nat2->external_mac, &mac2) ||
          +-                    !nat2->external_ip) {
          +-                continue;
          +-            }
          +-
          +-            family = AF_INET;
          +-            if (!ip_parse(nat2->external_ip, &ip) || !ip) {
          +-                family = AF_INET6;
          +-                if (!ipv6_parse(nat2->external_ip, &ipv6)) {
          +-                    static struct vlog_rate_limit rl =
          +-                        VLOG_RATE_LIMIT_INIT(5, 1);
          +-                    VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
          +-                                 "for router %s", nat2->external_ip, op->key);
          +-                    /* We'll create IPv6 flows anyway, but the address
          +-                     * is probably bogus ... */
          +-                }
          +-            }
          +-
          +-            ds_put_format(&match, "inport == %s && "
          +-                          "ip%s.src == %s && ip%s.dst == %s",
          +-                          op->json_key,
          +-                          family == AF_INET ? "4" : "6",
          +-                          nat->logical_ip,
          +-                          family == AF_INET ? "4" : "6",
          +-                          nat2->external_ip);
          +-            ds_put_format(&actions, "outport = %s; "
          +-                          "eth.src = %s; eth.dst = %s; "
          +-                          "%sreg0 = ip%s.dst; %sreg1 = %s; "
          +-                          REGBIT_DISTRIBUTED_NAT" = 1; "
          +-                          REGBIT_NAT_REDIRECT" = 0; next;",
          +-                          op->od->l3dgw_port->json_key,
          +-                          op->od->l3dgw_port->lrp_networks.ea_s,
          +-                          nat2->external_mac,
          +-                          family == AF_INET ? "" : "xx",
          +-                          family == AF_INET ? "4" : "6",
          +-                          family == AF_INET ? "" : "xx",
          +-                          nat->external_ip);
          +-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
          +-                          ds_cstr(&match), ds_cstr(&actions));
          +-            ds_clear(&match);
          +-            ds_clear(&actions);
          +-        }
          +-    }
          +-    ds_destroy(&match);
          +-    ds_destroy(&actions);
          +-}
          +-
          + static void
          + add_route(struct hmap *lflows, const struct ovn_port *op,
          +           const char *lrp_addr_s, const char *network_s, int plen,
          +@@ -8144,17 +8021,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
          + 
          +                     ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
          +                                   ds_cstr(&match), ds_cstr(&actions));
          +-
          +-                    /* Traffic received on other router ports must be
          +-                     * redirected to the central instance of the l3dgw_port
          +-                     * for NAT processing. */
          +-                    ds_clear(&match);
          +-                    ds_put_format(&match, "ip && ip%s.dst == %s",
          +-                                  is_v6 ? "6" : "4",
          +-                                  nat->external_ip);
          +-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
          +-                                  ds_cstr(&match),
          +-                                  REGBIT_NAT_REDIRECT" = 1; next;");
          +                 }
          +             }
          + 
          +@@ -8220,18 +8086,33 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
          + 
          +                     ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
          +                                   ds_cstr(&match), ds_cstr(&actions));
          ++                }
          ++            }
          + 
          +-                    /* Traffic received on other router ports must be
          +-                     * redirected to the central instance of the l3dgw_port
          +-                     * for NAT processing. */
          ++            /* ARP resolve for NAT IPs. */
          ++            if (od->l3dgw_port) {
          ++                if (!strcmp(nat->type, "snat")) {
          +                     ds_clear(&match);
          +-                    ds_put_format(&match, "ip && ip%s.dst == %s",
          +-                                  is_v6 ? "6" : "4",
          +-                                  nat->external_ip);
          +-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
          +-                                  ds_cstr(&match),
          +-                                  REGBIT_NAT_REDIRECT" = 1; next;");
          ++                    ds_put_format(
          ++                        &match, "inport == %s && %s == %s",
          ++                        od->l3dgw_port->json_key,
          ++                        is_v6 ? "ip6.src" : "ip4.src", nat->external_ip);
          ++                    ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 120,
          ++                                  ds_cstr(&match), "next;");
          +                 }
          ++
          ++                ds_clear(&match);
          ++                ds_put_format(
          ++                    &match, "outport == %s && %s == %s",
          ++                    od->l3dgw_port->json_key,
          ++                    is_v6 ? "xxreg0" : "reg0", nat->external_ip);
          ++                ds_clear(&actions);
          ++                ds_put_format(
          ++                    &actions, "eth.dst = %s; next;",
          ++                    distributed ? nat->external_mac :
          ++                    od->l3dgw_port->lrp_networks.ea_s);
          ++                ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 100,
          ++                                ds_cstr(&match), ds_cstr(&actions));
          +             }
          + 
          +             /* Egress UNDNAT table: It is for already established connections'
          +@@ -8378,49 +8259,19 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
          +              * ingress pipeline with inport = outport. */
          +             if (od->l3dgw_port) {
          +                 /* Distributed router. */
          +-                if (!strcmp(nat->type, "dnat_and_snat") &&
          +-                        nat->external_mac && nat->external_ip &&
          +-                        eth_addr_from_string(nat->external_mac, &mac)) {
          +-                    for (int j = 0; j < od->nbr->n_nat; j++) {
          +-                        const struct nbrec_nat *nat2 = od->nbr->nat[j];
          +-
          +-                        if (nat2 == nat ||
          +-                            strcmp(nat2->type, "dnat_and_snat") ||
          +-                            !nat2->external_mac || !nat2->external_ip) {
          +-                            continue;
          +-                        }
          +-
          +-                        ds_clear(&match);
          +-                        ds_put_format(&match, "is_chassis_resident(\"%s\") && "
          +-                                      "ip%s.src == %s && ip%s.dst == %s",
          +-                                      nat->logical_port,
          +-                                      is_v6 ? "6" : "4", nat2->external_ip,
          +-                                      is_v6 ? "6" : "4", nat->external_ip);
          +-                        ds_clear(&actions);
          +-                        ds_put_format(&actions,
          +-                                      "inport = outport; outport = \"\"; "
          +-                                      "flags = 0; flags.loopback = 1; "
          +-                                      REGBIT_EGRESS_LOOPBACK" = 1; "
          +-                                      "next(pipeline=ingress, table=0); ");
          +-                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 300,
          +-                                      ds_cstr(&match),  ds_cstr(&actions));
          +-
          +-                        ds_clear(&match);
          +-                        ds_put_format(&match,
          +-                                      "ip%s.src == %s && ip%s.dst == %s",
          +-                                      is_v6 ? "6" : "4", nat2->external_ip,
          +-                                      is_v6 ? "6" : "4", nat->external_ip);
          +-                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200,
          +-                                      ds_cstr(&match), "next;");
          +-                        ds_clear(&match);
          +-                    }
          +-                }
          +-
          +                 ds_clear(&match);
          +                 ds_put_format(&match, "ip%s.dst == %s && outport == %s",
          +                               is_v6 ? "6" : "4",
          +                               nat->external_ip,
          +                               od->l3dgw_port->json_key);
          ++                if (!distributed) {
          ++                    ds_put_format(&match, " && is_chassis_resident(%s)",
          ++                                  od->l3redirect_port->json_key);
          ++                } else {
          ++                    ds_put_format(&match, " && is_chassis_resident(\"%s\")",
          ++                                  nat->logical_port);
          ++                }
          ++
          +                 ds_clear(&actions);
          +                 ds_put_format(&actions,
          +                               "clone { ct_clear; "
          +@@ -8491,40 +8342,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
          +             * we can do it here, saving a future re-circulation. */
          +             ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
          +                           "ip", "flags.loopback = 1; ct_dnat;");
          +-        } else {
          +-            ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 400,
          +-                          REGBIT_DISTRIBUTED_NAT" == 1", "next;");
          +-
          +-            /* For NAT on a distributed router, add flows to Ingress
          +-             * IP Routing table, Ingress ARP Resolution table, and
          +-             * Ingress Gateway Redirect Table that are not specific to a
          +-             * NAT rule. */
          +-
          +-            /* The highest priority IN_IP_ROUTING rule matches packets
          +-             * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages),
          +-             * with action "ip.ttl--; next;".  The IN_GW_REDIRECT table
          +-             * will take care of setting the outport. */
          +-            ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 300,
          +-                          REGBIT_NAT_REDIRECT" == 1", "ip.ttl--; next;");
          +-
          +-            /* The highest priority IN_ARP_RESOLVE rule matches packets
          +-             * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages),
          +-             * then sets eth.dst to the distributed gateway port's
          +-             * ethernet address. */
          +-            ds_clear(&actions);
          +-            ds_put_format(&actions, "eth.dst = %s; next;",
          +-                          od->l3dgw_port->lrp_networks.ea_s);
          +-            ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 200,
          +-                          REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions));
          +-
          +-            /* The highest priority IN_GW_REDIRECT rule redirects packets
          +-             * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages) to
          +-             * the central instance of the l3dgw_port for NAT processing. */
          +-            ds_clear(&actions);
          +-            ds_put_format(&actions, "outport = %s; next;",
          +-                          od->l3redirect_port->json_key);
          +-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200,
          +-                          REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions));
          +         }
          + 
          +         /* Load balancing and packet defrag are only valid on
          +@@ -8720,9 +8537,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
          +             continue;
          +         }
          + 
          +-        /* create logical flows for DVR floating IPs */
          +-        add_distributed_nat_routes(lflows, op);
          +-
          +         for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
          +             add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s,
          +                       op->lrp_networks.ipv4_addrs[i].network_s,
          +@@ -9252,9 +9066,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
          +             continue;
          +         }
          +         if (od->l3dgw_port && od->l3redirect_port) {
          +-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
          +-                          REGBIT_DISTRIBUTED_NAT" == 1", "next;");
          +-
          +             /* For traffic with outport == l3dgw_port, if the
          +              * packet did not match any higher priority redirect
          +              * rule, then the traffic is redirected to the central
          +diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
          +index da566f900..3e4120ac5 100644
          +--- a/tests/ovn-northd.at
          ++++ b/tests/ovn-northd.at
          +@@ -990,7 +990,7 @@ echo "CR-LRP UUID is: " $uuid
          + # IPV4
          + ovn-nbctl lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
          + 
          +-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          + wc -l`])
          + 
          + AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
          +@@ -1008,7 +1008,7 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [0
          + ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
          + 
          + ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
          +-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          + wc -l`])
          + 
          + AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
          +@@ -1027,7 +1027,7 @@ ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
          + # IPV6
          + ovn-nbctl lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
          + 
          +-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          + wc -l`])
          + 
          + AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
          +@@ -1045,7 +1045,7 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [0
          + ovn-nbctl lr-nat-del R1 dnat_and_snat  fd01::1
          + ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
          + 
          +-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
          + wc -l`])
          + 
          + AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
          +-- 
          +2.24.1
          +
          diff --git a/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch b/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
          new file mode 100644
          index 0000000..dcf519d
          --- /dev/null
          +++ b/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
          @@ -0,0 +1,126 @@
          +From 90a9a77f60b039ff404737d116dc16aeff84944c Mon Sep 17 00:00:00 2001
          +From: Numan Siddique 
          +Date: Thu, 19 Mar 2020 16:52:17 +0530
          +Subject: [PATCH] ovn-northd: Don't add arp responder flows for lports with
          + 'unknown' address.
          +MIME-Version: 1.0
          +Content-Type: text/plain; charset=UTF-8
          +Content-Transfer-Encoding: 8bit
          +
          +If a logical port has 'unknown' address, it means it can send and receive
          +packet with any IP and MAC and generally port security is not set for
          +such logical ports. If an lport has addresses set to - ["MAC1 IP1", unknown],
          +right now we add arp responder flows for IP1 and respond MAC1 in the arp
          +response. But it's possible that the VIF of the logical port can use the IP1
          +with a different MAC. This patch supports this usecase. When another logical port
          +sends ARP request for IP1, the VIF of the logical port will anyway respond.
          +
          +Reported-by: Maciej Józefczyk 
          +Acked-by: Han Zhou 
          +Signed-off-by: Numan Siddique 
          +---
          + ovn/northd/ovn-northd.8.xml |  5 +++--
          + ovn/northd/ovn-northd.c     | 13 ++++++++-----
          + tests/ovn.at                | 16 ++++++++++++----
          + 3 files changed, 23 insertions(+), 11 deletions(-)
          +
          +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
          +index f4d6c8f08..5f9809951 100644
          +--- a/ovn/northd/ovn-northd.8.xml
          ++++ b/ovn/northd/ovn-northd.8.xml
          +@@ -656,8 +656,9 @@ output;
          + 
          +         

          + These flows are omitted for logical ports (other than router ports or +- localport ports) that are down and for logical ports of +- type virtual. ++ localport ports) that are down, for logical ports of ++ type virtual and for logical ports with 'unknown' ++ address set. +

          +
        • + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index c2ac5ce07..d734d36ee 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -1113,7 +1113,7 @@ struct ovn_port { + + bool derived; /* Indicates whether this is an additional port + * derived from nbsp or nbrp. */ +- ++ bool has_unknown; /* If the addresses have 'unknown' defined. */ + /* The port's peer: + * + * - A switch port S of type "router" has a router port R as a peer, +@@ -2026,8 +2026,11 @@ join_logical_ports(struct northd_context *ctx, + op->lsp_addrs + = xmalloc(sizeof *op->lsp_addrs * nbsp->n_addresses); + for (size_t j = 0; j < nbsp->n_addresses; j++) { +- if (!strcmp(nbsp->addresses[j], "unknown") +- || !strcmp(nbsp->addresses[j], "router")) { ++ if (!strcmp(nbsp->addresses[j], "unknown")) { ++ op->has_unknown = true; ++ continue; ++ } ++ if (!strcmp(nbsp->addresses[j], "router")) { + continue; + } + if (is_dynamic_lsp_address(nbsp->addresses[j])) { +@@ -5834,7 +5837,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + } else { + /* + * Add ARP/ND reply flows if either the +- * - port is up or ++ * - port is up and it doesn't have 'unknown' address defined or + * - port type is router or + * - port type is localport + */ +@@ -5843,7 +5846,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + +- if (lsp_is_external(op->nbsp)) { ++ if (lsp_is_external(op->nbsp) || op->has_unknown) { + continue; + } + +diff --git a/tests/ovn.at b/tests/ovn.at +index 9fb6e4f98..da016be9e 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -1715,11 +1715,13 @@ for is in 1 2 3; do + sip=`ip_to_hex 192 168 0 $is$js` + tip=`ip_to_hex 192 168 0 $id$jd` + tip_unknown=`ip_to_hex 11 11 11 11` ++ reply_ha=; + if test $d != $s; then +- reply_ha=f000000000$d +- else +- reply_ha= ++ if test $jd != 1; then ++ reply_ha=f000000000$d ++ fi + fi ++ + test_arp $s f000000000$s $sip $tip $reply_ha #9 + test_arp $s f000000000$s $sip $tip_unknown #10 + +@@ -2158,7 +2160,13 @@ for s in 1 2 3; do + sip=192.168.0.$s + tip=192.168.0.$d + tip_unknown=11.11.11.11 +- if test $d != $s; then reply_ha=f0:00:00:00:00:0$d; else reply_ha=; fi ++ reply_ha=; ++ if test $d != $s; then ++ if test $d != 1; then ++ reply_ha=f0:00:00:00:00:0$d; ++ fi ++ fi ++ + test_arp $s f0:00:00:00:00:0$s $sip $tip $reply_ha #9 + test_arp $s f0:00:00:00:00:0$s $sip $tip_unknown #10 + +-- +2.24.1 + diff --git a/SOURCES/0001-ovn-northd-Fix-IP-local-multicast-flooding.patch b/SOURCES/0001-ovn-northd-Fix-IP-local-multicast-flooding.patch new file mode 100644 index 0000000..828c0a4 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Fix-IP-local-multicast-flooding.patch @@ -0,0 +1,162 @@ +From 635df521886b802dd7d4dce0da45fda8759e9d1f Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 27 Feb 2020 10:15:59 +0100 +Subject: [PATCH] ovn-northd: Fix IP local multicast flooding. + +Skip IGMP entries learned for local multicast groups when generating +logical flows. We still allow ovn-controller to learn them as +it might be useful information for administrators to see that hosts +register for the groups even though they are not expected to send JOIN +messages for this range. + +Note: The upstream OVN master patch doesn't apply cleanly because OVN +2.12 doesn't support MLD. The conflict is however easy to solve and +involves removing the IPv6 specific code. + +Fixes: ddc64665b678 ("OVN: Add ovn-northd IGMP support") +Reported-by: Lucas Alvares Gomes +Reported-at: https://bugzilla.redhat.com/1803008 +Signed-off-by: Dumitru Ceara +Acked-by: Mark Michelson +(cherry picked from OVN commit 755ffada2a66416173d5f1e09672909d40f87fd1) + +Conflicts: + ovn/northd/ovn-northd.c + tests/ovn.at +Signed-off-by: Ben Pfaff + +(cherry picked from upstream commit 616e3c592af8dcf77a40af67042c4c417f4d550a) + +Conflicts: + ovn/northd/ovn-northd.c + +Change-Id: Iedfaa16b10521cbaa3a0e0b373476aa7ac5903fd +--- + ovn/northd/ovn-northd.c | 9 +++++++++ + tests/ovn.at | 48 +++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 48 insertions(+), 9 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 7cb3773..1d21f8c 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6309,6 +6309,15 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + struct mcast_switch_info *mcast_sw_info = + &igmp_group->datapath->mcast_info.sw; + ++ /* RFC 4541, section 2.1.2, item 2: Skip groups in the 224.0.0.X ++ * range. ++ */ ++ ovs_be32 group_address = ++ in6_addr_get_mapped_ipv4(&igmp_group->address); ++ if (ip_is_local_multicast(group_address)) { ++ continue; ++ } ++ + if (mcast_sw_info->active_flows >= mcast_sw_info->table_size) { + continue; + } +diff --git a/tests/ovn.at b/tests/ovn.at +index d74a8b0..d6e9b65 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -15293,7 +15293,7 @@ ovn-nbctl set Logical_Switch sw1 \ + other_config:mcast_snoop="true" + + # No IGMP query should be generated by sw1 (mcast_querier="false"). +-truncate -s 0 expected ++> expected + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected]) + OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected]) + OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) +@@ -15313,14 +15313,14 @@ send_igmp_v3_report hv2-vif1 hv2 000000000002 $(ip_to_hex 10 0 0 2) f9f9 \ + + # Check that the IGMP Group is learned on both hv. + OVS_WAIT_UNTIL([ +- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` ++ total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c` + test "${total_entries}" = "2" + ]) + + # Send traffic and make sure it gets forwarded only on the two ports that + # joined. +-truncate -s 0 expected +-truncate -s 0 expected_empty ++> expected ++> expected_empty + send_ip_multicast_pkt hv1-vif2 hv1 \ + 000000000001 01005e000144 \ + $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ +@@ -15345,15 +15345,15 @@ send_igmp_v3_report hv1-vif1 hv1 \ + + # Check IGMP_Group table on both HV. + OVS_WAIT_UNTIL([ +- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` ++ total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c` + test "${total_entries}" = "1" + ]) + + # Send traffic and make sure it gets forwarded only on the port that joined. + as hv1 reset_pcap_file hv1-vif1 hv1/vif1 + as hv2 reset_pcap_file hv2-vif1 hv2/vif1 +-truncate -s 0 expected +-truncate -s 0 expected_empty ++> expected ++> expected_empty + send_ip_multicast_pkt hv1-vif2 hv1 \ + 000000000001 01005e000144 \ + $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ +@@ -15373,10 +15373,40 @@ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) + # Flush IGMP groups. + ovn-sbctl ip-multicast-flush sw1 + OVS_WAIT_UNTIL([ +- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` ++ total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c` + test "${total_entries}" = "0" + ]) + ++# Check that traffic for 224.0.0.X is flooded even if some hosts register for ++# it. ++# Inject IGMP Join for 224.0.0.42 on sw1-p11. ++send_igmp_v3_report hv1-vif1 hv1 \ ++ 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ ++ $(ip_to_hex 224 0 0 42) 04 f9d3 \ ++ /dev/null ++ ++# Check that the IGMP Group is learned. ++OVS_WAIT_UNTIL([ ++ total_entries=`ovn-sbctl find IGMP_Group | grep "224.0.0.42" -c` ++ test "${total_entries}" = "1" ++]) ++ ++# Send traffic and make sure it gets flooded to all ports. ++as hv1 reset_pcap_file hv1-vif1 hv1/vif1 ++as hv1 reset_pcap_file hv1-vif2 hv1/vif2 ++as hv2 reset_pcap_file hv2-vif1 hv2/vif1 ++as hv2 reset_pcap_file hv2-vif2 hv2/vif2 ++> expected ++send_ip_multicast_pkt hv1-vif2 hv1 \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 224 0 0 42) 1e f989 11 \ ++ e518e518000a4b540000 \ ++ expected ++ ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected]) ++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) ++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected]) ++ + # Enable IGMP snooping and querier on sw2 and set query interval to minimum. + ovn-nbctl set Logical_Switch sw2 \ + other_config:mcast_snoop="true" \ +@@ -15386,7 +15416,7 @@ ovn-nbctl set Logical_Switch sw2 \ + other_config:mcast_ip4_src="20.0.0.254" + + # Wait for 1 query interval (1 sec) and check that two queries are generated. +-truncate -s 0 expected ++> expected + store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected + store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected + +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch b/SOURCES/0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch new file mode 100644 index 0000000..6941bae --- /dev/null +++ b/SOURCES/0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch @@ -0,0 +1,146 @@ +From adcdbd9a4ead77037f5d0e6fe9dbc64bf55dce0b Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Wed, 16 Oct 2019 15:06:41 +0200 +Subject: [PATCH ovn] ovn-northd: Fix IP multicast flooding to mrouter. + +OVN logical flow "drop" actions can't be combined with other actions. +Commit 79308138891a created such a scenario if a logical switch has +mcast_snoop=true, mcast_flood_unregistered=false and is connected to a +logical router with mcast_relay=enabled. + +To fix the issue we now explicitly add a drop flow for unregistered IP +multicast traffic in a logical switch if mcast_snoop=true, +mcast_flood_unregistered=false and the switch doesn't have any ports +with mcast_flood=true and isn't connected to a router with +mcast_relay=true. + +Fixes: 79308138891a ("ovn-northd: Add static IP multicast flood configuration") +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 13 +++++++++++++ + ovn/northd/ovn-northd.c | 8 +++++++- + tests/ovn.at | 50 ++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 67 insertions(+), 4 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 937702e..b5dfcd1 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -992,6 +992,19 @@ output; + + +
        • ++ A priority-80 flow that drops all unregistered IP multicast traffic ++ if ++ :mcast_snoop='true' and ++ ++ :mcast_flood_unregistered='false' and the switch is ++ not connected to a logical router that has ++ ++ :mcast_relay='true' and the switch doesn't have any ++ logical port with ++ :mcast_flood='true'. ++
        • ++ ++
        • + A priority-70 flow that outputs all packets with an Ethernet broadcast + or multicast eth.dst to the MC_FLOOD + multicast group. +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index e41c9d7..d0844dd 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -5661,7 +5661,13 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + + if (mcast_sw_info->flood_static) { + ds_put_cstr(&actions, "outport =\""MC_STATIC"\"; output;"); +- } else { ++ } ++ ++ /* Explicitly drop the traffic if relay or static flooding ++ * is not configured. ++ */ ++ if (!mcast_sw_info->flood_relay && ++ !mcast_sw_info->flood_static) { + ds_put_cstr(&actions, "drop;"); + } + +diff --git a/tests/ovn.at b/tests/ovn.at +index df00517..d141367 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -16306,7 +16306,7 @@ sleep 1 + OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected]) + OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected]) + +-# Dissable IGMP querier on sw2. ++# Disable IGMP querier on sw2. + ovn-nbctl set Logical_Switch sw2 \ + other_config:mcast_querier="false" + +@@ -16357,6 +16357,50 @@ send_igmp_v3_report hv2-vif3 hv2 \ + 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ + $(ip_to_hex 239 0 1 68) 04 e9b9 \ + /dev/null ++ ++# Check that the IGMP Group is learned by all switches. ++OVS_WAIT_UNTIL([ ++ total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` ++ test "${total_entries}" = "2" ++]) ++ ++# Send traffic from sw3 and make sure it is relayed by rtr. ++# to ports that joined. ++truncate -s 0 expected_routed_sw1 ++truncate -s 0 expected_routed_sw2 ++truncate -s 0 expected_empty ++ ++as hv1 reset_pcap_file hv1-vif1 hv1/vif1 ++as hv1 reset_pcap_file hv1-vif2 hv1/vif2 ++as hv1 reset_pcap_file hv1-vif3 hv1/vif3 ++as hv1 reset_pcap_file hv1-vif4 hv1/vif4 ++as hv2 reset_pcap_file hv2-vif1 hv2/vif1 ++as hv2 reset_pcap_file hv2-vif2 hv2/vif2 ++as hv2 reset_pcap_file hv2-vif3 hv2/vif3 ++as hv2 reset_pcap_file hv2-vif4 hv2/vif4 ++ ++send_ip_multicast_pkt hv2-vif4 hv2 \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 ++store_ip_multicast_pkt \ ++ 000000000100 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \ ++ e518e518000a3b3a0000 expected_routed_sw1 ++store_ip_multicast_pkt \ ++ 000000000200 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \ ++ e518e518000a3b3a0000 expected_routed_sw2 ++ ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1]) ++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2]) ++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty]) ++ + # Inject IGMP Join for 239.0.1.68 on sw3-p1. + send_igmp_v3_report hv1-vif4 hv1 \ + 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ +@@ -16369,8 +16413,8 @@ OVS_WAIT_UNTIL([ + test "${total_entries}" = "3" + ]) + +-# Send traffic from sw3 and make sure it is relayed by rtr. +-# and ports that joined. ++# Send traffic from sw3 and make sure it is relayed by rtr ++# to ports that joined. + truncate -s 0 expected_routed_sw1 + truncate -s 0 expected_routed_sw2 + truncate -s 0 expected_switched +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch b/SOURCES/0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch new file mode 100644 index 0000000..7fdaeec --- /dev/null +++ b/SOURCES/0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch @@ -0,0 +1,86 @@ +From f5fc57969797fcea4910f67560db12c9a3b69b77 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 16 Jan 2020 16:37:14 +0100 +Subject: [PATCH 1/2] ovn-northd: Fix Pre-LB logical flows with IPv4 and IPv6. + +When both IPv4 and IPv6 load balancers were configured, the logical flows +that send packets that need to be load balanced to conntrack (for +defragmentation) were incorrectly trying to match on IPv6 fields. + +Fix this issue by using two address sets, one for IPv4 and the other for +IPv6. + +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.c | 29 +++++++++++++++++------------ + 1 file changed, 17 insertions(+), 12 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 655d6240a..b71ae7a94 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -4639,13 +4639,14 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;"); + +- struct sset all_ips = SSET_INITIALIZER(&all_ips); ++ struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); ++ struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); + bool vip_configured = false; +- int addr_family = AF_INET; + for (int i = 0; i < od->nbs->n_load_balancer; i++) { + struct nbrec_load_balancer *lb = od->nbs->load_balancer[i]; + struct smap *vips = &lb->vips; + struct smap_node *node; ++ int addr_family = AF_INET; + + SMAP_FOR_EACH (node, vips) { + vip_configured = true; +@@ -4659,8 +4660,10 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, + continue; + } + +- if (!sset_contains(&all_ips, ip_address)) { +- sset_add(&all_ips, ip_address); ++ if (addr_family == AF_INET) { ++ sset_add(&all_ips_v4, ip_address); ++ } else { ++ sset_add(&all_ips_v6, ip_address); + } + + build_empty_lb_event_flow(od, lflows, node, ip_address, lb, +@@ -4679,20 +4682,22 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, + /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send + * packet to conntrack for defragmentation. */ + const char *ip_address; +- SSET_FOR_EACH(ip_address, &all_ips) { +- char *match; ++ SSET_FOR_EACH (ip_address, &all_ips_v4) { ++ char *match = xasprintf("ip && ip4.dst == %s", ip_address); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, ++ 100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;"); ++ free(match); ++ } + +- if (addr_family == AF_INET) { +- match = xasprintf("ip && ip4.dst == %s", ip_address); +- } else { +- match = xasprintf("ip && ip6.dst == %s", ip_address); +- } ++ SSET_FOR_EACH (ip_address, &all_ips_v6) { ++ char *match = xasprintf("ip && ip6.dst == %s", ip_address); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, + 100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;"); + free(match); + } + +- sset_destroy(&all_ips); ++ sset_destroy(&all_ips_v4); ++ sset_destroy(&all_ips_v6); + + if (vip_configured) { + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, +-- +2.24.1 + diff --git a/SOURCES/0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch b/SOURCES/0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch new file mode 100644 index 0000000..1812981 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch @@ -0,0 +1,199 @@ +From 913179b88c7b0e191a5cad635efb65e661726f09 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Tue, 12 Nov 2019 08:57:08 -0800 +Subject: [PATCH ovn 1/3] ovn-northd: Fix get_router_load_balancer_ips() for + mixed address families. + +Function get_router_load_balancer_ips() was incorrectly returning a +single address_family even though the IP set could contain a mix of +IPv4 and IPv6 addresses. + +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry-picked from upstream commit d0ef9f0ac9b9e78a8a54e31ca62a66c640c0346a) +--- + ovn/northd/ovn-northd.c | 126 +++++++++++++++++++++++++++--------------------- + 1 file changed, 72 insertions(+), 54 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index d94276b..81c313f 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -2184,7 +2184,7 @@ ip_address_and_port_from_lb_key(const char *key, char **ip_address, + + static void + get_router_load_balancer_ips(const struct ovn_datapath *od, +- struct sset *all_ips, int *addr_family) ++ struct sset *all_ips_v4, struct sset *all_ips_v6) + { + if (!od->nbr) { + return; +@@ -2199,13 +2199,21 @@ get_router_load_balancer_ips(const struct ovn_datapath *od, + /* node->key contains IP:port or just IP. */ + char *ip_address = NULL; + uint16_t port; ++ int addr_family; + + ip_address_and_port_from_lb_key(node->key, &ip_address, &port, +- addr_family); ++ &addr_family); + if (!ip_address) { + continue; + } + ++ struct sset *all_ips; ++ if (addr_family == AF_INET) { ++ all_ips = all_ips_v4; ++ } else { ++ all_ips = all_ips_v6; ++ } ++ + if (!sset_contains(all_ips, ip_address)) { + sset_add(all_ips, ip_address); + } +@@ -2299,17 +2307,22 @@ get_nat_addresses(const struct ovn_port *op, size_t *n) + } + } + +- /* A set to hold all load-balancer vips. */ +- struct sset all_ips = SSET_INITIALIZER(&all_ips); +- int addr_family; +- get_router_load_balancer_ips(op->od, &all_ips, &addr_family); ++ /* Two sets to hold all load-balancer vips. */ ++ struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); ++ struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); ++ get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); + + const char *ip_address; +- SSET_FOR_EACH (ip_address, &all_ips) { ++ SSET_FOR_EACH (ip_address, &all_ips_v4) { + ds_put_format(&c_addresses, " %s", ip_address); + central_ip_address = true; + } +- sset_destroy(&all_ips); ++ SSET_FOR_EACH (ip_address, &all_ips_v6) { ++ ds_put_format(&c_addresses, " %s", ip_address); ++ central_ip_address = true; ++ } ++ sset_destroy(&all_ips_v4); ++ sset_destroy(&all_ips_v6); + + if (central_ip_address) { + /* Gratuitous ARP for centralized NAT rules on distributed gateway +@@ -6893,61 +6906,66 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + } + + /* A set to hold all load-balancer vips that need ARP responses. */ +- struct sset all_ips = SSET_INITIALIZER(&all_ips); +- int addr_family; +- get_router_load_balancer_ips(op->od, &all_ips, &addr_family); ++ struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); ++ struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); ++ get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); + + const char *ip_address; +- SSET_FOR_EACH(ip_address, &all_ips) { ++ SSET_FOR_EACH (ip_address, &all_ips_v4) { + ds_clear(&match); +- if (addr_family == AF_INET) { +- ds_put_format(&match, +- "inport == %s && arp.tpa == %s && arp.op == 1", +- op->json_key, ip_address); +- } else { +- ds_put_format(&match, +- "inport == %s && nd_ns && nd.target == %s", +- op->json_key, ip_address); +- } ++ ds_put_format(&match, ++ "inport == %s && arp.tpa == %s && arp.op == 1", ++ op->json_key, ip_address); + + ds_clear(&actions); +- if (addr_family == AF_INET) { +- ds_put_format(&actions, +- "eth.dst = eth.src; " +- "eth.src = %s; " +- "arp.op = 2; /* ARP reply */ " +- "arp.tha = arp.sha; " +- "arp.sha = %s; " +- "arp.tpa = arp.spa; " +- "arp.spa = %s; " +- "outport = %s; " +- "flags.loopback = 1; " +- "output;", +- op->lrp_networks.ea_s, +- op->lrp_networks.ea_s, +- ip_address, +- op->json_key); +- } else { +- ds_put_format(&actions, +- "nd_na { " +- "eth.src = %s; " +- "ip6.src = %s; " +- "nd.target = %s; " +- "nd.tll = %s; " +- "outport = inport; " +- "flags.loopback = 1; " +- "output; " +- "};", +- op->lrp_networks.ea_s, +- ip_address, +- ip_address, +- op->lrp_networks.ea_s); +- } ++ ds_put_format(&actions, ++ "eth.dst = eth.src; " ++ "eth.src = %s; " ++ "arp.op = 2; /* ARP reply */ " ++ "arp.tha = arp.sha; " ++ "arp.sha = %s; " ++ "arp.tpa = arp.spa; " ++ "arp.spa = %s; " ++ "outport = %s; " ++ "flags.loopback = 1; " ++ "output;", ++ op->lrp_networks.ea_s, ++ op->lrp_networks.ea_s, ++ ip_address, ++ op->json_key); ++ + ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, + ds_cstr(&match), ds_cstr(&actions)); + } + +- sset_destroy(&all_ips); ++ SSET_FOR_EACH (ip_address, &all_ips_v6) { ++ ds_clear(&match); ++ ds_put_format(&match, ++ "inport == %s && nd_ns && nd.target == %s", ++ op->json_key, ip_address); ++ ++ ds_clear(&actions); ++ ds_put_format(&actions, ++ "nd_na { " ++ "eth.src = %s; " ++ "ip6.src = %s; " ++ "nd.target = %s; " ++ "nd.tll = %s; " ++ "outport = inport; " ++ "flags.loopback = 1; " ++ "output; " ++ "};", ++ op->lrp_networks.ea_s, ++ ip_address, ++ ip_address, ++ op->lrp_networks.ea_s); ++ ++ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, ++ ds_cstr(&match), ds_cstr(&actions)); ++ } ++ ++ sset_destroy(&all_ips_v4); ++ sset_destroy(&all_ips_v6); + + /* A gateway router can have 2 SNAT IP addresses to force DNATed and + * LBed traffic respectively to be SNATed. In addition, there can be +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch b/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch new file mode 100644 index 0000000..38e1dd9 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch @@ -0,0 +1,321 @@ +From 8f0056c923bbd0af9256ef7735d070e128fa77df Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Tue, 24 Mar 2020 11:03:29 +0100 +Subject: [PATCH 1/2] ovn-northd: Forward ARP requests on localnet ports. + +Commit 32f5ebb06226 limited the ARP/ND broadcast domain but in scenarios +where ARP responder flows are installed only on chassis that own the +associated logical ports ARP requests should still be forwarded on +localnet ports because the router pipeline should be executed on the +chassis that owns the logical port. Only that chassis will reply to the +ARP/ND request. + +Reported-by: Michael Plato +Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-March/049856.html +Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.") +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique +(cherry picked from upstream commit d2ab98463f299e67a9f9a31e8b7c42680b8645cf) + +Change-Id: I1c7cd9341b249ba754a698ac205a10ddc10d2dc2 +--- + ovn/northd/ovn-northd.8.xml | 3 +- + ovn/northd/ovn-northd.c | 6 +- + tests/ovn.at | 169 ++++++++++++++++++++++++++++++++++++++++---- + 3 files changed, 162 insertions(+), 16 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 5f98099..ad17323 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1104,7 +1104,8 @@ output; + Priority-75 flows for each IP address/VIP/NAT address owned by a + router port connected to the switch. These flows match ARP requests + and ND packets for the specific IP addresses. Matched packets are +- forwarded only to the router that owns the IP address. ++ forwarded only to the router that owns the IP address and, if ++ present, to the localnet port of the logical switch. +
        • + +
        • +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index d734d36..7cb3773 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -5625,8 +5625,12 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, + ds_put_cstr(&match, "}"); + + /* Send a the packet only to the router pipeline and skip flooding it +- * in the broadcast domain. ++ * in the broadcast domain (except for the localnet port). + */ ++ if (od->localnet_port) { ++ ds_put_format(&actions, "clone { outport = %s; output; }; ", ++ od->localnet_port->json_key); ++ } + ds_put_format(&actions, "outport = %s; output;", patch_op->json_key); + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, + ds_cstr(&match), ds_cstr(&actions)); +diff --git a/tests/ovn.at b/tests/ovn.at +index da016be..551c399 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -15856,13 +15856,14 @@ net_add n1 + sim_add hv1 + as hv1 + ovs-vsctl add-br br-phys ++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys + ovn_attach n1 br-phys 192.168.0.1 + +-ovs-vsctl -- add-port br-int hv1-vif1 -- \ +- set interface hv1-vif1 external-ids:iface-id=sw-agg-ext \ +- options:tx_pcap=hv1/vif1-tx.pcap \ +- options:rxq_pcap=hv1/vif1-rx.pcap \ +- ofport-request=1 ++sim_add hv2 ++as hv2 ++ovs-vsctl add-br br-phys ++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++ovn_attach n1 br-phys 192.168.0.2 + + # One Aggregation Switch connected to two Logical networks (routers). + ovn-nbctl ls-add sw-agg +@@ -15878,18 +15879,66 @@ ovn-nbctl lsp-add sw-agg sw-rtr2 \ + -- lsp-set-addresses sw-rtr2 00:00:00:00:02:00 \ + -- lsp-set-options sw-rtr2 router-port=rtr2-sw + +-# Configure L3 interface IPv4 & IPv6 on both routers ++# Localnet port on the Aggregation Switch. ++ovn-nbctl lsp-add sw-agg sw-agg-ln ++ovn-nbctl lsp-set-addresses sw-agg-ln unknown ++ovn-nbctl lsp-set-type sw-agg-ln localnet ++ovn-nbctl lsp-set-options sw-agg-ln network_name=phys ++ ++# Configure L3 interface IPv4 & IPv6 on both routers. + ovn-nbctl lr-add rtr1 + ovn-nbctl lrp-add rtr1 rtr1-sw 00:00:00:00:01:00 10.0.0.1/24 10::1/64 + ++ovn-nbctl lrp-add rtr1 rtr1-sw1 00:00:01:00:00:00 20.0.0.1/24 20::1/64 ++ + ovn-nbctl lr-add rtr2 + ovn-nbctl lrp-add rtr2 rtr2-sw 00:00:00:00:02:00 10.0.0.2/24 10::2/64 + ++# Configure router gateway ports. ++ovn-nbctl lrp-set-gateway-chassis rtr1-sw hv1 20 ++ovn-nbctl lrp-set-gateway-chassis rtr2-sw hv1 20 ++ ++# One private network behind rtr1 with two VMs. ++ovn-nbctl ls-add sw1 ++ovn-nbctl lsp-add sw1 sw1-p1 \ ++ -- lsp-set-addresses sw1-p1 00:00:00:01:00:00 ++ovn-nbctl lsp-add sw1 sw1-p2 \ ++ -- lsp-set-addresses sw1-p2 00:00:00:02:00:00 ++ovn-nbctl lsp-add sw1 sw1-rtr1 \ ++ -- lsp-set-type sw1-rtr1 router \ ++ -- lsp-set-addresses sw1-rtr1 00:00:01:00:00:00 \ ++ -- lsp-set-options sw1-rtr1 router-port=rtr1-sw1 ++ ++# Bind a "VM" connected to sw-agg on hv1. ++as hv1 ++ovs-vsctl -- add-port br-int hv1-vif0 -- \ ++ set interface hv1-vif0 external-ids:iface-id=sw-agg-ext \ ++ options:tx_pcap=hv1/vif0-tx.pcap \ ++ options:rxq_pcap=hv1/vif0-rx.pcap \ ++ ofport-request=1 ++ ++# Bind a "VM" connected to sw1 on hv1. ++as hv1 ++ovs-vsctl -- add-port br-int hv1-vif1 -- \ ++ set interface hv1-vif1 external-ids:iface-id=sw1-p1 \ ++ options:tx_pcap=hv1/vif1-tx.pcap \ ++ options:rxq_pcap=hv1/vif1-rx.pcap \ ++ ofport-request=2 ++ ++# Bind a "VM" connected to sw1 on hv2. ++as hv2 ++ovs-vsctl -- add-port br-int hv1-vif2 -- \ ++ set interface hv1-vif2 external-ids:iface-id=sw1-p2 \ ++ options:tx_pcap=hv1/vif2-tx.pcap \ ++ options:rxq_pcap=hv1/vif2-rx.pcap \ ++ ofport-request=3 ++ + OVN_POPULATE_ARP + ovn-nbctl --wait=hv sync + + sw_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding sw-agg) + sw_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw-agg) ++r1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding rtr1) + + r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1) + r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2) +@@ -15898,9 +15947,10 @@ mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${s + mc_key=$(printf "%04x" $mc_key) + + match_sw_metadata="metadata=0x${sw_dp_key}" ++match_r1_metadata="metadata=0x${r1_dp_key}" + + # Inject ARP request for first router owned IP address. +-send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1) ++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1) + + # Verify that the ARP request is sent only to rtr1. + match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1" +@@ -15929,7 +15979,7 @@ OVS_WAIT_UNTIL([ + # Inject ND_NS for ofirst router owned IP address. + src_ipv6=00100000000000000000000000000254 + dst_ipv6=00100000000000000000000000000001 +-send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d ++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d + + # Verify that the ND_NS is sent only to rtr1. + match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1" +@@ -15966,7 +16016,7 @@ ovn-nbctl lr-lb-add rtr2 lb2-v6 + ovn-nbctl --wait=hv sync + + # Inject ARP request for first router owned VIP address. +-send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11) ++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11) + + # Verify that the ARP request is sent only to rtr1. + match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1" +@@ -15995,7 +16045,7 @@ OVS_WAIT_UNTIL([ + # Inject ND_NS for first router owned VIP address. + src_ipv6=00100000000000000000000000000254 + dst_ipv6=00100000000000000000000000000011 +-send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d ++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d + + # Verify that the ND_NS is sent only to rtr1. + match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11" +@@ -16019,14 +16069,21 @@ OVS_WAIT_UNTIL([ + test "0" = "${pkts_flooded}" + ]) + +-# Configure NAT on both routers ++# Configure NAT on both routers. + ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1 + ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::111 42::1 + ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10.0.0.222 42.42.42.2 + ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10::222 42::2 + ++# Configure FIP1 and FIP2 on rtr1 for sw1-p1 and sw1-p2. ++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.121 20.0.0.11 sw1-p1 00:00:00:01:00:00 ++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::121 20::11 sw1-p1 00:00:00:01:00:00 ++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.122 20.0.0.12 sw1-p2 00:00:00:02:00:00 ++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::122 20::12 sw1-p2 00:00:00:02:00:00 ++ovn-nbctl --wait=hv sync ++ + # Inject ARP request for first router owned NAT address. +-send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111) ++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111) + + # Verify that the ARP request is sent only to rtr1. + match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1" +@@ -16052,10 +16109,50 @@ OVS_WAIT_UNTIL([ + test "0" = "${pkts_flooded}" + ]) + ++# Inject ARP request for FIP1. ++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121) ++ ++# Verify that the ARP request is replied to from hv1 and not hv2. ++match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.121,arp_op=1" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep n_packets=1 -c) ++ test "1" = "${pkts_on_rtr1}" ++]) ++ ++as hv2 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep n_packets=1 -c) ++ test "0" = "${pkts_on_rtr1}" ++]) ++ ++# Inject ARP request for FIP2. ++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 122) ++ ++# Verify that the ARP request is replied to from hv2 and not hv1. ++match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.122,arp_op=1" ++ ++as hv2 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep n_packets=1 -c) ++ test "1" = "${pkts_on_rtr1}" ++]) ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep n_packets=1 -c) ++ test "0" = "${pkts_on_rtr1}" ++]) ++ + # Inject ND_NS for first router owned IP address. + src_ipv6=00100000000000000000000000000254 + dst_ipv6=00100000000000000000000000000111 +-send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d ++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d + + # Verify that the ND_NS is sent only to rtr1. + match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111" +@@ -16079,7 +16176,51 @@ OVS_WAIT_UNTIL([ + test "0" = "${pkts_flooded}" + ]) + +-OVN_CLEANUP([hv1]) ++# Inject ND_NS for FIP1. ++src_ipv6=00100000000000000000000000000254 ++dst_ipv6=00100000000000000000000000000121 ++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72dd ++ ++# Verify that the ND_NS is replied to from hv1 and not hv2. ++match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::121" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep n_packets=1 -c) ++ test "1" = "${pkts_on_rtr1}" ++]) ++ ++as hv2 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep n_packets=1 -c) ++ test "0" = "${pkts_on_rtr1}" ++]) ++ ++# Inject ND_NS for FIP2. ++src_ipv6=00100000000000000000000000000254 ++dst_ipv6=00100000000000000000000000000122 ++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72db ++ ++# Verify that the ND_NS is replied to from hv2 and not hv1. ++match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::122" ++ ++as hv2 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep n_packets=1 -c) ++ test "1" = "${pkts_on_rtr1}" ++]) ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep n_packets=1 -c) ++ test "0" = "${pkts_on_rtr1}" ++]) ++ ++OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP + + +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch b/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch new file mode 100644 index 0000000..41013b4 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch @@ -0,0 +1,73 @@ +From 32972260b50f39d493cc42a78d9648ed668c2aec Mon Sep 17 00:00:00 2001 +Message-Id: <32972260b50f39d493cc42a78d9648ed668c2aec.1590584789.git.lorenzo.bianconi@redhat.com> +From: Han Zhou +Date: Tue, 5 May 2020 23:16:09 -0700 +Subject: [PATCH ovn] ovn-northd: Remove useless flow for GW_REDIRECT. + +Remove the flow in lr_in_gw_redirect stage: + + A priority-150 logical flow with match + outport == GW && + eth.dst == 00:00:00:00:00:00 has actions + outport = CR; next;, where + GW is the logical router distributed gateway + port and CR is the chassisredirect + port representing the instance of the logical router + distributed gateway port on the + redirect-chassis. + +The commit c0bf32d ("Manage ARP process locally in a DVR scenario") updated +the priority-100 flow in this stage to priority 200, which makes this +priority-150 flow useless, because whatever packets matching this flow +would also match the priority-50 flow. + +Cc: Lorenzo Bianconi +Tested-by: Lorenzo Bianconi +Acked-by: Numan Siddique +Signed-off-by: Han Zhou +Signed-off-by: Lorenzo Bianconi +--- + ovn/northd/ovn-northd.8.xml | 12 ------------ + ovn/northd/ovn-northd.c | 11 ----------- + 2 files changed, 23 deletions(-) + +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -2662,18 +2662,6 @@ icmp4 { + +
            +
          • +- A priority-150 logical flow with match +- outport == GW && +- eth.dst == 00:00:00:00:00:00 has actions +- outport = CR; next;, where +- GW is the logical router distributed gateway +- port and CR is the chassisredirect +- port representing the instance of the logical router +- distributed gateway port on the +- redirect-chassis. +-
          • +- +-
          • + For each NAT rule in the OVN Northbound database that can + be handled in a distributed manner, a priority-200 logical + flow with match ip4.src == B && +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -9196,16 +9196,6 @@ build_lrouter_flows(struct hmap *datapat + od->l3redirect_port->json_key); + ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50, + ds_cstr(&match), ds_cstr(&actions)); +- +- /* If the Ethernet destination has not been resolved, +- * redirect to the central instance of the l3dgw_port. +- * Such traffic will be replaced by an ARP request or ND +- * Neighbor Solicitation in the ARP request ingress +- * table, before being redirected to the central instance. +- */ +- ds_put_format(&match, " && eth.dst == 00:00:00:00:00:00"); +- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150, +- ds_cstr(&match), ds_cstr(&actions)); + } + + /* Packets are allowed by default. */ diff --git a/SOURCES/0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch b/SOURCES/0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch new file mode 100644 index 0000000..ffbfd40 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch @@ -0,0 +1,53 @@ +From 67d348798cddef1d48f1249c9dfdc0b8d9693371 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 1 Aug 2019 13:26:20 +0200 +Subject: [PATCH 1/4] ovn-northd: Use HMAP_FOR_EACH when adding multicast + lflows + +There's no need to use HMAP_FOR_EACH_SAFE when we add the logical flows +that correspond to IGMP groups as we don't modify the hashmap in the +loop. + +Also, we should reinitialize match and actions only if the flow will be +added. + +Fixes: ddc64665b678 ("OVN: Add ovn-northd IGMP support") +Signed-off-by: Dumitru Ceara +Acked-by: Numan Siddique +Signed-off-by: Mark Michelson +--- + ovn/northd/ovn-northd.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index eea9144d0..d81cfd893 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -5429,12 +5429,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + + /* Ingress table 17: Add IP multicast flows learnt from IGMP + * (priority 90). */ +- struct ovn_igmp_group *igmp_group, *next_igmp_group; +- +- HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, igmp_groups) { +- ds_clear(&match); +- ds_clear(&actions); ++ struct ovn_igmp_group *igmp_group; + ++ HMAP_FOR_EACH (igmp_group, hmap_node, igmp_groups) { + if (!igmp_group->datapath) { + continue; + } +@@ -5446,6 +5443,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + } + mcast_info->active_flows++; + ++ ds_clear(&match); ++ ds_clear(&actions); ++ + ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ", + igmp_group->mcgroup.name); + ds_put_format(&actions, "outport = \"%s\"; output; ", +-- +2.21.0 + diff --git a/SOURCES/0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch b/SOURCES/0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch new file mode 100644 index 0000000..1dd9243 --- /dev/null +++ b/SOURCES/0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch @@ -0,0 +1,68 @@ +From e02f2e47383097b737bb9a1cf3a371268c0693da Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 7 Nov 2019 13:19:19 +0100 +Subject: [PATCH ovn] ovn-northd: Validate dnat_and_snat + external_mac/logical_ip. + +When dnat_and_snat NAT rules are configured, if the user tries to set +external_mac in the NAT rule record without setting logical_ip +ovn-northd crashes as there's no validation in place. + +Add checks for valid ethernet address in NAT.external_mac and for +non-null NAT.logical_ip where applicable. + +Reported-by: Daniel Alvarez Sanchez +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1769709 +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index c23c270..2f0f501 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6032,9 +6032,12 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op) + for (size_t i = 0; i < op->od->nbr->n_nat; i++) { + const struct nbrec_nat *nat = op->od->nbr->nat[i]; + bool found = false; ++ struct eth_addr mac; + + if (strcmp(nat->type, "dnat_and_snat") || +- !nat->external_mac || !nat->external_ip) { ++ !nat->external_mac || ++ !eth_addr_from_string(nat->external_mac, &mac) || ++ !nat->external_ip || !nat->logical_port) { + continue; + } + +@@ -6083,10 +6086,14 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op) + + for (size_t j = 0; j < op->od->nbr->n_nat; j++) { + const struct nbrec_nat *nat2 = op->od->nbr->nat[j]; ++ struct eth_addr mac2; + + if (nat == nat2 || strcmp(nat2->type, "dnat_and_snat") || +- !nat2->external_mac || !nat2->external_ip) ++ !nat2->external_mac || ++ !eth_addr_from_string(nat2->external_mac, &mac2) || ++ !nat2->external_ip) { + continue; ++ } + + family = AF_INET; + if (!ip_parse(nat2->external_ip, &ip) || !ip) { +@@ -7785,7 +7792,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (od->l3dgw_port) { + /* Distributed router. */ + if (!strcmp(nat->type, "dnat_and_snat") && +- nat->external_mac && nat->external_ip) { ++ nat->external_mac && nat->external_ip && ++ eth_addr_from_string(nat->external_mac, &mac)) { + for (int j = 0; j < od->nbr->n_nat; j++) { + const struct nbrec_nat *nat2 = od->nbr->nat[j]; + +-- +1.8.3.1 + diff --git a/SOURCES/0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch b/SOURCES/0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch new file mode 100644 index 0000000..80d0f39 --- /dev/null +++ b/SOURCES/0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch @@ -0,0 +1,217 @@ +From 5d7f0f78f295f9cb02c7d90341e587f0a1cf4173 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Fri, 5 Jun 2020 14:00:29 +0530 +Subject: [PATCH 1/2] ovsdb idl: Try committing the pending txn in + ovsdb_idl_loop_run. + +The function ovsdb_idl_loop_run(), after calling ovsdb_idl_run(), +returns a transaction object (of type 'struct ovsdb_idl_txn'). +The returned transaction object can be NULL if there is a pending +transaction (loop->committing_txn) in the idl loop object. + +Normally the clients of idl library, first call ovsdb_idl_loop_run(), +then do their own processing and create any idl transactions during +this processing and then finally call ovsdb_idl_loop_commit_and_wait(). + +If ovsdb_idl_loop_run() returns NULL transaction object, then much +of the processing done by the client gets wasted as in the case +of ovn-controller. + +The client (in this case ovn-controller), can skip the processing +and instead call ovsdb_idl_loop_commit_and_wait() if the transaction +oject is NULL. But ovn-controller uses IDL tracking and it may +loose the tracked changes in that run. + +This patch tries to improve this scenario, by checking if the +pending transaction can be committed in the ovsdb_idl_loop_run() +itself and if the pending transaction is cleared (because of the +response messages from ovsdb-server due to a transaction message +in the previous run), ovsdb_idl_loop_run() can return a valid +transaction object. + +CC: Han Zhou +Signed-off-by: Numan Siddique +Signed-off-by: Ben Pfaff +--- + lib/ovsdb-idl.c | 143 +++++++++++++++++++++++++++++++----------------- + 1 file changed, 93 insertions(+), 50 deletions(-) + +diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c +index 190143f36..fd62470ff 100644 +--- a/lib/ovsdb-idl.c ++++ b/lib/ovsdb-idl.c +@@ -385,6 +385,8 @@ static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl); + static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *); + static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *); + static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *); ++static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, ++ bool *may_need_wakeup); + + static void + ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class, +@@ -5331,6 +5333,12 @@ struct ovsdb_idl_txn * + ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) + { + ovsdb_idl_run(loop->idl); ++ ++ /* See if we can commit the loop->committing_txn. */ ++ if (loop->committing_txn) { ++ ovsdb_idl_try_commit_loop_txn(loop, NULL); ++ } ++ + loop->open_txn = (loop->committing_txn + || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno + ? NULL +@@ -5338,6 +5346,87 @@ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) + return loop->open_txn; + } + ++/* Attempts to commit the current transaction, if one is open. ++ * ++ * If a transaction was open, in this or a previous iteration of the main loop, ++ * and had not before finished committing (successfully or unsuccessfully), the ++ * return value is one of: ++ * ++ * 1: The transaction committed successfully (or it did not change anything in ++ * the database). ++ * 0: The transaction failed. ++ * -1: The commit is still in progress. ++ * ++ * Thus, the return value is -1 if the transaction is in progress and otherwise ++ * true for success, false for failure. ++ * ++ * (In the corner case where the IDL sends a transaction to the database and ++ * the database commits it, and the connection between the IDL and the database ++ * drops before the IDL receives the message confirming the commit, this ++ * function can return 0 even though the transaction succeeded.) ++ */ ++static int ++ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, ++ bool *may_need_wakeup) ++{ ++ if (!loop->committing_txn) { ++ /* Not a meaningful return value: no transaction was in progress. */ ++ return 1; ++ } ++ ++ int retval; ++ struct ovsdb_idl_txn *txn = loop->committing_txn; ++ ++ enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); ++ if (status != TXN_INCOMPLETE) { ++ switch (status) { ++ case TXN_TRY_AGAIN: ++ /* We want to re-evaluate the database when it's changed from ++ * the contents that it had when we started the commit. (That ++ * might have already happened.) */ ++ loop->skip_seqno = loop->precommit_seqno; ++ if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno ++ && may_need_wakeup) { ++ *may_need_wakeup = true; ++ } ++ retval = 0; ++ break; ++ ++ case TXN_SUCCESS: ++ /* Possibly some work on the database was deferred because no ++ * further transaction could proceed. Wake up again. */ ++ retval = 1; ++ loop->cur_cfg = loop->next_cfg; ++ if (may_need_wakeup) { ++ *may_need_wakeup = true; ++ } ++ break; ++ ++ case TXN_UNCHANGED: ++ retval = 1; ++ loop->cur_cfg = loop->next_cfg; ++ break; ++ ++ case TXN_ABORTED: ++ case TXN_NOT_LOCKED: ++ case TXN_ERROR: ++ retval = 0; ++ break; ++ ++ case TXN_UNCOMMITTED: ++ case TXN_INCOMPLETE: ++ default: ++ OVS_NOT_REACHED(); ++ } ++ ovsdb_idl_txn_destroy(txn); ++ loop->committing_txn = NULL; ++ } else { ++ retval = -1; ++ } ++ ++ return retval; ++} ++ + /* Attempts to commit the current transaction, if one is open, and sets up the + * poll loop to wake up when some more work might be needed. + * +@@ -5368,57 +5457,11 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) + loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl); + } + +- struct ovsdb_idl_txn *txn = loop->committing_txn; +- int retval; +- if (txn) { +- enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); +- if (status != TXN_INCOMPLETE) { +- switch (status) { +- case TXN_TRY_AGAIN: +- /* We want to re-evaluate the database when it's changed from +- * the contents that it had when we started the commit. (That +- * might have already happened.) */ +- loop->skip_seqno = loop->precommit_seqno; +- if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) { +- poll_immediate_wake(); +- } +- retval = 0; +- break; +- +- case TXN_SUCCESS: +- /* Possibly some work on the database was deferred because no +- * further transaction could proceed. Wake up again. */ +- retval = 1; +- loop->cur_cfg = loop->next_cfg; +- poll_immediate_wake(); +- break; +- +- case TXN_UNCHANGED: +- retval = 1; +- loop->cur_cfg = loop->next_cfg; +- break; +- +- case TXN_ABORTED: +- case TXN_NOT_LOCKED: +- case TXN_ERROR: +- retval = 0; +- break; +- +- case TXN_UNCOMMITTED: +- case TXN_INCOMPLETE: +- default: +- OVS_NOT_REACHED(); +- } +- ovsdb_idl_txn_destroy(txn); +- loop->committing_txn = NULL; +- } else { +- retval = -1; +- } +- } else { +- /* Not a meaningful return value: no transaction was in progress. */ +- retval = 1; ++ bool may_need_wakeup = false; ++ int retval = ovsdb_idl_try_commit_loop_txn(loop, &may_need_wakeup); ++ if (may_need_wakeup) { ++ poll_immediate_wake(); + } +- + ovsdb_idl_wait(loop->idl); + + return retval; +-- +2.26.2 + diff --git a/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch b/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch new file mode 100644 index 0000000..32a5402 --- /dev/null +++ b/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch @@ -0,0 +1,97 @@ +From 1ba92ed2f401b8288481134279d7843908621c86 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Fri, 17 Apr 2020 11:45:56 +0530 +Subject: [PATCH] pinctrl: Handle service monitors even if the lport doesn't + have IPv4 addresses set. + +If a logical port is only configured with MAC address(es) in the +Logical_Switch_Port.addresses, pinctrl is ignoring the service monitors +configured for that logical port. This patch allows that. + +Change-Id: I0351395a88cd0d27ee3e18e1b2af7d9e0f0483b3 +Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1801058 +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique + +(cherry-picked from upstream ovn commit 71c6157575f4ac52484fa8703395301bd2189903) +--- + ovn/controller/pinctrl.c | 5 +++++ + tests/ovn.at | 31 +++++++++++++++++++++++++++++++ + tests/system-ovn.at | 4 ++-- + 3 files changed, 38 insertions(+), 2 deletions(-) + +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 1824347c2..8bc99df4e 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -4935,6 +4935,11 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, + + if (mac_found) { + break; ++ } else if (!laddrs.n_ipv4_addrs) { ++ /* IPv4 address(es) are not configured. Use the first mac. */ ++ ea = laddrs.ea; ++ mac_found = true; ++ break; + } + } + +diff --git a/tests/ovn.at b/tests/ovn.at +index d6e9b6581..c06337147 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -17345,5 +17345,36 @@ AT_CHECK([cat lflows.txt], [0], [dnl + table=6 (lr_in_dnat ), priority=120 , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(drop;) + ]) + ++# Delete sw0-p1 ++ovn-nbctl lsp-del sw0-p1 ++ ++OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l)]) ++ ++# Add back sw0-p1 but without any IP address. ++ovn-nbctl lsp-add sw0 sw0-p1 ++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03" -- \ ++lsp-set-port-security sw0-p1 "50:54:00:00:00:03" ++ ++OVS_WAIT_UNTIL([test 2 = $(ovn-sbctl --bare --columns status find \ ++service_monitor | grep offline | wc -l)]) ++ ++ovn-nbctl lsp-del sw0-p1 ++ovn-nbctl lsp-del sw1-p1 ++OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l)]) ++ ++# Add back sw0-p1 but without any address set. ++ovn-nbctl lsp-add sw0 sw0-p1 ++ ++OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l)]) ++ ++OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \ ++service_monitor | grep offline | wc -l)]) ++ ++OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \ ++service_monitor | grep online | wc -l)]) ++ + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP +diff --git a/tests/system-ovn.at b/tests/system-ovn.at +index 22e9a5926..d863c828d 100644 +--- a/tests/system-ovn.at ++++ b/tests/system-ovn.at +@@ -2545,8 +2545,8 @@ start_daemon ovn-controller + ovn-nbctl ls-add sw0 + + ovn-nbctl lsp-add sw0 sw0-p1 +-ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3" +-ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3" ++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03" ++ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03" + + ovn-nbctl lsp-add sw0 sw0-p2 + ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4" +-- +2.25.1 + diff --git a/SOURCES/0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch b/SOURCES/0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch new file mode 100644 index 0000000..624c064 --- /dev/null +++ b/SOURCES/0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch @@ -0,0 +1,401 @@ +From 4fb306df98419d45889dff6a5b4fb0c21f237609 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Fri, 29 May 2020 23:29:37 +0530 +Subject: [PATCH] pinctrl: Support DHCPRELEASE and DHCPINFORM in native OVN + dhcp responder. + +Right now we ignore these dhcp packets. This patch adds the support +as per RFC 2131. + +Change-Id: I57091f18212b93e3a366a97120f2a54009fde1d4 +Acked-by: Lorenzo Bianconi +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique + +(cherry-picked from upstream ovn master commit e008a4d46020a778b8f1f85b9dfd7c9e9b6fde21) +--- + ovn/controller/pinctrl.c | 125 +++++++++++++++++++++++++++++++-------- + ovn/lib/ovn-l7.h | 12 ++++ + tests/ovn.at | 117 +++++++++++++++++++++++++++++++++++- + 3 files changed, 227 insertions(+), 27 deletions(-) + +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 51a0f0224..b9e115a39 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -979,11 +979,13 @@ static void + pinctrl_handle_put_dhcp_opts( + struct rconn *swconn, + struct dp_packet *pkt_in, struct ofputil_packet_in *pin, +- struct ofpbuf *userdata, struct ofpbuf *continuation) ++ struct flow *in_flow, struct ofpbuf *userdata, ++ struct ofpbuf *continuation) + { + enum ofp_version version = rconn_get_version(swconn); + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); + struct dp_packet *pkt_out_ptr = NULL; ++ struct ofpbuf *dhcp_inform_reply_buf = NULL; + uint32_t success = 0; + + /* Parse result field. */ +@@ -1107,22 +1109,15 @@ pinctrl_handle_put_dhcp_opts( + VLOG_WARN_RL(&rl, "Missing DHCP message type"); + goto exit; + } +- if (*in_dhcp_msg_type != DHCP_MSG_DISCOVER && +- *in_dhcp_msg_type != DHCP_MSG_REQUEST) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); +- VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type); +- goto exit; +- } + +- uint8_t msg_type; +- if (*in_dhcp_msg_type == DHCP_MSG_DISCOVER) { ++ struct ofpbuf *reply_dhcp_opts_ptr = userdata; ++ uint8_t msg_type = 0; ++ ++ switch (*in_dhcp_msg_type) { ++ case DHCP_MSG_DISCOVER: + msg_type = DHCP_MSG_OFFER; +- } else { +- /* This is a DHCPREQUEST. If the client has requested an IP that +- * does not match the offered IP address, reply with a NAK. The +- * requested IP address may be supplied either via Requested IP Address +- * (opt 50) or via ciaddr, depending on the client's state. +- */ ++ break; ++ case DHCP_MSG_REQUEST: { + msg_type = DHCP_MSG_ACK; + if (request_ip != *offer_ip) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); +@@ -1131,12 +1126,81 @@ pinctrl_handle_put_dhcp_opts( + IP_ARGS(*offer_ip)); + msg_type = DHCP_MSG_NAK; + } ++ break; ++ } ++ case OVN_DHCP_MSG_RELEASE: { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40); ++ const struct eth_header *l2 = dp_packet_eth(pkt_in); ++ VLOG_INFO_RL(&rl, "DHCPRELEASE "ETH_ADDR_FMT " "IP_FMT"", ++ ETH_ADDR_ARGS(l2->eth_src), ++ IP_ARGS(in_dhcp_data->ciaddr)); ++ break; ++ } ++ case OVN_DHCP_MSG_INFORM: { ++ /* RFC 2131 section 3.4. ++ * Remove all the offer ip related dhcp options and ++ * all the time related dhcp options. ++ * Loop through the dhcp option defined in the userdata buffer ++ * and copy all the options into dhcp_inform_reply_buf skipping ++ * the not required ones. ++ * */ ++ msg_type = DHCP_MSG_ACK; ++ in_dhcp_ptr = userdata->data; ++ end = (const char *)userdata->data + userdata->size; ++ ++ /* The buf size cannot be greater > userdata->size. */ ++ dhcp_inform_reply_buf = ofpbuf_new(userdata->size); ++ ++ reply_dhcp_opts_ptr = dhcp_inform_reply_buf; ++ while (in_dhcp_ptr < end) { ++ const struct dhcp_opt_header *in_dhcp_opt = ++ (const struct dhcp_opt_header *)in_dhcp_ptr; ++ ++ switch (in_dhcp_opt->code) { ++ case OVN_DHCP_OPT_CODE_NETMASK: ++ case OVN_DHCP_OPT_CODE_LEASE_TIME: ++ case OVN_DHCP_OPT_CODE_T1: ++ case OVN_DHCP_OPT_CODE_T2: ++ break; ++ default: ++ /* Copy the dhcp option to reply_dhcp_opts_ptr. */ ++ ofpbuf_put(reply_dhcp_opts_ptr, in_dhcp_opt, ++ in_dhcp_opt->len + sizeof *in_dhcp_opt); ++ break; ++ } ++ ++ in_dhcp_ptr += sizeof *in_dhcp_opt; ++ if (in_dhcp_ptr > end) { ++ break; ++ } ++ in_dhcp_ptr += in_dhcp_opt->len; ++ if (in_dhcp_ptr > end) { ++ break; ++ } ++ } ++ ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40); ++ VLOG_INFO_RL(&rl, "DHCPINFORM from "ETH_ADDR_FMT " "IP_FMT"", ++ ETH_ADDR_ARGS(in_flow->dl_src), ++ IP_ARGS(in_flow->nw_src)); ++ ++ break; ++ } ++ default: { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type); ++ goto exit; ++ } ++ } ++ ++ if (!msg_type) { ++ goto exit; + } + + /* Frame the DHCP reply packet +- * Total DHCP options length will be options stored in the userdata + +- * 16 bytes. Note that the DHCP options stored in userdata are not included +- * in DHCPNAK messages. ++ * Total DHCP options length will be options stored in the ++ * reply_dhcp_opts_ptr + 16 bytes. Note that the DHCP options stored in ++ * reply_dhcp_opts_ptr are not included in DHCPNAK messages. + * + * -------------------------------------------------------------- + *| 4 Bytes (dhcp cookie) | 3 Bytes (option type) | DHCP options | +@@ -1146,7 +1210,7 @@ pinctrl_handle_put_dhcp_opts( + */ + uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16; + if (msg_type != DHCP_MSG_NAK) { +- new_l4_size += userdata->size; ++ new_l4_size += reply_dhcp_opts_ptr->size; + } + size_t new_packet_size = pkt_in->l4_ofs + new_l4_size; + +@@ -1171,12 +1235,18 @@ pinctrl_handle_put_dhcp_opts( + struct dhcp_header *dhcp_data = dp_packet_put( + &pkt_out, dp_packet_pull(pkt_in, DHCP_HEADER_LEN), DHCP_HEADER_LEN); + dhcp_data->op = DHCP_OP_REPLY; +- dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip; ++ ++ if (*in_dhcp_msg_type != OVN_DHCP_MSG_INFORM) { ++ dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip; ++ } else { ++ dhcp_data->yiaddr = 0; ++ } ++ + dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32)); + + uint16_t out_dhcp_opts_size = 12; + if (msg_type != DHCP_MSG_NAK) { +- out_dhcp_opts_size += userdata->size; ++ out_dhcp_opts_size += reply_dhcp_opts_ptr->size; + } + uint8_t *out_dhcp_opts = dp_packet_put_zeros(&pkt_out, + out_dhcp_opts_size); +@@ -1187,8 +1257,9 @@ pinctrl_handle_put_dhcp_opts( + out_dhcp_opts += 3; + + if (msg_type != DHCP_MSG_NAK) { +- memcpy(out_dhcp_opts, userdata->data, userdata->size); +- out_dhcp_opts += userdata->size; ++ memcpy(out_dhcp_opts, reply_dhcp_opts_ptr->data, ++ reply_dhcp_opts_ptr->size); ++ out_dhcp_opts += reply_dhcp_opts_ptr->size; + } + + /* Padding */ +@@ -1236,6 +1307,10 @@ exit: + if (pkt_out_ptr) { + dp_packet_uninit(pkt_out_ptr); + } ++ ++ if (dhcp_inform_reply_buf) { ++ ofpbuf_delete(dhcp_inform_reply_buf); ++ } + } + + static bool +@@ -1936,8 +2011,8 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) + break; + + case ACTION_OPCODE_PUT_DHCP_OPTS: +- pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &userdata, +- &continuation); ++ pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &headers, ++ &userdata, &continuation); + break; + + case ACTION_OPCODE_ND_NA: +diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h +index c43218224..f81acb0f4 100644 +--- a/ovn/lib/ovn-l7.h ++++ b/ovn/lib/ovn-l7.h +@@ -34,6 +34,14 @@ struct gen_opts_map { + + #define DHCP_BROADCAST_FLAG 0x8000 + ++/* These are not defined in ovs/lib/dhcp.h and hence defined here with ++ * OVN_DHCP_OPT_CODE_. ++ */ ++#define OVN_DHCP_OPT_CODE_NETMASK 1 ++#define OVN_DHCP_OPT_CODE_LEASE_TIME 51 ++#define OVN_DHCP_OPT_CODE_T1 58 ++#define OVN_DHCP_OPT_CODE_T2 59 ++ + #define DHCP_OPTION(NAME, CODE, TYPE) \ + {.name = NAME, .code = CODE, .type = TYPE} + +@@ -161,6 +169,10 @@ struct dhcp_opt6_header { + ovs_be16 size; + }; + ++/* These are not defined in ovs/lib/dhcp.h, hence defining here. */ ++#define OVN_DHCP_MSG_RELEASE 7 ++#define OVN_DHCP_MSG_INFORM 8 ++ + /* Supported DHCPv6 Message Types */ + #define DHCPV6_MSG_TYPE_SOLICIT 1 + #define DHCPV6_MSG_TYPE_ADVT 2 +diff --git a/tests/ovn.at b/tests/ovn.at +index 0c545b0b2..203c1d80f 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -4639,6 +4639,12 @@ test_dhcp() { + done + if test $offer_ip != 0; then + local srv_mac=$1 srv_ip=$2 dhcp_reply_type=$3 expected_dhcp_opts=$4 ++ local offered_ip=$offer_ip ++ if [[ "$dhcp_type" == "08" ]]; then ++ # DHCP ACK for DHCP INFORM should not have any offer ip. ++ offered_ip=00000000 ++ fi ++ + # total IP length will be the IP length of the request packet + # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2) + ip_len=`expr 280 + ${#expected_dhcp_opts} / 2` +@@ -4654,7 +4660,7 @@ test_dhcp() { + if test $dhcp_reply_type = 06; then + reply=${reply}00000000 + else +- reply=${reply}${offer_ip} ++ reply=${reply}${offered_ip} + fi + # next server ip address, relay agent ip address, client mac address + reply=${reply}0000000000000000${src_mac} +@@ -4794,7 +4800,7 @@ rm -f 2.expected + ciaddr=`ip_to_hex 0 0 0 0` + offer_ip=0 + request_ip=0 +-test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 1 ++test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 1 1 + + # NXT_RESUMEs should be 4. + OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) +@@ -4964,6 +4970,113 @@ AT_CHECK([cat 1.packets | cut -c -48], [0], [expout]) + cat 1.expected | cut -c 53- > expout + AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout]) + ++reset_pcap_file hv1-vif1 hv1/vif1 ++reset_pcap_file hv1-vif2 hv1/vif2 ++rm -f 1.expected ++rm -f 2.expected ++ ++# Send DHCPRELEASE. ++offer_ip=0 ++server_ip=`ip_to_hex 10 0 0 1` ++ciaddr=`ip_to_hex 10 0 0 6` ++request_ip=0 ++expected_dhcp_opts=0 ++test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 ++ ++# NXT_RESUMEs should be 10. ++OVS_WAIT_UNTIL([test 10 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) ++ ++# There is no reply for this. Check for the INFO log in ovn-controller.log ++AT_CHECK([test 1 = $(cat hv1/ovn-controller.log | \ ++grep "DHCPRELEASE f0:00:00:00:00:02 10.0.0.6" -c)]) ++ ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets ++AT_CHECK([cat 2.packets], [0], []) ++ ++reset_pcap_file hv1-vif1 hv1/vif1 ++reset_pcap_file hv1-vif2 hv1/vif2 ++rm -f 1.expected ++rm -f 2.expected ++ ++# Send DHCPINFORM ++offer_ip=`ip_to_hex 10 0 0 6` ++server_ip=`ip_to_hex 10 0 0 1` ++ciaddr=$offer_ip ++request_ip=0 ++src_ip=$offer_ip ++dst_ip=$server_ip ++# In the expected_dhcp_opts we should not see 330400000e10 which is ++# dhcp lease time option and 0104ffffff00 which is subnet mask option. ++expected_dhcp_opts=03040a00000136040a000001 ++test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts ++ ++# NXT_RESUMEs should be 11. ++OVS_WAIT_UNTIL([test 11 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) ++ ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets ++cat 2.expected | cut -c -48 > expout ++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) ++# Skipping the IPv4 checksum. ++cat 2.expected | cut -c 53- > expout ++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) ++ ++# Now add the dhcp option T1 to the dhcp options. ++ovn-nbctl set dhcp_options ${d1} options:T1=4000 ++ ++reset_pcap_file hv1-vif1 hv1/vif1 ++reset_pcap_file hv1-vif2 hv1/vif2 ++rm -f 1.expected ++rm -f 2.expected ++ ++# Send DHCPREQUEST to make sure that T1 is in the reply dhcp options. ++offer_ip=`ip_to_hex 10 0 0 6` ++server_ip=`ip_to_hex 10 0 0 1` ++ciaddr=$offer_ip ++request_ip=0 ++src_ip=$offer_ip ++dst_ip=$server_ip ++# In the expected_dhcp_opts we should not see 330400000e10 which is ++# dhcp lease time option. ++expected_dhcp_opts=3a0400000fa0330400000e100104ffffff0003040a00000136040a000001 ++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts ++ ++# NXT_RESUMEs should be 12. ++OVS_WAIT_UNTIL([test 12 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) ++ ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets ++cat 2.expected | cut -c -48 > expout ++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) ++# Skipping the IPv4 checksum. ++cat 2.expected | cut -c 53- > expout ++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) ++ ++reset_pcap_file hv1-vif1 hv1/vif1 ++reset_pcap_file hv1-vif2 hv1/vif2 ++rm -f 1.expected ++rm -f 2.expected ++ ++# Now send DHCPINFORM again. ++offer_ip=`ip_to_hex 10 0 0 6` ++server_ip=`ip_to_hex 10 0 0 1` ++ciaddr=00000000 ++request_ip=0 ++src_ip=$offer_ip ++dst_ip=$server_ip ++# In the expected_dhcp_opts we should not see 330400000e10 which is ++# dhcp lease time option and 0104ffffff00 which is subnet mask option. ++expected_dhcp_opts=03040a00000136040a000001 ++test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts ++ ++# NXT_RESUMEs should be 13. ++OVS_WAIT_UNTIL([test 13 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)]) ++ ++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets ++cat 2.expected | cut -c -48 > expout ++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) ++# Skipping the IPv4 checksum. ++cat 2.expected | cut -c 53- > expout ++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) ++ + OVN_CLEANUP([hv1]) + + AT_CLEANUP +-- +2.26.2 + diff --git a/SOURCES/0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch b/SOURCES/0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch new file mode 100644 index 0000000..96479f2 --- /dev/null +++ b/SOURCES/0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch @@ -0,0 +1,65 @@ +From b4ddab6aa4d08a04eb7b55337b3fbdcc30b916a6 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Lorenzo Bianconi +Date: Thu, 13 Feb 2020 17:49:37 +0100 +Subject: [PATCH 1/2] pinctrl: fix IP buffering with connection-tracking + +Whenever we need to reinject an IP packet buffered during L2 address +resolution we need to preserve ovs ofport in order to let ovs +connection tracking to properly SNAT/DNAT the packet. +Do not overwrite the MFF_IN_PORT in consider_port_binding routine + +Suggested-by: Numan Siddique +Signed-off-by: Lorenzo Bianconi +Acked-by: Dumitru Ceara +Signed-off-by: Numan Siddique +--- + ovn/controller/physical.c | 1 - + ovn/controller/pinctrl.c | 5 ++++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c +index 8f8a5ba27..95cbaba49 100644 +--- a/ovn/controller/physical.c ++++ b/ovn/controller/physical.c +@@ -893,7 +893,6 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, + for (int i = 0; i < MFF_N_LOG_REGS; i++) { + put_load(0, MFF_LOG_REG0 + i, 0, 32, ofpacts_p); + } +- put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p); + put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); + clone = ofpbuf_at_assert(ofpacts_p, clone_ofs, sizeof *clone); + ofpacts_p->header = clone; +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 57e450c26..01a1bcbe0 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -548,6 +548,7 @@ set_actions_and_enqueue_msg(struct rconn *swconn, + + struct buffer_info { + struct ofpbuf ofpacts; ++ ofp_port_t ofp_port; + struct dp_packet *p; + }; + +@@ -618,6 +619,8 @@ buffered_push_packet(struct buffered_packets *bp, + ofpbuf_init(&bi->ofpacts, 4096); + + reload_metadata(&bi->ofpacts, md); ++ bi->ofp_port = md->flow.in_port.ofp_port; ++ + struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&bi->ofpacts); + resubmit->in_port = OFPP_CONTROLLER; + resubmit->table_id = OFTABLE_REMOTE_OUTPUT; +@@ -652,7 +655,7 @@ buffered_send_packets(struct rconn *swconn, struct buffered_packets *bp, + .ofpacts = bi->ofpacts.data, + .ofpacts_len = bi->ofpacts.size, + }; +- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); ++ match_set_in_port(&po.flow_metadata, bi->ofp_port); + queue_msg(swconn, ofputil_encode_packet_out(&po, proto)); + + ofpbuf_uninit(&bi->ofpacts); +-- +2.24.1 + diff --git a/SOURCES/0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch b/SOURCES/0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch new file mode 100644 index 0000000..53fde8d --- /dev/null +++ b/SOURCES/0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch @@ -0,0 +1,58 @@ +From 600a018ea1136e184b421c86da170b35d05e949f Mon Sep 17 00:00:00 2001 +Message-Id: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com> +From: Han Zhou +Date: Wed, 30 Oct 2019 12:38:34 -0700 +Subject: [PATCH 1/3] pinctrl.c: Fix maybe-uninitialized warnings with old GCC + versions. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There are warnings with older GCC versions (e.g. 4.9.2): +=== +../controller/pinctrl.c: In function ‘ipv6_ra_send’: +../controller/pinctrl.c:2393:13: error: ‘r1’ may be used uninitialized in this function [-Werror=maybe-uninitialized] + memcpy(&dnssl[i], t1, strlen(t1)); + ^ +../controller/pinctrl.c:2383:20: note: ‘r1’ was declared here + char *t1, *r1; + ^ +=== +This is not a real problem but compile fails because of it. This patch +fixes it by initializing the pointers to NULL, to avoid the warnings +with older GCC. + +CC: Lorenzo Bianconi +Fixes: 5a12a940f63a ("Add DNSSL support to OVN") +Acked-by: Numan Siddique +Signed-off-by: Han Zhou +Signed-off-by: Lorenzo Bianconi +--- + ovn/controller/pinctrl.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index ae1b8e1ca..04ccb25a8 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2422,7 +2422,7 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, + size_t prev_l4_size = dp_packet_l4_size(b); + size_t size = sizeof(struct ovs_nd_dnssl); + struct ip6_hdr *nh = dp_packet_l3(b); +- char *t0, *r0, dnssl[255] = {}; ++ char *t0, *r0 = NULL, dnssl[255] = {}; + int i = 0; + + /* Multiple DNS Search List must be 'comma' separated +@@ -2433,7 +2433,7 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, + */ + for (t0 = strtok_r(dnssl_list, ",", &r0); t0; + t0 = strtok_r(NULL, ",", &r0)) { +- char *t1, *r1; ++ char *t1, *r1 = NULL; + + size += strlen(t0) + 2; + if (size > sizeof(dnssl)) { +-- +2.21.1 + diff --git a/SOURCES/0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch b/SOURCES/0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch new file mode 100644 index 0000000..da199ca --- /dev/null +++ b/SOURCES/0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch @@ -0,0 +1,294 @@ +From 5dcb7ee1b36aa4a906f09a95479586f917d893c8 Mon Sep 17 00:00:00 2001 +Message-Id: <5dcb7ee1b36aa4a906f09a95479586f917d893c8.1575382494.git.lorenzo.bianconi@redhat.com> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Thu, 28 Nov 2019 14:48:36 +0200 +Subject: [PATCH 2/2] Add support for Route Info Option in RA - RFC 4191 + +Introduce support for Route Info Option sent in Router +Advertisement according to RFC 4191. Route Info Option are +configured providing route_info in ipv6_ra_configs column of +Logical_Router_Port table. route_info is a comma separated string +where each field provides PRF and prefix for a given route +(e.g: HIGH-aef1::11/48,LOW-aef2::11/96) + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/pinctrl.c | 80 ++++++++++++++++++++++++++++++++++++++++ + ovn/lib/ovn-l7.h | 12 ++++++ + ovn/northd/ovn-northd.c | 6 +++ + ovn/ovn-nb.xml | 14 +++++++ + tests/ovn.at | 32 ++++++++++------ + 5 files changed, 133 insertions(+), 11 deletions(-) + +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2199,6 +2199,7 @@ struct ipv6_ra_config { + struct in6_addr rdnss; + bool has_rdnss; + struct ds dnssl; ++ struct ds route_info; + }; + + struct ipv6_ra_state { +@@ -2221,6 +2222,7 @@ ipv6_ra_config_delete(struct ipv6_ra_con + if (config) { + destroy_lport_addresses(&config->prefixes); + ds_destroy(&config->dnssl); ++ ds_destroy(&config->route_info); + free(config); + } + } +@@ -2260,6 +2262,7 @@ ipv6_ra_update_config(const struct sbrec + config->mtu = smap_get_int(&pb->options, "ipv6_ra_mtu", ND_MTU_DEFAULT); + config->la_flags = IPV6_ND_RA_OPT_PREFIX_ON_LINK; + ds_init(&config->dnssl); ++ ds_init(&config->route_info); + + const char *address_mode = smap_get(&pb->options, "ipv6_ra_address_mode"); + if (!address_mode) { +@@ -2317,6 +2320,11 @@ ipv6_ra_update_config(const struct sbrec + ds_put_buffer(&config->dnssl, dnssl, strlen(dnssl)); + } + ++ const char *route_info = smap_get(&pb->options, "ipv6_ra_route_info"); ++ if (route_info) { ++ ds_put_buffer(&config->route_info, route_info, strlen(route_info)); ++ } ++ + return config; + + fail: +@@ -2424,6 +2432,74 @@ packet_put_ra_dnssl_opt(struct dp_packet + prev_l4_size + size)); + } + ++static void ++packet_put_ra_route_info_opt(struct dp_packet *b, ovs_be32 lifetime, ++ char *route_list) ++{ ++ size_t prev_l4_size = dp_packet_l4_size(b); ++ struct ip6_hdr *nh = dp_packet_l3(b); ++ char *t0, *r0 = NULL; ++ size_t size = 0; ++ ++ for (t0 = strtok_r(route_list, ",", &r0); t0; ++ t0 = strtok_r(NULL, ",", &r0)) { ++ struct ovs_nd_route_info nd_rinfo; ++ char *t1, *r1 = NULL; ++ int index; ++ ++ for (t1 = strtok_r(t0, "-", &r1), index = 0; t1; ++ t1 = strtok_r(NULL, "-", &r1), index++) { ++ ++ nd_rinfo.type = ND_OPT_ROUTE_INFO; ++ nd_rinfo.route_lifetime = lifetime; ++ ++ switch (index) { ++ case 0: ++ if (!strcmp(t1, "HIGH")) { ++ nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_HIGH; ++ } else if (!strcmp(t1, "LOW")) { ++ nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_LOW; ++ } else { ++ nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_NORMAL; ++ } ++ break; ++ case 1: { ++ struct lport_addresses route; ++ uint8_t plen; ++ ++ if (!extract_ip_addresses(t1, &route)) { ++ return; ++ } ++ if (!route.n_ipv6_addrs) { ++ destroy_lport_addresses(&route); ++ return; ++ } ++ ++ nd_rinfo.prefix_len = route.ipv6_addrs->plen; ++ plen = DIV_ROUND_UP(nd_rinfo.prefix_len, 64); ++ nd_rinfo.len = 1 + plen; ++ dp_packet_put(b, &nd_rinfo, sizeof(struct ovs_nd_route_info)); ++ dp_packet_put(b, &route.ipv6_addrs->network, plen * 8); ++ size += sizeof(struct ovs_nd_route_info) + plen * 8; ++ ++ destroy_lport_addresses(&route); ++ index = 0; ++ break; ++ } ++ default: ++ return; ++ } ++ } ++ } ++ ++ nh->ip6_plen = htons(prev_l4_size + size); ++ struct ovs_ra_msg *ra = dp_packet_l4(b); ++ ra->icmph.icmp6_cksum = 0; ++ uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); ++ ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra, ++ prev_l4_size + size)); ++} ++ + /* Called with in the pinctrl_handler thread context. */ + static long long int + ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra) +@@ -2463,6 +2539,10 @@ ipv6_ra_send(struct rconn *swconn, struc + packet_put_ra_dnssl_opt(&packet, htonl(0xffffffff), + ra->config->dnssl.string); + } ++ if (ra->config->route_info.length) { ++ packet_put_ra_route_info_opt(&packet, htonl(0xffffffff), ++ ra->config->route_info.string); ++ } + + uint64_t ofpacts_stub[4096 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); +--- a/ovn/lib/ovn-l7.h ++++ b/ovn/lib/ovn-l7.h +@@ -242,6 +242,18 @@ struct ovs_nd_dnssl { + }; + BUILD_ASSERT_DECL(ND_DNSSL_OPT_LEN == sizeof(struct ovs_nd_dnssl)); + ++/* Route Information option RFC 4191 */ ++#define ND_OPT_ROUTE_INFO 24 ++#define ND_ROUTE_INFO_OPT_LEN 8 ++struct ovs_nd_route_info { ++ u_int8_t type; /* ND_OPT_ROUTE_INFO */ ++ u_int8_t len; /* 1, 2 or 3 */ ++ u_int8_t prefix_len; ++ u_int8_t flags; ++ ovs_be32 route_lifetime; ++}; ++BUILD_ASSERT_DECL(ND_ROUTE_INFO_OPT_LEN == sizeof(struct ovs_nd_route_info)); ++ + #define DHCPV6_DUID_LL 3 + #define DHCPV6_HW_TYPE_ETH 1 + +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6763,6 +6763,12 @@ copy_ra_to_sb(struct ovn_port *op, const + smap_add(&options, "ipv6_ra_prf", prf); + } + ++ const char *route_info = smap_get(&op->nbrp->ipv6_ra_configs, ++ "route_info"); ++ if (route_info) { ++ smap_add(&options, "ipv6_ra_route_info", route_info); ++ } ++ + sbrec_port_binding_set_options(op->sb, &options); + smap_destroy(&options); + } +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -1871,6 +1871,20 @@ +
          + + ++ ++ Route Info is used to configure Route Info Option sent in Router ++ Advertisment according to RFC 4191. Route Info is a comma ++ separated string where each field provides PRF and prefix for a ++ given route (e.g: HIGH-aef1::11/48,LOW-aef2::11/96) ++ Possible PRF values are: ++ ++
            ++
          • HIGH: mapped to 0x01 in RA PRF field
          • ++
          • MEDIUM: mapped to 0x00 in RA PRF field
          • ++
          • LOW: mapped to 0x11 in RA PRF field
          • ++
          ++
          ++ + + The recommended MTU for the link. Default is 0, which means no MTU + Option will be included in RA packet replied by ovn-controller. +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -11417,14 +11417,15 @@ construct_expected_ra() { + local ra_mo=$2 + local rdnss=$3 + local dnssl=$4 +- local ra_prefix_la=$5 ++ local route_info=$5 ++ local ra_prefix_la=$6 + + local slla=0101${src_mac} + local mtu_opt="" + if test $mtu != 0; then + mtu_opt=05010000${mtu} + fi +- shift 5 ++ shift 6 + + local prefix="" + while [[ $# -gt 0 ]] ; do +@@ -11442,8 +11443,12 @@ construct_expected_ra() { + if test $dnssl != 0; then + dnssl_opt=1f030000ffffffff${dnssl} + fi ++ local route_info_opt="" ++ if test $route_info != 0; then ++ route_info_opt=${route_info} ++ fi + +- local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}${dnssl_opt} ++ local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}${dnssl_opt}${route_info_opt} + local icmp=8600XXXX${ra} + + local ip_len=$(expr ${#icmp} / 2) +@@ -11478,38 +11483,43 @@ ra_test() { + } + + # Baseline test with no MTU +-ra_test 0 00 0 0 c0 40 aef00000000000000000000000000000 ++ra_test 0 00 0 0 0 c0 40 aef00000000000000000000000000000 + + # Now make sure an MTU option makes it + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500 +-ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 ++ra_test 000005dc 00 0 0 0 c0 40 aef00000000000000000000000000000 + + # Now test for multiple network prefixes + ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48' +-ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 00 0 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # Test PRF for default gw + ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="LOW" +-ra_test 000005dc 18 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 18 0 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # Now test for RDNSS + ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11' + dns_addr=aef00000000000000000000000000011 +-ra_test 000005dc 18 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 18 $dns_addr 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # Now test for DNSSL + ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:dnssl="aa.bb.cc" + ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="HIGH" + dnssl=02616102626202636300000000000000 +-ra_test 000005dc 08 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 08 $dns_addr $dnssl 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ ++# Now test Route Info option ++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:route_info="HIGH-aef1::11/48,LOW-aef2::11/96" ++route_info=18023008ffffffffaef100000000000018036018ffffffffaef20000000000000000000000000000 ++ra_test 000005dc 08 $dns_addr $dnssl $route_info c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + ## Test a different address mode now + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful +-ra_test 000005dc 88 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 88 $dns_addr $dnssl $route_info 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + # And the other address mode + ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless +-ra_test 000005dc 48 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 ++ra_test 000005dc 48 $dns_addr $dnssl $route_info c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000 + + OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP diff --git a/SOURCES/0002-Combine-conjunctions-with-identical-matches-into-one.patch b/SOURCES/0002-Combine-conjunctions-with-identical-matches-into-one.patch new file mode 100644 index 0000000..37c06df --- /dev/null +++ b/SOURCES/0002-Combine-conjunctions-with-identical-matches-into-one.patch @@ -0,0 +1,213 @@ +From b52c176dd76bc0321d9c8bfe1c9b4832daf64835 Mon Sep 17 00:00:00 2001 +From: Mark Michelson +Date: Fri, 25 Oct 2019 13:25:12 -0400 +Subject: [PATCH 2/2] Combine conjunctions with identical matches into one + flow. + +As stated in previous commits, conjunctive matches have an issue where +it is possible to install multiple flows that have identical matches. +This results in ambiguity, and can lead to features (such as ACLs) not +functioning properly. + +This change fixes the problem by combining conjunctions with identical +matches into a single flow. As an example, in the past we may have had +something like: + +nw_dst=10.0.0.1 actions=conjunction(2,1/2) +nw_dst=10.0.0.1 actions=conjunction(3,1/2) + +This commit changes this into + +nw_dst=10.0.0.1 actions=conjunction(2,1/2),conjunction(3,1/2) + +This way, there is only a single flow with the proscribed match, and +there is no ambiguity. + +Signed-off-by: Mark Michelson +--- + ovn/controller/lflow.c | 5 ++-- + ovn/controller/ofctrl.c | 74 +++++++++++++++++++++++++++++++++++++++++++++-------- + ovn/controller/ofctrl.h | 6 +++++ + tests/ovn.at | 17 +++++------- + 4 files changed, 80 insertions(+), 22 deletions(-) + +diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c +index 24e3a521e..f34abcebc 100644 +--- a/ovn/controller/lflow.c ++++ b/ovn/controller/lflow.c +@@ -734,8 +734,9 @@ consider_logical_flow( + dst->clause = src->clause; + dst->n_clauses = src->n_clauses; + } +- ofctrl_add_flow(flow_table, ptable, lflow->priority, 0, &m->match, +- &conj, &lflow->header_.uuid); ++ ++ ofctrl_add_or_append_flow(flow_table, ptable, lflow->priority, 0, ++ &m->match, &conj, &lflow->header_.uuid); + ofpbuf_uninit(&conj); + } + } +diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c +index 3131baff0..10edd84fb 100644 +--- a/ovn/controller/ofctrl.c ++++ b/ovn/controller/ofctrl.c +@@ -69,6 +69,11 @@ struct ovn_flow { + uint64_t cookie; + }; + ++static struct ovn_flow *ovn_flow_alloc(uint8_t table_id, uint16_t priority, ++ uint64_t cookie, ++ const struct match *match, ++ const struct ofpbuf *actions, ++ const struct uuid *sb_uuid); + static uint32_t ovn_flow_match_hash(const struct ovn_flow *); + static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table, + const struct ovn_flow *target, +@@ -657,16 +662,8 @@ ofctrl_check_and_add_flow(struct ovn_desired_flow_table *flow_table, + const struct uuid *sb_uuid, + bool log_duplicate_flow) + { +- struct ovn_flow *f = xmalloc(sizeof *f); +- f->table_id = table_id; +- f->priority = priority; +- minimatch_init(&f->match, match); +- f->ofpacts = xmemdup(actions->data, actions->size); +- f->ofpacts_len = actions->size; +- f->sb_uuid = *sb_uuid; +- f->match_hmap_node.hash = ovn_flow_match_hash(f); +- f->uuid_hindex_node.hash = uuid_hash(&f->sb_uuid); +- f->cookie = cookie; ++ struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match, ++ actions, sb_uuid); + + ovn_flow_log(f, "ofctrl_add_flow"); + +@@ -721,9 +718,66 @@ ofctrl_add_flow(struct ovn_desired_flow_table *desired_flows, + ofctrl_check_and_add_flow(desired_flows, table_id, priority, cookie, + match, actions, sb_uuid, true); + } ++ ++void ++ofctrl_add_or_append_flow(struct ovn_desired_flow_table *desired_flows, ++ uint8_t table_id, uint16_t priority, uint64_t cookie, ++ const struct match *match, ++ const struct ofpbuf *actions, ++ const struct uuid *sb_uuid) ++{ ++ struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match, ++ actions, sb_uuid); ++ ++ ovn_flow_log(f, "ofctrl_add_or_append_flow"); ++ ++ struct ovn_flow *existing; ++ existing = ovn_flow_lookup(&desired_flows->match_flow_table, f, false); ++ if (existing) { ++ /* There's already a flow with this particular match. Append the ++ * action to that flow rather than adding a new flow ++ */ ++ uint64_t compound_stub[64 / 8]; ++ struct ofpbuf compound; ++ ofpbuf_use_stub(&compound, compound_stub, sizeof(compound_stub)); ++ ofpbuf_put(&compound, existing->ofpacts, existing->ofpacts_len); ++ ofpbuf_put(&compound, f->ofpacts, f->ofpacts_len); ++ ++ free(existing->ofpacts); ++ existing->ofpacts = xmemdup(compound.data, compound.size); ++ existing->ofpacts_len = compound.size; ++ ++ ofpbuf_uninit(&compound); ++ ovn_flow_destroy(f); ++ } else { ++ hmap_insert(&desired_flows->match_flow_table, &f->match_hmap_node, ++ f->match_hmap_node.hash); ++ hindex_insert(&desired_flows->uuid_flow_table, &f->uuid_hindex_node, ++ f->uuid_hindex_node.hash); ++ } ++} + + /* ovn_flow. */ + ++static struct ovn_flow * ++ovn_flow_alloc(uint8_t table_id, uint16_t priority, uint64_t cookie, ++ const struct match *match, const struct ofpbuf *actions, ++ const struct uuid *sb_uuid) ++{ ++ struct ovn_flow *f = xmalloc(sizeof *f); ++ f->table_id = table_id; ++ f->priority = priority; ++ minimatch_init(&f->match, match); ++ f->ofpacts = xmemdup(actions->data, actions->size); ++ f->ofpacts_len = actions->size; ++ f->sb_uuid = *sb_uuid; ++ f->match_hmap_node.hash = ovn_flow_match_hash(f); ++ f->uuid_hindex_node.hash = uuid_hash(&f->sb_uuid); ++ f->cookie = cookie; ++ ++ return f; ++} ++ + /* Returns a hash of the match key in 'f'. */ + static uint32_t + ovn_flow_match_hash(const struct ovn_flow *f) +diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h +index 1e9ac16b9..21d2ce648 100644 +--- a/ovn/controller/ofctrl.h ++++ b/ovn/controller/ofctrl.h +@@ -70,6 +70,12 @@ void ofctrl_add_flow(struct ovn_desired_flow_table *, uint8_t table_id, + const struct match *, const struct ofpbuf *ofpacts, + const struct uuid *); + ++void ofctrl_add_or_append_flow(struct ovn_desired_flow_table *desired_flows, ++ uint8_t table_id, uint16_t priority, ++ uint64_t cookie, const struct match *match, ++ const struct ofpbuf *actions, ++ const struct uuid *sb_uuid); ++ + void ofctrl_remove_flows(struct ovn_desired_flow_table *, const struct uuid *); + + void ovn_desired_flow_table_init(struct ovn_desired_flow_table *); +diff --git a/tests/ovn.at b/tests/ovn.at +index 641a646fc..50d8efeec 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -12247,7 +12247,7 @@ ovn-nbctl create Address_Set name=set1 \ + addresses=\"10.0.0.4\",\"10.0.0.5\",\"10.0.0.6\" + ovn-nbctl create Address_Set name=set2 \ + addresses=\"10.0.0.7\",\"10.0.0.8\",\"10.0.0.9\" +-ovn-nbctl acl-add ls1 to-lport 1002 \ ++ovn-nbctl acl-add ls1 to-lport 1001 \ + 'ip4 && ip4.src == $set1 && ip4.dst == $set1' allow + ovn-nbctl acl-add ls1 to-lport 1001 \ + 'ip4 && ip4.src == $set1 && ip4.dst == $set2' drop +@@ -12296,7 +12296,7 @@ cat 2.expected > expout + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets + AT_CHECK([cat 2.packets], [0], [expout]) + +-# There should be total of 12 flows present with conjunction action and 2 flows ++# There should be total of 9 flows present with conjunction action and 2 flows + # with conj match. Eg. + # table=44, priority=2002,conj_id=2,metadata=0x1 actions=resubmit(,45) + # table=44, priority=2001,conj_id=3,metadata=0x1 actions=drop +@@ -12306,14 +12306,11 @@ AT_CHECK([cat 2.packets], [0], [expout]) + # priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 actions=conjunction(3,2/2) + # priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 actions=conjunction(3,2/2) + # priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 actions=conjunction(3,2/2) +-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(2,1/2) +-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(2,1/2) +-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(2,1/2) +-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(3,1/2) +-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(3,1/2) +-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(3,1/2) +- +-OVS_WAIT_UNTIL([test 12 = `as hv1 ovs-ofctl dump-flows br-int | \ ++# priority=2002,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(2,1/2),conjunction(3,1/2) ++# priority=2002,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) ++# priority=2002,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(2,1/2),conjunction(3,1/2) ++ ++OVS_WAIT_UNTIL([test 9 = `as hv1 ovs-ofctl dump-flows br-int | \ + grep conjunction | wc -l`]) + OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \ + grep conj_id | wc -l`]) +-- +2.14.5 + diff --git a/SOURCES/0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch b/SOURCES/0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch new file mode 100644 index 0000000..c292a9a --- /dev/null +++ b/SOURCES/0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch @@ -0,0 +1,85 @@ +From b59b64c0be5f16791c77d1b0104f09fd424ac098 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com> +References: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 7 Jan 2020 17:50:05 +0100 +Subject: [PATCH 2/3] DNSSL: copy dnssl string in order to avoid truncated + value + +ipv6_ra_send can run 2 times in a row before prepare_ipv6_ras updates +the dnss list. Copy the dnss dynamic string in packet_put_ra_dnssl_opt +in order to avoid sending truncated dnssl list on the wire. +Moreover move ip6_hdr definition just before accessing it because the +packet can be reallocated if the data area is not big enough for packet +content + +Fixes: 5a12a940f ("Add DNSSL support to OVN") +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/pinctrl.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 04ccb25a8..43b4e9299 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2417,14 +2417,15 @@ packet_put_ra_rdnss_opt(struct dp_packet *b, uint8_t num, + + static void + packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, +- char *dnssl_list) ++ char *dnssl_data) + { ++ char *dnssl_list, *t0, *r0 = NULL, dnssl[255] = {}; + size_t prev_l4_size = dp_packet_l4_size(b); + size_t size = sizeof(struct ovs_nd_dnssl); +- struct ip6_hdr *nh = dp_packet_l3(b); +- char *t0, *r0 = NULL, dnssl[255] = {}; + int i = 0; + ++ dnssl_list = xstrdup(dnssl_data); ++ + /* Multiple DNS Search List must be 'comma' separated + * (e.g. "a.b.c, d.e.f"). Domain names must be encoded + * as described in Section 3.1 of RFC1035. +@@ -2437,7 +2438,7 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, + + size += strlen(t0) + 2; + if (size > sizeof(dnssl)) { +- return; ++ goto out; + } + + for (t1 = strtok_r(t0, ".", &r1); t1; +@@ -2449,6 +2450,8 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, + dnssl[i++] = 0; + } + size = ROUND_UP(size, 8); ++ ++ struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + size); + + struct ovs_nd_dnssl *nd_dnssl = dp_packet_put_uninit(b, sizeof *nd_dnssl); +@@ -2464,6 +2467,8 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime, + uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra, + prev_l4_size + size)); ++out: ++ free(dnssl_list); + } + + static void +@@ -2571,7 +2576,7 @@ ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra) + } + if (ra->config->dnssl.length) { + packet_put_ra_dnssl_opt(&packet, htonl(0xffffffff), +- ra->config->dnssl.string); ++ ds_cstr(&ra->config->dnssl)); + } + if (ra->config->route_info.length) { + packet_put_ra_route_info_opt(&packet, htonl(0xffffffff), +-- +2.21.1 + diff --git a/SOURCES/0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch b/SOURCES/0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch new file mode 100644 index 0000000..0cbb3d3 --- /dev/null +++ b/SOURCES/0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch @@ -0,0 +1,188 @@ +From 13895b0543721db71569134d101d472e482c3997 Mon Sep 17 00:00:00 2001 +Message-Id: <13895b0543721db71569134d101d472e482c3997.1583175283.git.lorenzo.bianconi@redhat.com> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Mon, 2 Mar 2020 10:37:49 +0100 +Subject: [PATCH 2/2] Manage ARP process locally in a DVR scenario + +OVN currently performs L2 address resolution and IP buffering on the +gw node. If the system relies on FIPs, OVN will re-inject the buffered +IP packets on the gw node, while following packets will go though +the localnet port on the compute node resulting in a ToR switch +misconfiguration. This patch addresses the issue managing ARP +and IP buffering locally if FIPs are configured on the node + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 37 +++++++++++++++++++++++-- + ovn/northd/ovn-northd.c | 55 ++++++++++++++++++++++++++++++++++++- + tests/ovn.at | 14 ++++++++++ + 3 files changed, 103 insertions(+), 3 deletions(-) + +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -2290,11 +2290,44 @@ output; + +
        • +

          ++ For distributed logical routers where one of the logical router ports ++ specifies a redirect-chassis, a priority-400 logical ++ flow for each dnat_and_snat NAT rules configured. ++ These flows will allow to properly forward traffic to the external ++ connections if available and avoid sending it through the tunnel. ++ Assuming the following NAT rule has been configured: ++

          ++ ++
          ++external_ip = A;
          ++external_mac = B;
          ++logical_ip = C;
          ++        
          ++ ++

          ++ the following action will be applied: ++

          ++ ++
          ++ip.ttl--;
          ++reg0 = ip.dst;
          ++reg1 = A;
          ++eth.src = B;
          ++outport = router-port;
          ++next;
          ++        
          ++ ++
        • ++ ++
        • ++

          + IPv4 routing table. For each route to IPv4 network N with + netmask M, on router port P with IP address + A and Ethernet + address E, a logical flow with match ip4.dst == +- N/M, whose priority is the number of ++ N/M, whose priority is 400 ++ + the number of 1-bits in M if the router port is not a ++ distributed gateway port, else the priority is the number of + 1-bits in M, has the following actions: +

          + +@@ -2621,7 +2654,7 @@ icmp4 { + +
        • + For each NAT rule in the OVN Northbound database that can +- be handled in a distributed manner, a priority-100 logical ++ be handled in a distributed manner, a priority-200 logical + flow with match ip4.src == B && + outport == GW, where GW is + the logical router distributed gateway port, with actions +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6598,6 +6598,43 @@ build_routing_policy_flow(struct hmap *l + ds_destroy(&actions); + } + ++/* default logical flow prioriry for distributed routes */ ++#define DROUTE_PRIO 400 ++ ++static void ++add_distributed_routes(struct hmap *lflows, struct ovn_datapath *od) ++{ ++ struct ds actions = DS_EMPTY_INITIALIZER; ++ struct ds match = DS_EMPTY_INITIALIZER; ++ ++ for (size_t i = 0; i < od->nbr->n_nat; i++) { ++ const struct nbrec_nat *nat = od->nbr->nat[i]; ++ ++ if (strcmp(nat->type, "dnat_and_snat") || ++ !nat->external_mac) { ++ continue; ++ } ++ ++ bool is_ipv4 = strchr(nat->logical_ip, '.') ? true : false; ++ ds_put_format(&match, "ip%s.src == %s && is_chassis_resident(\"%s\")", ++ is_ipv4 ? "4" : "6", nat->logical_ip, ++ nat->logical_port); ++ char *prefix = is_ipv4 ? "" : "xx"; ++ ds_put_format(&actions, "outport = %s; eth.src = %s; " ++ "%sreg0 = ip%s.dst; %sreg1 = %s; next;", ++ od->l3dgw_port->json_key, nat->external_mac, ++ prefix, is_ipv4 ? "4" : "6", ++ prefix, nat->external_ip); ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, DROUTE_PRIO, ++ ds_cstr(&match), ds_cstr(&actions)); ++ ds_clear(&match); ++ ds_clear(&actions); ++ } ++ ++ ds_destroy(&actions); ++ ds_destroy(&match); ++} ++ + static void + add_route(struct hmap *lflows, const struct ovn_port *op, + const char *lrp_addr_s, const char *network_s, int plen, +@@ -6616,6 +6653,13 @@ add_route(struct hmap *lflows, const str + priority = (plen * 2) + 1; + } + ++ /* traffic for internal IPs of logical switch ports must be sent to ++ * the gw controller through the overlay tunnels ++ */ ++ if (op->nbrp && !op->nbrp->n_gateway_chassis) { ++ priority += DROUTE_PRIO; ++ } ++ + /* IPv6 link-local addresses must be scoped to the local router port. */ + if (!is_ipv4) { + struct in6_addr network; +@@ -8256,7 +8300,7 @@ build_lrouter_flows(struct hmap *datapat + is_v6 ? "6" : "4", + nat->logical_ip, + od->l3dgw_port->json_key); +- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100, ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200, + ds_cstr(&match), "next;"); + } + +@@ -8532,6 +8576,15 @@ build_lrouter_flows(struct hmap *datapat + ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1", "next;"); + } + ++ /* Logical router ingress table IP_ROUTING - IP routing for distributed ++ * logical router ++ */ ++ HMAP_FOR_EACH (od, key_node, datapaths) { ++ if (od->nbr && od->l3dgw_port) { ++ add_distributed_routes(lflows, od); ++ } ++ } ++ + /* Logical router ingress table 7: IP Routing. + * + * A packet that arrives at this table is an IP packet that should be +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -9440,6 +9440,20 @@ AT_CHECK([as hv3 ovs-vsctl set Open_vSwi + OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-vsctl show | \ + grep "Port patch-br-int-to-ln_port" | wc -l`]) + ++AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \ ++grep "ip4.src == 10.0.0.3 && is_chassis_resident(\"foo1\")" -c`]) ++AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \ ++grep "ip4.src == 10.0.0.4 && is_chassis_resident(\"foo2\")" -c`]) ++ ++key=`ovn-sbctl --bare --columns tunnel_key list datapath_Binding lr0` ++# Check that the OVS flows appear for the dnat_and_snat entries in ++# lr_in_ip_routing table. ++OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \ ++grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.3" -c`]) ++ ++OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \ ++grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.4" -c`]) ++ + # Re-add nat-addresses option + ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" + diff --git a/SOURCES/0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch b/SOURCES/0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch new file mode 100644 index 0000000..5e602cd --- /dev/null +++ b/SOURCES/0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch @@ -0,0 +1,291 @@ +From c0bdec23cc9967abdac8eeaa3b8d7cddafedacdb Mon Sep 17 00:00:00 2001 +From: Ankur Sharma +Date: Wed, 28 Aug 2019 01:55:28 +0000 +Subject: [PATCH 02/12] OVN: Vlan backed DVR N-S, redirect-type option + +Background: +With 795d7f24ce0e ("OVN: Enable E-W Traffic, Vlan backed DVR"), we have added +support for E-W workflow for vlan backed DVRs. + +This series enables N-S workflow for vlan backed DVRs. + +Key difference between E-W and N-S traffic flow is that +N-S flow requires a gateway chassis. A gateway chassis +will be responsible for following: +a. Doing Network Address Translation (NAT). +b. Becoming entry and exit point for North->South + and South->North traffic respectively. + +OVN by default always uses overlay encapsulation to redirect +the packet to gateway chassis. This series will enable +the redirection to gateway chassis in the absence of encapsulation. + +This patch: +a. Adds a new key-value in options of a router port. +b. This new config key will be used by ovn-controller + to determine if a redirected packet will go out of + tunnel port or localnet port. +c. key is "redirect-type" and it takes "overlay" and + "bridged" as values. +d. Added ovn-nbctl command to set and get redirect-type + option on a router port. +e. This new configuration is added because bridged or overlay + based forwarding is considered to be a logical switch property, + hence for a router configuration has to be done at the router port + level. + +Change-Id: Ic002afa5ddb1da22d5833df9d584de256ba3f65a +Signed-off-by: Ankur Sharma +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.c | 6 ++++ + ovn/ovn-nb.xml | 43 ++++++++++++++++++++++++++ + ovn/utilities/ovn-nbctl.c | 64 +++++++++++++++++++++++++++++++++++++++ + tests/ovn-nbctl.at | 25 +++++++++++++++ + tests/ovn-northd.at | 30 ++++++++++++++++++ + 5 files changed, 168 insertions(+) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 952d0ae8a..cd13f308e 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -2648,6 +2648,9 @@ ovn_port_update_sbrec(struct northd_context *ctx, + if (op->derived) { + const char *redirect_chassis = smap_get(&op->nbrp->options, + "redirect-chassis"); ++ const char *redirect_type = smap_get(&op->nbrp->options, ++ "redirect-type"); ++ + int n_gw_options_set = 0; + if (op->nbrp->ha_chassis_group) { + n_gw_options_set++; +@@ -2739,6 +2742,9 @@ ovn_port_update_sbrec(struct northd_context *ctx, + sbrec_port_binding_set_gateway_chassis(op->sb, NULL, 0); + } + smap_add(&new, "distributed-port", op->nbrp->name); ++ if (redirect_type) { ++ smap_add(&new, "redirect-type", redirect_type); ++ } + } else { + if (op->peer) { + smap_add(&new, "peer", op->peer->key); +diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml +index 0323a9d33..bce7463d2 100644 +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -2032,6 +2032,49 @@ + to true. +

          + ++ ++ ++

          ++ This options dictates if a packet redirected to ++ gateway chassis will be overlay encapsulated ++ or go as a regular packet via the localnet port. ++

          ++ ++

          ++ Option takes following values ++

          ++ ++
            ++
          • ++ OVERLAY ++
          • ++ ++
          • ++ BRIDGED ++
          • ++
          ++ ++

          ++ OVERLAY option will ensure that redirected packet goes out as ++ encapsulation via the tunnel port. ++

          ++ ++

          ++ BRIDGED option will ensure that redirected packet goes out ++ via the localnet port tagged with vlan (if configured). ++

          ++ ++

          ++ OVERLAY is the default redirection type. ++

          ++ ++

          ++ Option is applicable only to gateway chassis attached logical ++ router ports. ++

          ++ ++
          ++ + + + +diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c +index 497d6f231..112cc1d54 100644 +--- a/ovn/utilities/ovn-nbctl.c ++++ b/ovn/utilities/ovn-nbctl.c +@@ -661,6 +661,14 @@ Logical router port commands:\n\ + ('enabled' or 'disabled')\n\ + lrp-get-enabled PORT get administrative state PORT\n\ + ('enabled' or 'disabled')\n\ ++ lrp-set-redirect-type PORT TYPE\n\ ++ set whether redirected packet to gateway chassis\n\ ++ of PORT will be encapsulated or not\n\ ++ ('overlay' or 'vlan')\n\ ++ lrp-get-redirect-type PORT\n\ ++ get whether redirected packet to gateway chassis\n\ ++ of PORT will be encapsulated or not\n\ ++ ('overlay' or 'vlan')\n\ + \n\ + Route commands:\n\ + [--policy=POLICY] lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\ +@@ -4617,6 +4625,58 @@ nbctl_lrp_get_enabled(struct ctl_context *ctx) + *lrp->enabled ? "enabled" : "disabled"); + } + ++/* Set the logical router port redirect type. */ ++static void ++nbctl_lrp_set_redirect_type(struct ctl_context *ctx) ++{ ++ const char *id = ctx->argv[1]; ++ const char *type = ctx->argv[2]; ++ const struct nbrec_logical_router_port *lrp = NULL; ++ struct smap lrp_options; ++ ++ char *error = lrp_by_name_or_uuid(ctx, id, true, &lrp); ++ if (error) { ++ ctx->error = error; ++ return; ++ } ++ ++ if (strcasecmp(type, "bridged") && strcasecmp(type, "overlay")) { ++ error = xasprintf("Invalid redirect type: %s", type); ++ ctx->error = error; ++ return; ++ } ++ ++ smap_init(&lrp_options); ++ smap_clone(&lrp_options, &lrp->options); ++ ++ if (smap_get(&lrp_options, "redirect-type")) { ++ smap_replace(&lrp_options, "redirect-type", type); ++ } else { ++ smap_add(&lrp_options, "redirect-type", type); ++ } ++ ++ nbrec_logical_router_port_set_options(lrp, &lrp_options); ++ ++ smap_destroy(&lrp_options); ++} ++ ++static void ++nbctl_lrp_get_redirect_type(struct ctl_context *ctx) ++{ ++ const char *id = ctx->argv[1]; ++ const struct nbrec_logical_router_port *lrp = NULL; ++ ++ char *error = lrp_by_name_or_uuid(ctx, id, true, &lrp); ++ if (error) { ++ ctx->error = error; ++ return; ++ } ++ ++ const char *redirect_type = smap_get(&lrp->options, "redirect-type"); ++ ds_put_format(&ctx->output, "%s\n", ++ !redirect_type ? "overlay": redirect_type); ++} ++ + struct ipv4_route { + int priority; + ovs_be32 addr; +@@ -5623,6 +5683,10 @@ static const struct ctl_command_syntax nbctl_commands[] = { + NULL, "", RW }, + { "lrp-get-enabled", 1, 1, "PORT", NULL, nbctl_lrp_get_enabled, + NULL, "", RO }, ++ { "lrp-set-redirect-type", 2, 2, "PORT TYPE", NULL, ++ nbctl_lrp_set_redirect_type, NULL, "", RW }, ++ { "lrp-get-redirect-type", 1, 1, "PORT", NULL, nbctl_lrp_get_redirect_type, ++ NULL, "", RO }, + + /* logical router route commands. */ + { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL, +diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at +index 30849682c..620b778b7 100644 +--- a/tests/ovn-nbctl.at ++++ b/tests/ovn-nbctl.at +@@ -1243,6 +1243,31 @@ lrp0-chassis1 1 + + dnl --------------------------------------------------------------------- + ++OVN_NBCTL_TEST([ovn_nbctl_redirect_type], [logical router port redirect type], [ ++AT_CHECK([ovn-nbctl lr-add lr0]) ++AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24]) ++AT_CHECK([ovn-nbctl lrp-get-redirect-type lrp0], [0], [dnl ++overlay ++]) ++AT_CHECK([ovn-nbctl lrp-set-redirect-type lp0 bridged], [1], [], ++[ovn-nbctl: lp0: port name not found ++]) ++AT_CHECK([ovn-nbctl lrp-set-redirect-type lrp0 bridged], [0], []) ++AT_CHECK([ovn-nbctl lrp-get-redirect-type lrp0], [0], [dnl ++bridged ++]) ++AT_CHECK([ovn-nbctl lrp-set-redirect-type lrp0 overlay], [0], []) ++AT_CHECK([ovn-nbctl lrp-get-redirect-type lrp0], [0], [dnl ++overlay ++]) ++AT_CHECK([ovn-nbctl lrp-set-redirect-type lrp0 abcd], [1], [], ++[ovn-nbctl: Invalid redirect type: abcd ++]) ++ ++]) ++ ++dnl --------------------------------------------------------------------- ++ + OVN_NBCTL_TEST([ovn_nbctl_lrp_enable], [logical router port enable and disable], [ + AT_CHECK([ovn-nbctl lr-add lr0]) + AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24]) +diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at +index 0dea04edc..42033d589 100644 +--- a/tests/ovn-northd.at ++++ b/tests/ovn-northd.at +@@ -936,3 +936,33 @@ OVS_WAIT_UNTIL([ + test 0 = $?]) + + AT_CLEANUP ++ ++AT_SETUP([ovn -- check Redirect Chassis propagation from NB to SB]) ++AT_SKIP_IF([test $HAVE_PYTHON = no]) ++ovn_start ++ ++ovn-sbctl chassis-add gw1 geneve 127.0.0.1 ++ ++ovn-nbctl lr-add R1 ++ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24 ++ ++ovn-nbctl ls-add S1 ++ovn-nbctl lsp-add S1 S1-R1 ++ovn-nbctl lsp-set-type S1-R1 router ++ovn-nbctl lsp-set-addresses S1-R1 router ++ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 ++ ++ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1 ++ ++uuid=`ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=cr-R1-S1` ++echo "CR-LRP UUID is: " $uuid ++ ++ovn-nbctl lrp-set-redirect-type R1-S1 bridged ++OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0], [bridged ++]) ++ ++ovn-nbctl lrp-set-redirect-type R1-S1 overlay ++OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0], [overlay ++]) ++ ++AT_CLEANUP +-- +2.23.0 + diff --git a/SOURCES/0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch b/SOURCES/0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch new file mode 100644 index 0000000..25e0968 --- /dev/null +++ b/SOURCES/0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch @@ -0,0 +1,85 @@ +From c4621fefd5a5a9169125c104e6a81bf987b4c029 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Sun, 5 Jan 2020 00:46:37 +0530 +Subject: [PATCH 2/2] Restrict ARP/IPv6 ND replies for LB VIP only on chassis + redirect port + +Presently when ARP/ND request for the load balance VIP is received +from the provider network, all the ovn-controllers' reply to the ARP/ND +request which have ovn-bridge-mappings configured. + +This patch restricts these ARP/ND replies only on the chassis where the +chassis redirect port of the distributed router port is resident. + +Acked-by: Dumitru Ceara +Signed-off-by: Numan Siddique + +(cherry-picked from upstream commit 6a5783021c35429dbb98ecebbbfbd7cdf3983f73) + +Change-Id: I70b091730a7380ed8be63040ce30aa9e64a684bd +--- + ovn/northd/ovn-northd.8.xml | 14 ++++++++++++++ + ovn/northd/ovn-northd.c | 8 ++++++++ + 2 files changed, 22 insertions(+) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 956a10362..11ef65964 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1670,6 +1670,13 @@ flags.loopback = 1; + output; +
  • + ++

    ++ If the router port P is a distributed gateway router ++ port, then the is_chassis_resident(P) is ++ also added in the match condition for the load balancer IPv4 ++ VIP A. ++

    ++ +

    + IPv6: For a configured DNAT IP address or a load balancer + IPv6 VIP A, solicited node address S, +@@ -1694,6 +1701,13 @@ nd_na { + } + + ++

    ++ If the router port P is a distributed gateway router ++ port, then the is_chassis_resident(P) ++ is also added in the match condition for the load balancer IPv6 ++ VIP A. ++

    ++ +

    + For the gateway port on a distributed logical router with NAT + (where one of the logical router ports specifies a +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 55734b090..655d6240a 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -7495,6 +7495,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + "inport == %s && arp.tpa == %s && arp.op == 1", + op->json_key, ip_address); + ++ if (op == op->od->l3dgw_port) { ++ ds_put_format(&match, " && is_chassis_resident(%s)", ++ op->od->l3redirect_port->json_key); ++ } + ds_clear(&actions); + ds_put_format(&actions, + "eth.dst = eth.src; " +@@ -7522,6 +7526,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + "inport == %s && nd_ns && nd.target == %s", + op->json_key, ip_address); + ++ if (op == op->od->l3dgw_port) { ++ ds_put_format(&match, " && is_chassis_resident(%s)", ++ op->od->l3redirect_port->json_key); ++ } + ds_clear(&actions); + ds_put_format(&actions, + "nd_na { " +-- +2.24.1 + diff --git a/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch b/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch new file mode 100644 index 0000000..2c75235 --- /dev/null +++ b/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch @@ -0,0 +1,181 @@ +From d9ed450713eda62af1bec5009694b2d206c9f435 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Lorenzo Bianconi +Date: Mon, 25 May 2020 23:55:06 +0200 +Subject: [PATCH ovn 1/3] Revert "Manage ARP process locally in a DVR scenario" + +This reverts commit c0bf32d72f8b893bbe3cb64912b0fd259d71555f. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Han Zhou +--- + northd/ovn-northd.8.xml | 37 ++-------------------------- + northd/ovn-northd.c | 53 +---------------------------------------- + tests/ovn.at | 14 ----------- + 3 files changed, 3 insertions(+), 101 deletions(-) + +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -2311,44 +2311,11 @@ output; + +

  • +

    +- For distributed logical routers where one of the logical router ports +- specifies a redirect-chassis, a priority-400 logical +- flow for each dnat_and_snat NAT rules configured. +- These flows will allow to properly forward traffic to the external +- connections if available and avoid sending it through the tunnel. +- Assuming the following NAT rule has been configured: +-

    +- +-
    +-external_ip = A;
    +-external_mac = B;
    +-logical_ip = C;
    +-        
    +- +-

    +- the following action will be applied: +-

    +- +-
    +-ip.ttl--;
    +-reg0 = ip.dst;
    +-reg1 = A;
    +-eth.src = B;
    +-outport = router-port;
    +-next;
    +-        
    +- +-
  • +- +-
  • +-

    + IPv4 routing table. For each route to IPv4 network N with + netmask M, on router port P with IP address + A and Ethernet + address E, a logical flow with match ip4.dst == +- N/M, whose priority is 400 +- + the number of 1-bits in M if the router port is not a +- distributed gateway port, else the priority is the number of ++ N/M, whose priority is the number of + 1-bits in M, has the following actions: +

    + +@@ -2663,7 +2630,7 @@ icmp4 { +
      +
    • + For each NAT rule in the OVN Northbound database that can +- be handled in a distributed manner, a priority-200 logical ++ be handled in a distributed manner, a priority-100 logical + flow with match ip4.src == B && + outport == GW, where GW is + the logical router distributed gateway port, with actions +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6655,43 +6655,6 @@ build_routing_policy_flow(struct hmap *l + ds_destroy(&actions); + } + +-/* default logical flow prioriry for distributed routes */ +-#define DROUTE_PRIO 400 +- +-static void +-add_distributed_routes(struct hmap *lflows, struct ovn_datapath *od) +-{ +- struct ds actions = DS_EMPTY_INITIALIZER; +- struct ds match = DS_EMPTY_INITIALIZER; +- +- for (size_t i = 0; i < od->nbr->n_nat; i++) { +- const struct nbrec_nat *nat = od->nbr->nat[i]; +- +- if (strcmp(nat->type, "dnat_and_snat") || +- !nat->external_mac) { +- continue; +- } +- +- bool is_ipv4 = strchr(nat->logical_ip, '.') ? true : false; +- ds_put_format(&match, "ip%s.src == %s && is_chassis_resident(\"%s\")", +- is_ipv4 ? "4" : "6", nat->logical_ip, +- nat->logical_port); +- char *prefix = is_ipv4 ? "" : "xx"; +- ds_put_format(&actions, "outport = %s; eth.src = %s; " +- "%sreg0 = ip%s.dst; %sreg1 = %s; next;", +- od->l3dgw_port->json_key, nat->external_mac, +- prefix, is_ipv4 ? "4" : "6", +- prefix, nat->external_ip); +- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, DROUTE_PRIO, +- ds_cstr(&match), ds_cstr(&actions)); +- ds_clear(&match); +- ds_clear(&actions); +- } +- +- ds_destroy(&actions); +- ds_destroy(&match); +-} +- + static void + add_route(struct hmap *lflows, const struct ovn_port *op, + const char *lrp_addr_s, const char *network_s, int plen, +@@ -6710,13 +6673,6 @@ add_route(struct hmap *lflows, const str + priority = (plen * 2) + 1; + } + +- /* traffic for internal IPs of logical switch ports must be sent to +- * the gw controller through the overlay tunnels +- */ +- if (op->nbrp && !op->nbrp->n_gateway_chassis) { +- priority += DROUTE_PRIO; +- } +- + /* IPv6 link-local addresses must be scoped to the local router port. */ + if (!is_ipv4) { + struct in6_addr network; +@@ -8357,7 +8313,7 @@ build_lrouter_flows(struct hmap *datapat + is_v6 ? "6" : "4", + nat->logical_ip, + od->l3dgw_port->json_key); +- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200, ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100, + ds_cstr(&match), "next;"); + } + +@@ -8633,15 +8589,6 @@ build_lrouter_flows(struct hmap *datapat + ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1", "next;"); + } + +- /* Logical router ingress table IP_ROUTING - IP routing for distributed +- * logical router +- */ +- HMAP_FOR_EACH (od, key_node, datapaths) { +- if (od->nbr && od->l3dgw_port) { +- add_distributed_routes(lflows, od); +- } +- } +- + /* Logical router ingress table 7: IP Routing. + * + * A packet that arrives at this table is an IP packet that should be +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -9482,20 +9482,6 @@ AT_CHECK([as hv3 ovs-vsctl set Open_vSwi + OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-vsctl show | \ + grep "Port patch-br-int-to-ln_port" | wc -l`]) + +-AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \ +-grep "ip4.src == 10.0.0.3 && is_chassis_resident(\"foo1\")" -c`]) +-AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \ +-grep "ip4.src == 10.0.0.4 && is_chassis_resident(\"foo2\")" -c`]) +- +-key=`ovn-sbctl --bare --columns tunnel_key list datapath_Binding lr0` +-# Check that the OVS flows appear for the dnat_and_snat entries in +-# lr_in_ip_routing table. +-OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \ +-grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.3" -c`]) +- +-OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \ +-grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.4" -c`]) +- + # Re-add nat-addresses option + ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" + diff --git a/SOURCES/0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch b/SOURCES/0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch new file mode 100644 index 0000000..7682c37 --- /dev/null +++ b/SOURCES/0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch @@ -0,0 +1,67 @@ +From be5b458778abfc6dd3d30c0f520c77186935f9b2 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Tue, 19 Nov 2019 17:52:01 +0530 +Subject: [PATCH 2/2] Skip IPv6 NS packets in router egress SNAT pipeline + +When ovn-controller injects IPv6 NS packet to learn the mac, in some +setups it is observed that the packet is dropped by ovs-vswitchd. We see +below logs + +2019-11-18T10:42:17.973Z|00001|ofproto_dpif_upcall(handler6)|INFO|received packet on unassociated datapath port 4294967295 +2019-11-18T10:42:18.221Z|00001|ofproto_dpif_upcall(revalidator8)|WARN|Failed to acquire udpif_key corresponding to unexpected flow (Invalid argument): ufid:9ba1081f-a692-4c1c-a79b-d1cf04175f7d + +Upon further debugging I noticed that, xlate_lookup() fails when there is upcall +from kernel datapath because of ct related actions. When ovn-controller injects +the packet it sets inport=CONTROLLER. + +This patch addresses this issue by avoiding the IPv6 NS packets to be +sent to conntrack in the router egress pipeline. + +This should be ideally fixed in ovs-vswitchd. + +Reported-by: Russell Bryant +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 9 +++++++++ + ovn/northd/ovn-northd.c | 5 +++++ + 2 files changed, 14 insertions(+) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 0fac02340..8f8fd5c01 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -2704,6 +2704,15 @@ nd_ns { + changed based on the configuration in the OVN Northbound database. +

      + ++
        ++
      • ++ A priority-120 flow to advance the IPv6 Neighbor solicitation packet ++ to next table to skip SNAT. In the case where ovn-controller injects ++ an IPv6 Neighbor Solicitation packet (for nd_ns action) ++ we don't want the packet to go throught conntrack. ++
      • ++
      ++ +

      Egress Table 1: SNAT on Gateway Routers

      + +
        +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index bcadcca3d..d94276b1e 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -7394,6 +7394,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 0, "1", "next;"); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;"); + ++ /* Send the IPv6 NS packets to next table. When ovn-controller ++ * generates IPv6 NS (for the action - nd_ns{}), the injected ++ * packet would go through conntrack - which is not required. */ ++ ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 120, "nd_ns", "next;"); ++ + /* NAT rules are only valid on Gateway routers and routers with + * l3dgw_port (router has a port with "redirect-chassis" + * specified). */ +-- +2.23.0 + diff --git a/SOURCES/0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch b/SOURCES/0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch new file mode 100644 index 0000000..4a011f9 --- /dev/null +++ b/SOURCES/0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch @@ -0,0 +1,185 @@ +From 67b9788bb5c4de9a22f52ebfaee2701c804cd9e8 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Fri, 25 Oct 2019 17:20:32 -0400 +Subject: [PATCH 2/5] actions: Add IPv6 support to lflow NAT actions + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique +--- + include/ovn/actions.h | 6 +++++- + ovn/lib/actions.c | 35 +++++++++++++++++++++++++++-------- + ovn/utilities/ovn-trace.c | 15 ++++++++++----- + tests/ovn.at | 18 ++++++++++++------ + 4 files changed, 54 insertions(+), 20 deletions(-) + +diff --git a/include/ovn/actions.h b/include/ovn/actions.h +index 4e2f4d28d..f4997e9c9 100644 +--- a/include/ovn/actions.h ++++ b/include/ovn/actions.h +@@ -225,7 +225,11 @@ struct ovnact_ct_commit { + /* OVNACT_CT_DNAT, OVNACT_CT_SNAT. */ + struct ovnact_ct_nat { + struct ovnact ovnact; +- ovs_be32 ip; ++ int family; ++ union { ++ struct in6_addr ipv6; ++ ovs_be32 ipv4; ++ }; + uint8_t ltable; /* Logical table ID of next table. */ + }; + +diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c +index 45f8715cf..7857f658a 100644 +--- a/ovn/lib/actions.c ++++ b/ovn/lib/actions.c +@@ -755,11 +755,18 @@ parse_ct_nat(struct action_context *ctx, const char *name, + + if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { + if (ctx->lexer->token.type != LEX_T_INTEGER +- || ctx->lexer->token.format != LEX_F_IPV4) { +- lexer_syntax_error(ctx->lexer, "expecting IPv4 address"); ++ || (ctx->lexer->token.format != LEX_F_IPV4 ++ && ctx->lexer->token.format != LEX_F_IPV6)) { ++ lexer_syntax_error(ctx->lexer, "expecting IPv4 or IPv6 address"); + return; + } +- cn->ip = ctx->lexer->token.value.ipv4; ++ if (ctx->lexer->token.format == LEX_F_IPV4) { ++ cn->family = AF_INET; ++ cn->ipv4 = ctx->lexer->token.value.ipv4; ++ } else if (ctx->lexer->token.format == LEX_F_IPV6) { ++ cn->family = AF_INET6; ++ cn->ipv6 = ctx->lexer->token.value.ipv6; ++ } + lexer_get(ctx->lexer); + + if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) { +@@ -784,8 +791,12 @@ static void + format_ct_nat(const struct ovnact_ct_nat *cn, const char *name, struct ds *s) + { + ds_put_cstr(s, name); +- if (cn->ip) { +- ds_put_format(s, "("IP_FMT")", IP_ARGS(cn->ip)); ++ if (cn->family == AF_INET) { ++ ds_put_format(s, "("IP_FMT")", IP_ARGS(cn->ipv4)); ++ } else if (cn->family == AF_INET6) { ++ ds_put_char(s, '('); ++ ipv6_format_addr(&cn->ipv6, s); ++ ds_put_char(s, ')'); + } + ds_put_char(s, ';'); + } +@@ -831,9 +842,17 @@ encode_ct_nat(const struct ovnact_ct_nat *cn, + nat->flags = 0; + nat->range_af = AF_UNSPEC; + +- if (cn->ip) { ++ if (cn->family == AF_INET) { + nat->range_af = AF_INET; +- nat->range.addr.ipv4.min = cn->ip; ++ nat->range.addr.ipv4.min = cn->ipv4; ++ if (snat) { ++ nat->flags |= NX_NAT_F_SRC; ++ } else { ++ nat->flags |= NX_NAT_F_DST; ++ } ++ } else if (cn->family == AF_INET6) { ++ nat->range_af = AF_INET6; ++ nat->range.addr.ipv6.min = cn->ipv6; + if (snat) { + nat->flags |= NX_NAT_F_SRC; + } else { +@@ -843,7 +862,7 @@ encode_ct_nat(const struct ovnact_ct_nat *cn, + + ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset); + ct = ofpacts->header; +- if (cn->ip) { ++ if (cn->family == AF_INET || cn->family == AF_INET6) { + ct->flags |= NX_CT_F_COMMIT; + } + ofpact_finish(ofpacts, &ct->ofpact); +diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c +index 103b25891..7ed4a3842 100644 +--- a/ovn/utilities/ovn-trace.c ++++ b/ovn/utilities/ovn-trace.c +@@ -1886,7 +1886,7 @@ execute_ct_nat(const struct ovnact_ct_nat *ct_nat, + enum ovnact_pipeline pipeline, struct ovs_list *super) + { + bool is_dst = ct_nat->ovnact.type == OVNACT_CT_DNAT; +- if (!is_dst && dp->has_local_l3gateway && !ct_nat->ip) { ++ if (!is_dst && dp->has_local_l3gateway && ct_nat->family == AF_UNSPEC) { + /* "ct_snat;" has no visible effect in a gateway router. */ + return; + } +@@ -1897,10 +1897,15 @@ execute_ct_nat(const struct ovnact_ct_nat *ct_nat, + struct flow ct_flow = *uflow; + struct ds s = DS_EMPTY_INITIALIZER; + ds_put_format(&s, "ct_%cnat", direction[0]); +- if (ct_nat->ip) { +- ds_put_format(&s, "(ip4.%s="IP_FMT")", direction, IP_ARGS(ct_nat->ip)); +- ovs_be32 *ip = is_dst ? &ct_flow.nw_dst : &ct_flow.nw_src; +- *ip = ct_nat->ip; ++ if (ct_nat->family != AF_UNSPEC) { ++ if (ct_nat->family == AF_INET) { ++ ds_put_format(&s, "(ip4.%s="IP_FMT")", direction, ++ IP_ARGS(ct_nat->ipv4)); ++ } else { ++ ds_put_format(&s, "(ip6.%s=", direction); ++ ipv6_format_addr(&ct_nat->ipv6, &s); ++ ds_put_char(&s, ')'); ++ } + + uint8_t state = is_dst ? CS_DST_NAT : CS_SRC_NAT; + ct_flow.ct_state |= state; +diff --git a/tests/ovn.at b/tests/ovn.at +index ccf6a5332..e38d14cdf 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -1027,15 +1027,18 @@ ct_dnat; + ct_dnat(192.168.1.2); + encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2)) + has prereqs ip ++ct_dnat(fd11::2); ++ encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=fd11::2)) ++ has prereqs ip + + ct_dnat(192.168.1.2, 192.168.1.3); + Syntax error at `,' expecting `)'. + ct_dnat(foo); +- Syntax error at `foo' expecting IPv4 address. ++ Syntax error at `foo' expecting IPv4 or IPv6 address. + ct_dnat(foo, bar); +- Syntax error at `foo' expecting IPv4 address. ++ Syntax error at `foo' expecting IPv4 or IPv6 address. + ct_dnat(); +- Syntax error at `)' expecting IPv4 address. ++ Syntax error at `)' expecting IPv4 or IPv6 address. + + # ct_snat + ct_snat; +@@ -1044,15 +1047,18 @@ ct_snat; + ct_snat(192.168.1.2); + encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=192.168.1.2)) + has prereqs ip ++ct_snat(fd11::2); ++ encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=fd11::2)) ++ has prereqs ip + + ct_snat(192.168.1.2, 192.168.1.3); + Syntax error at `,' expecting `)'. + ct_snat(foo); +- Syntax error at `foo' expecting IPv4 address. ++ Syntax error at `foo' expecting IPv4 or IPv6 address. + ct_snat(foo, bar); +- Syntax error at `foo' expecting IPv4 address. ++ Syntax error at `foo' expecting IPv4 or IPv6 address. + ct_snat(); +- Syntax error at `)' expecting IPv4 address. ++ Syntax error at `)' expecting IPv4 or IPv6 address. + + # ct_clear + ct_clear; +-- +2.23.0 + diff --git a/SOURCES/0002-add-meter-support-to-trigger_event-action.patch b/SOURCES/0002-add-meter-support-to-trigger_event-action.patch new file mode 100644 index 0000000..bd48e6b --- /dev/null +++ b/SOURCES/0002-add-meter-support-to-trigger_event-action.patch @@ -0,0 +1,141 @@ +From da1253be2ed4bc2ff43d8eb2e2f57c4abdbd5e5f Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com> +References: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2019 16:22:52 +0200 +Subject: [PATCH ovn 2/3] add meter support to trigger_event action + +Introduce meter support to trigger_event action in order to not +overload pinctrl thread under heavy load + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Mark Michelson +Acked-by: Mark Michelson +--- + include/ovn/actions.h | 1 + + ovn/lib/actions.c | 43 +++++++++++++++++++++++++++++++++++++------ + ovn/ovn-sb.xml | 5 ++++- + tests/ovn.at | 4 ++++ + 4 files changed, 46 insertions(+), 7 deletions(-) + +--- a/include/ovn/actions.h ++++ b/include/ovn/actions.h +@@ -327,6 +327,7 @@ struct ovnact_controller_event { + int event_type; /* controller event type */ + struct ovnact_gen_option *options; + size_t n_options; ++ char *meter; + }; + + /* OVNACT_BIND_VPORT. */ +--- a/ovn/lib/actions.c ++++ b/ovn/lib/actions.c +@@ -1273,6 +1273,9 @@ format_TRIGGER_EVENT(const struct ovnact + { + ds_put_format(s, "trigger_event(event = \"%s\"", + event_to_string(event->event_type)); ++ if (event->meter) { ++ ds_put_format(s, ", meter = \"%s\"", event->meter); ++ } + for (const struct ovnact_gen_option *o = event->options; + o < &event->options[event->n_options]; o++) { + ds_put_cstr(s, ", "); +@@ -1420,10 +1423,21 @@ encode_TRIGGER_EVENT(const struct ovnact + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) + { ++ uint32_t meter_id = NX_CTLR_NO_METER; + size_t oc_offset; + ++ if (event->meter) { ++ meter_id = ovn_extend_table_assign_id(ep->meter_table, event->meter, ++ ep->lflow_uuid); ++ if (meter_id == EXT_TABLE_ID_INVALID) { ++ VLOG_WARN("Unable to assign id for trigger meter: %s", ++ event->meter); ++ return; ++ } ++ } ++ + oc_offset = encode_start_controller_op(ACTION_OPCODE_EVENT, false, +- NX_CTLR_NO_METER, ofpacts); ++ meter_id, ofpacts); + ovs_be32 ofs = htonl(event->event_type); + ofpbuf_put(ofpacts, &ofs, sizeof ofs); + +@@ -1738,11 +1752,27 @@ parse_trigger_event(struct action_contex + sizeof *event->options); + } + +- struct ovnact_gen_option *o = &event->options[event->n_options++]; +- memset(o, 0, sizeof *o); +- parse_gen_opt(ctx, o, +- &ctx->pp->controller_event_opts->event_opts[event_type], +- event_to_string(event_type)); ++ if (lexer_match_id(ctx->lexer, "meter")) { ++ if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { ++ return; ++ } ++ /* If multiple meters are given, use the most recent. */ ++ if (ctx->lexer->token.type == LEX_T_STRING && ++ strlen(ctx->lexer->token.s)) { ++ free(event->meter); ++ event->meter = xstrdup(ctx->lexer->token.s); ++ } else if (ctx->lexer->token.type != LEX_T_STRING) { ++ lexer_syntax_error(ctx->lexer, "expecting string"); ++ return; ++ } ++ lexer_get(ctx->lexer); ++ } else { ++ struct ovnact_gen_option *o = &event->options[event->n_options++]; ++ memset(o, 0, sizeof *o); ++ parse_gen_opt(ctx, o, ++ &ctx->pp->controller_event_opts->event_opts[event_type], ++ event_to_string(event_type)); ++ } + if (ctx->lexer->error) { + return; + } +@@ -1763,6 +1793,7 @@ static void + ovnact_controller_event_free(struct ovnact_controller_event *event) + { + free_gen_options(event->options, event->n_options); ++ free(event->meter); + } + + static void +--- a/ovn/ovn-sb.xml ++++ b/ovn/ovn-sb.xml +@@ -1994,7 +1994,9 @@ tcp.flags = RST; +

        + This action is used to allow ovs-vswitchd to report CMS related + events writing them in table. +- Supported event: ++ It is possible to associate a meter to a each event in order to ++ not overload pinctrl thread under heavy load; each meter is ++ identified though a defined naming convention. Supported events: +

        + +
          +@@ -2005,6 +2007,7 @@ tcp.flags = RST; + no configured backend destinations. For this event, the event + info includes the load balancer VIP, the load balancer UUID, + and the transport protocol. ++ Associated meter: event-elb +

          + +
        +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -1363,6 +1363,10 @@ tcp_reset { }; + trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); + encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63) + ++trigger_event(event = "empty_lb_backends", meter="event-elb" vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); ++ formats as trigger_event(event = "empty_lb_backends", meter = "event-elb", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); ++ encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63,meter_id=5) ++ + # Testing invalid vip results in extra error messages from socket-util.c + trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "sctp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); + Load balancer protocol 'sctp' is not 'tcp' or 'udp' diff --git a/SOURCES/0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch b/SOURCES/0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch new file mode 100644 index 0000000..c77dbde --- /dev/null +++ b/SOURCES/0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch @@ -0,0 +1,65 @@ +From d6d98d46254435b0ce0507d694f37b401bd91b30 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Thu, 11 Jun 2020 18:44:41 +0530 +Subject: [PATCH 2/2] northd: By pass IPv6 Router Adv and Router Solicitation + packets from ACL stages. + +We already add below logical flows to by pass IPv6 Neighbor discovery packets +from in/out ACL stage. + +table=6 (ls_in_acl ), priority=65535, match=(nd), action=(next;) +table=4 (ls_out_acl ), priority=65535, match=(nd), action=(next;) + +This patch also adds nd_rs and nd_ra to these logical flows. Without these +the IPv6 Router Adv packets generated by ovn-controller are dropped if +CMS has configured ACLs. + +Reported-by: Jakub Libosvar +Signed-off-by: Numan Siddique +Acked-by: Mark Michelson + +(cherry-picked from upstream master commit 90e5971018277ab0f383a56f59ffcfe17466a2c6) + +Change-Id: I33fcb3032fe946f2b2333a8cf2791af75dceaf44 +--- + ovn/northd/ovn-northd.8.xml | 6 ++++++ + ovn/northd/ovn-northd.c | 6 ++++-- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 95b826944..3fb811bf6 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -421,6 +421,12 @@ + ACL re-allow this connection. + + ++
      • ++ A priority-65535 flow that allows IPv6 Neighbor solicitation, ++ Neighbor discover, Router solicitation and Router advertisement ++ packets. ++
      • ++ +
      • + A priority 34000 logical flow is added for each logical switch datapath + with the match eth.dst = E to allow the service +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index b3561f986..ab5c291c7 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -5217,8 +5217,10 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, + /* Ingress and Egress ACL Table (Priority 65535). + * + * Not to do conntrack on ND packets. */ +- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "nd", "next;"); +- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "nd", "next;"); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, ++ "nd || nd_ra || nd_rs", "next;"); ++ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, ++ "nd || nd_ra || nd_rs", "next;"); + } + + /* Ingress or Egress ACL Table (Various priorities). */ +-- +2.26.2 + diff --git a/SOURCES/0002-northd-Log-all-dynamic-address-assignments.patch b/SOURCES/0002-northd-Log-all-dynamic-address-assignments.patch new file mode 100644 index 0000000..c26b700 --- /dev/null +++ b/SOURCES/0002-northd-Log-all-dynamic-address-assignments.patch @@ -0,0 +1,55 @@ +From e7461df9caf912b2040bb8e330ba68f538001113 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Sat, 7 Dec 2019 23:09:38 -0500 +Subject: [PATCH ovn 2/4] northd: Log all dynamic address assignments + +This patch adds INFO level log messages for all dynamic address +assignments (MAC, IPv4, IPv6). While debugging some issues in +ovn-kubernetes, I found it would be helpful to see ovn-northd's view +of what addresses were assigned where and when from its perspective. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique + +(cherry picked from upstream commit 50fdc3e4cfc487eeb9b81c07d645cf46f4557309) +--- + ovn/northd/ovn-northd.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 55734b0..40835a0 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -1708,6 +1708,8 @@ update_dynamic_addresses(struct dynamic_address_update *update) + break; + case DYNAMIC: + ip4 = htonl(ipam_get_unused_ip(update->od)); ++ VLOG_INFO("Assigned dynamic IPv4 address '"IP_FMT"' to port '%s'", ++ IP_ARGS(ip4), update->op->nbsp->name); + } + + struct eth_addr mac; +@@ -1722,6 +1724,8 @@ update_dynamic_addresses(struct dynamic_address_update *update) + break; + case DYNAMIC: + eth_addr_from_uint64(ipam_get_unused_mac(ip4), &mac); ++ VLOG_INFO("Assigned dynamic MAC address '"ETH_ADDR_FMT"' to port '%s'", ++ ETH_ADDR_ARGS(mac), update->op->nbsp->name); + break; + } + +@@ -1739,6 +1743,11 @@ update_dynamic_addresses(struct dynamic_address_update *update) + break; + case DYNAMIC: + in6_generate_eui64(mac, &update->od->ipam_info.ipv6_prefix, &ip6); ++ struct ds ip6_ds = DS_EMPTY_INITIALIZER; ++ ipv6_format_addr(&ip6, &ip6_ds); ++ VLOG_INFO("Assigned dynamic IPv6 address '%s' to port '%s'", ++ ip6_ds.string, update->op->nbsp->name); ++ ds_destroy(&ip6_ds); + break; + } + +-- +1.8.3.1 + diff --git a/SOURCES/0002-northd-add-the-possibility-to-define-localnet-as-qos.patch b/SOURCES/0002-northd-add-the-possibility-to-define-localnet-as-qos.patch new file mode 100644 index 0000000..5dbd365 --- /dev/null +++ b/SOURCES/0002-northd-add-the-possibility-to-define-localnet-as-qos.patch @@ -0,0 +1,117 @@ +From 82e377ad94ff84f3b1fd82e9aa8ea8edbd5b9337 Mon Sep 17 00:00:00 2001 +Message-Id: <82e377ad94ff84f3b1fd82e9aa8ea8edbd5b9337.1569932887.git.lorenzo.bianconi@redhat.com> +In-Reply-To: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com> +References: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 24 Sep 2019 18:39:55 +0200 +Subject: [PATCH ovn 2/3] northd: add the possibility to define localnet as qos + capable port + +Refactor allocate_chassis_queueid and free_chassis_queueid in order +to get an unused queue_id even for localnet ports and add the +the possibility to define localnet as qos capable port + +Acked-by: Dumitru Ceara +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Mark Michelson +Acked-by: Mark Michelson +--- + ovn/northd/ovn-northd.c | 45 ++++++++++++++++++++++++++++++--------------- + 1 file changed, 30 insertions(+), 15 deletions(-) + +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -357,7 +357,7 @@ destroy_chassis_queues(struct hmap *set) + } + + static void +-add_chassis_queue(struct hmap *set, struct uuid *chassis_uuid, ++add_chassis_queue(struct hmap *set, const struct uuid *chassis_uuid, + uint32_t queue_id) + { + struct ovn_chassis_qdisc_queues *node = xmalloc(sizeof *node); +@@ -368,7 +368,7 @@ add_chassis_queue(struct hmap *set, stru + } + + static bool +-chassis_queueid_in_use(const struct hmap *set, struct uuid *chassis_uuid, ++chassis_queueid_in_use(const struct hmap *set, const struct uuid *chassis_uuid, + uint32_t queue_id) + { + const struct ovn_chassis_qdisc_queues *node; +@@ -383,31 +383,38 @@ chassis_queueid_in_use(const struct hmap + } + + static uint32_t +-allocate_chassis_queueid(struct hmap *set, struct sbrec_chassis *chassis) ++allocate_chassis_queueid(struct hmap *set, const struct uuid *uuid, char *name) + { ++ if (!uuid) { ++ return 0; ++ } ++ + for (uint32_t queue_id = QDISC_MIN_QUEUE_ID + 1; + queue_id <= QDISC_MAX_QUEUE_ID; + queue_id++) { +- if (!chassis_queueid_in_use(set, &chassis->header_.uuid, queue_id)) { +- add_chassis_queue(set, &chassis->header_.uuid, queue_id); ++ if (!chassis_queueid_in_use(set, uuid, queue_id)) { ++ add_chassis_queue(set, uuid, queue_id); + return queue_id; + } + } + + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_WARN_RL(&rl, "all %s queue ids exhausted", chassis->name); ++ VLOG_WARN_RL(&rl, "all %s queue ids exhausted", name); + return 0; + } + + static void +-free_chassis_queueid(struct hmap *set, struct sbrec_chassis *chassis, ++free_chassis_queueid(struct hmap *set, const struct uuid *uuid, + uint32_t queue_id) + { +- const struct uuid *chassis_uuid = &chassis->header_.uuid; ++ if (!uuid) { ++ return; ++ } ++ + struct ovn_chassis_qdisc_queues *node; + HMAP_FOR_EACH_WITH_HASH (node, key_node, +- hash_chassis_queue(chassis_uuid, queue_id), set) { +- if (uuid_equals(chassis_uuid, &node->chassis_uuid) ++ hash_chassis_queue(uuid, queue_id), set) { ++ if (uuid_equals(uuid, &node->chassis_uuid) + && node->queue_id == queue_id) { + hmap_remove(set, &node->key_node); + free(node); +@@ -2568,15 +2575,23 @@ ovn_port_update_sbrec(struct northd_cont + uint32_t queue_id = smap_get_int( + &op->sb->options, "qdisc_queue_id", 0); + bool has_qos = port_has_qos_params(&op->nbsp->options); ++ const struct uuid *uuid = NULL; + struct smap options; ++ char *name = ""; ++ ++ if (!strcmp(op->nbsp->type, "localnet")) { ++ uuid = &op->sb->header_.uuid; ++ name = "localnet"; ++ } else if (op->sb->chassis) { ++ uuid = &op->sb->chassis->header_.uuid; ++ name = op->sb->chassis->name; ++ } + +- if (op->sb->chassis && has_qos && !queue_id) { ++ if (has_qos && !queue_id) { + queue_id = allocate_chassis_queueid(chassis_qdisc_queues, +- op->sb->chassis); ++ uuid, name); + } else if (!has_qos && queue_id) { +- free_chassis_queueid(chassis_qdisc_queues, +- op->sb->chassis, +- queue_id); ++ free_chassis_queueid(chassis_qdisc_queues, uuid, queue_id); + queue_id = 0; + } + diff --git a/SOURCES/0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch b/SOURCES/0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch new file mode 100644 index 0000000..fe15b5b --- /dev/null +++ b/SOURCES/0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch @@ -0,0 +1,799 @@ +From 9bb08e772a3ac946466906d89cf460d852e6144d Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Wed, 27 Nov 2019 14:15:22 +0100 +Subject: [PATCH ovn 2/4] ovn-controller: Add per node states to I-P engine. + +This commit transforms the 'changed' field in struct engine_node in a +'state' field. Possible node states are: +- "Stale": data in the node is not up to date with the DB. +- "Updated": data in the node is valid but was updated during + the last run of the engine. +- "Valid": data in the node is valid and didn't change during + the last run of the engine. +- "Aborted": during the last run, processing was aborted for + this node. + +This commit also further refactors the I-P engine: +- instead of recursively performing all the engine processing a + preprocessing stage is added (engine_get_nodes()) before the main processing + loop is executed in order to topologically sort nodes in the engine such + that all inputs of a given node appear in the sorted array before the node + itself. This simplifies a bit the code in engine_run(). +- remove the need for using an engine_run_id by using the newly added states. +- turn the global 'engine_abort_recompute' into an argument to be passed to + engine_run(). It's relevant only in the current run context anyway as + we reset it before every call to engine_run(). + +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit 5ed53faecef12c09330ced445418c961cb1f8caf) + +Change-Id: I9c3322fe8eb51ffcad62797f5f2a7306bb0c53f4 +--- + ovn/controller/ovn-controller.c | 101 +++++++++--------- + ovn/lib/inc-proc-eng.c | 231 ++++++++++++++++++++++++++++------------ + ovn/lib/inc-proc-eng.h | 74 +++++++++---- + 3 files changed, 268 insertions(+), 138 deletions(-) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index ad5b067..8c474e9 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -722,10 +722,10 @@ en_ofctrl_is_connected_run(struct engine_node *node) + (struct ed_type_ofctrl_is_connected *)node->data; + if (data->connected != ofctrl_is_connected()) { + data->connected = !data->connected; +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + return; + } +- node->changed = false; ++ engine_set_node_state(node, EN_VALID); + } + + struct ed_type_addr_sets { +@@ -775,7 +775,7 @@ en_addr_sets_run(struct engine_node *node) + addr_sets_init(as_table, &as->addr_sets); + + as->change_tracked = false; +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + } + + static bool +@@ -794,11 +794,14 @@ addr_sets_sb_address_set_handler(struct engine_node *node) + addr_sets_update(as_table, &as->addr_sets, &as->new, + &as->deleted, &as->updated); + +- node->changed = !sset_is_empty(&as->new) || !sset_is_empty(&as->deleted) +- || !sset_is_empty(&as->updated); ++ if (!sset_is_empty(&as->new) || !sset_is_empty(&as->deleted) || ++ !sset_is_empty(&as->updated)) { ++ engine_set_node_state(node, EN_UPDATED); ++ } else { ++ engine_set_node_state(node, EN_VALID); ++ } + + as->change_tracked = true; +- node->changed = true; + return true; + } + +@@ -849,7 +852,7 @@ en_port_groups_run(struct engine_node *node) + port_groups_init(pg_table, &pg->port_groups); + + pg->change_tracked = false; +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + } + + static bool +@@ -868,11 +871,14 @@ port_groups_sb_port_group_handler(struct engine_node *node) + port_groups_update(pg_table, &pg->port_groups, &pg->new, + &pg->deleted, &pg->updated); + +- node->changed = !sset_is_empty(&pg->new) || !sset_is_empty(&pg->deleted) +- || !sset_is_empty(&pg->updated); ++ if (!sset_is_empty(&pg->new) || !sset_is_empty(&pg->deleted) || ++ !sset_is_empty(&pg->updated)) { ++ engine_set_node_state(node, EN_UPDATED); ++ } else { ++ engine_set_node_state(node, EN_VALID); ++ } + + pg->change_tracked = true; +- node->changed = true; + return true; + } + +@@ -1053,7 +1059,7 @@ en_runtime_data_run(struct engine_node *node) + update_ct_zones(local_lports, local_datapaths, ct_zones, + ct_zone_bitmap, pending_ct_zones); + +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + } + + static bool +@@ -1119,10 +1125,10 @@ en_mff_ovn_geneve_run(struct engine_node *node) + enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id(); + if (data->mff_ovn_geneve != mff_ovn_geneve) { + data->mff_ovn_geneve = mff_ovn_geneve; +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + return; + } +- node->changed = false; ++ engine_set_node_state(node, EN_VALID); + } + + struct ed_type_flow_output { +@@ -1284,7 +1290,7 @@ en_flow_output_run(struct engine_node *node) + active_tunnels, + flow_table); + +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + } + + static bool +@@ -1366,7 +1372,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node) + flow_table, group_table, meter_table, lfrr, + conj_id_ofs); + +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + return handled; + } + +@@ -1389,7 +1395,7 @@ flow_output_sb_mac_binding_handler(struct engine_node *node) + lflow_handle_changed_neighbors(sbrec_port_binding_by_name, + mac_binding_table, flow_table); + +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + return true; + } + +@@ -1493,7 +1499,7 @@ flow_output_sb_port_binding_handler(struct engine_node *node) + chassis, ct_zones, local_datapaths, + active_tunnels, flow_table); + +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + return true; + } + +@@ -1542,7 +1548,7 @@ flow_output_sb_multicast_group_handler(struct engine_node *node) + mff_ovn_geneve, chassis, ct_zones, local_datapaths, + flow_table); + +- node->changed = true; ++ engine_set_node_state(node, EN_UPDATED); + return true; + + } +@@ -1656,7 +1662,9 @@ _flow_output_resource_ref_handler(struct engine_node *node, + conj_id_ofs, &changed)) { + return false; + } +- node->changed = changed || node->changed; ++ if (changed) { ++ engine_set_node_state(node, EN_UPDATED); ++ } + } + SSET_FOR_EACH (ref_name, updated) { + if (!lflow_handle_changed_ref(ref_type, ref_name, +@@ -1669,7 +1677,9 @@ _flow_output_resource_ref_handler(struct engine_node *node, + conj_id_ofs, &changed)) { + return false; + } +- node->changed = changed || node->changed; ++ if (changed) { ++ engine_set_node_state(node, EN_UPDATED); ++ } + } + SSET_FOR_EACH (ref_name, new) { + if (!lflow_handle_changed_ref(ref_type, ref_name, +@@ -1682,7 +1692,9 @@ _flow_output_resource_ref_handler(struct engine_node *node, + conj_id_ofs, &changed)) { + return false; + } +- node->changed = changed || node->changed; ++ if (changed) { ++ engine_set_node_state(node, EN_UPDATED); ++ } + } + + return true; +@@ -1904,9 +1916,6 @@ main(int argc, char *argv[]) + unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd, + NULL); + +- uint64_t engine_run_id = 0; +- bool engine_run_done = true; +- + unsigned int ovs_cond_seqno = UINT_MAX; + unsigned int ovnsb_cond_seqno = UINT_MAX; + +@@ -1914,7 +1923,7 @@ main(int argc, char *argv[]) + exiting = false; + restart = false; + while (!exiting) { +- engine_run_id++; ++ engine_init_run(); + + update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl); + update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl)); +@@ -2007,15 +2016,9 @@ main(int argc, char *argv[]) + * this round of engine_run and continue processing + * acculated changes incrementally later when + * ofctrl_can_put() returns true. */ +- if (engine_run_done) { +- engine_set_abort_recompute(true); +- engine_run_done = engine_run(&en_flow_output, +- engine_run_id); +- } ++ engine_run(false); + } else { +- engine_set_abort_recompute(false); +- engine_run_done = true; +- engine_run(&en_flow_output, engine_run_id); ++ engine_run(true); + } + } + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, +@@ -2034,7 +2037,7 @@ main(int argc, char *argv[]) + sbrec_meter_table_get(ovnsb_idl_loop.idl), + get_nb_cfg(sbrec_sb_global_table_get( + ovnsb_idl_loop.idl)), +- en_flow_output.changed); ++ engine_node_changed(&en_flow_output)); + pinctrl_run(ovnsb_idl_txn, + sbrec_datapath_binding_by_key, + sbrec_port_binding_by_datapath, +@@ -2052,7 +2055,7 @@ main(int argc, char *argv[]) + &ed_runtime_data.local_datapaths, + &ed_runtime_data.active_tunnels); + +- if (en_runtime_data.changed) { ++ if (engine_node_changed(&en_runtime_data)) { + update_sb_monitors(ovnsb_idl_loop.idl, chassis, + &ed_runtime_data.local_lports, + &ed_runtime_data.local_datapaths); +@@ -2060,20 +2063,23 @@ main(int argc, char *argv[]) + } + + } +- if (engine_need_run(&en_flow_output, engine_run_id)) { +- VLOG_DBG("engine did not run, force recompute next time: " +- "br_int %p, chassis %p", br_int, chassis); +- engine_set_force_recompute(true); +- poll_immediate_wake(); +- } else if (!engine_run_done) { ++ ++ if (!engine_has_run()) { ++ if (engine_need_run()) { ++ VLOG_DBG("engine did not run, force recompute next time: " ++ "br_int %p, chassis %p", br_int, chassis); ++ engine_set_force_recompute(true); ++ poll_immediate_wake(); ++ } else { ++ VLOG_DBG("engine did not run, and it was not needed" ++ " either: br_int %p, chassis %p", ++ br_int, chassis); ++ } ++ } else if (engine_aborted()) { + VLOG_DBG("engine was aborted, force recompute next time: " + "br_int %p, chassis %p", br_int, chassis); + engine_set_force_recompute(true); + poll_immediate_wake(); +- } else if (!engine_has_run(&en_flow_output, engine_run_id)) { +- VLOG_DBG("engine did not run, and it was not needed" +- " either: br_int %p, chassis %p", +- br_int, chassis); + } else { + engine_set_force_recompute(false); + } +@@ -2098,8 +2104,7 @@ main(int argc, char *argv[]) + } + } else { + VLOG_DBG("Pending_pkt conn but br_int %p or chassis " +- "%p not ready. run-id: %"PRIu64, br_int, +- chassis, engine_run_id); ++ "%p not ready.", br_int, chassis); + unixctl_command_reply_error(pending_pkt.conn, + "ovn-controller not ready."); + } +@@ -2148,7 +2153,7 @@ main(int argc, char *argv[]) + } + + engine_set_context(NULL); +- engine_cleanup(&en_flow_output); ++ engine_cleanup(); + + /* It's time to exit. Clean up the databases if we are not restarting */ + if (!restart) { +diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c +index ff07ad9..59b5cac 100644 +--- a/ovn/lib/inc-proc-eng.c ++++ b/ovn/lib/inc-proc-eng.c +@@ -31,21 +31,25 @@ + VLOG_DEFINE_THIS_MODULE(inc_proc_eng); + + static bool engine_force_recompute = false; +-static bool engine_abort_recompute = false; ++static bool engine_run_aborted = false; + static const struct engine_context *engine_context; + ++static struct engine_node **engine_nodes; ++static size_t engine_n_nodes; ++ ++static const char *engine_node_state_name[EN_STATE_MAX] = { ++ [EN_STALE] = "Stale", ++ [EN_UPDATED] = "Updated", ++ [EN_VALID] = "Valid", ++ [EN_ABORTED] = "Aborted", ++}; ++ + void + engine_set_force_recompute(bool val) + { + engine_force_recompute = val; + } + +-void +-engine_set_abort_recompute(bool val) +-{ +- engine_abort_recompute = val; +-} +- + const struct engine_context * + engine_get_context(void) + { +@@ -58,26 +62,69 @@ engine_set_context(const struct engine_context *ctx) + engine_context = ctx; + } + +-void +-engine_init(struct engine_node *node) ++/* Builds the topologically sorted 'sorted_nodes' array starting from ++ * 'node'. ++ */ ++static struct engine_node ** ++engine_topo_sort(struct engine_node *node, struct engine_node **sorted_nodes, ++ size_t *n_count, size_t *n_size) + { ++ /* It's not so efficient to walk the array of already sorted nodes but ++ * we know that sorting is done only once at startup so it's ok for now. ++ */ ++ for (size_t i = 0; i < *n_count; i++) { ++ if (sorted_nodes[i] == node) { ++ return sorted_nodes; ++ } ++ } ++ + for (size_t i = 0; i < node->n_inputs; i++) { +- engine_init(node->inputs[i].node); ++ sorted_nodes = engine_topo_sort(node->inputs[i].node, sorted_nodes, ++ n_count, n_size); + } +- if (node->init) { +- node->init(node); ++ if (*n_count == *n_size) { ++ sorted_nodes = x2nrealloc(sorted_nodes, n_size, sizeof *sorted_nodes); + } ++ sorted_nodes[(*n_count)] = node; ++ (*n_count)++; ++ return sorted_nodes; ++} ++ ++/* Return the array of topologically sorted nodes when starting from ++ * 'node'. Stores the number of nodes in 'n_count'. ++ */ ++static struct engine_node ** ++engine_get_nodes(struct engine_node *node, size_t *n_count) ++{ ++ size_t n_size = 0; ++ ++ *n_count = 0; ++ return engine_topo_sort(node, NULL, n_count, &n_size); + } + + void +-engine_cleanup(struct engine_node *node) ++engine_init(struct engine_node *node) + { +- for (size_t i = 0; i < node->n_inputs; i++) { +- engine_cleanup(node->inputs[i].node); ++ engine_nodes = engine_get_nodes(node, &engine_n_nodes); ++ ++ for (size_t i = 0; i < engine_n_nodes; i++) { ++ if (engine_nodes[i]->init) { ++ engine_nodes[i]->init(engine_nodes[i]); ++ } + } +- if (node->cleanup) { +- node->cleanup(node); ++} ++ ++void ++engine_cleanup(void) ++{ ++ for (size_t i = 0; i < engine_n_nodes; i++) { ++ if (engine_nodes[i]->cleanup) { ++ engine_nodes[i]->cleanup(engine_nodes[i]); ++ } + } ++ free(engine_nodes); ++ engine_nodes = NULL; ++ engine_n_nodes = 0; + } + + struct engine_node * +@@ -128,16 +175,59 @@ engine_ovsdb_node_add_index(struct engine_node *node, const char *name, + ed->n_indexes ++; + } + ++void ++engine_set_node_state_at(struct engine_node *node, ++ enum engine_node_state state, ++ const char *where) ++{ ++ if (node->state == state) { ++ return; ++ } ++ ++ VLOG_DBG("%s: node: %s, old_state %s, new_state %s", ++ where, node->name, ++ engine_node_state_name[node->state], ++ engine_node_state_name[state]); ++ ++ node->state = state; ++} ++ ++bool ++engine_node_changed(struct engine_node *node) ++{ ++ return node->state == EN_UPDATED; ++} ++ + bool +-engine_has_run(struct engine_node *node, uint64_t run_id) ++engine_has_run(void) + { +- return node->run_id == run_id; ++ for (size_t i = 0; i < engine_n_nodes; i++) { ++ if (engine_nodes[i]->state != EN_STALE) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool ++engine_aborted(void) ++{ ++ return engine_run_aborted; ++} ++ ++void ++engine_init_run(void) ++{ ++ VLOG_DBG("Initializing new run"); ++ for (size_t i = 0; i < engine_n_nodes; i++) { ++ engine_set_node_state(engine_nodes[i], EN_STALE); ++ } + } + + /* Do a full recompute (or at least try). If we're not allowed then + * mark the node as "aborted". + */ +-static bool ++static void + engine_recompute(struct engine_node *node, bool forced, bool allowed) + { + VLOG_DBG("node: %s, recompute (%s)", node->name, +@@ -145,12 +235,12 @@ engine_recompute(struct engine_node *node, bool forced, bool allowed) + + if (!allowed) { + VLOG_DBG("node: %s, recompute aborted", node->name); +- return false; ++ engine_set_node_state(node, EN_ABORTED); ++ return; + } + ++ /* Run the node handler which might change state. */ + node->run(node); +- VLOG_DBG("node: %s, changed: %d", node->name, node->changed); +- return true; + } + + /* Return true if the node could be computed, false otherwise. */ +@@ -159,7 +249,7 @@ engine_compute(struct engine_node *node, bool recompute_allowed) + { + for (size_t i = 0; i < node->n_inputs; i++) { + /* If the input node data changed call its change handler. */ +- if (node->inputs[i].node->changed) { ++ if (node->inputs[i].node->state == EN_UPDATED) { + VLOG_DBG("node: %s, handle change for input %s", + node->name, node->inputs[i].node->name); + +@@ -170,55 +260,40 @@ engine_compute(struct engine_node *node, bool recompute_allowed) + VLOG_DBG("node: %s, can't handle change for input %s, " + "fall back to recompute", + node->name, node->inputs[i].node->name); +- return engine_recompute(node, false, recompute_allowed); ++ engine_recompute(node, false, recompute_allowed); ++ return (node->state != EN_ABORTED); + } + } + } +- + return true; + } + +-bool engine_run(struct engine_node *node, uint64_t run_id) ++static void ++engine_run_node(struct engine_node *node, bool recompute_allowed) + { +- if (node->run_id == run_id) { +- /* The node was already updated in this run (could be input for +- * multiple other nodes). Stop processing. +- */ +- return true; +- } +- +- /* Initialize the node for this run. */ +- node->run_id = run_id; +- node->changed = false; +- + if (!node->n_inputs) { ++ /* Run the node handler which might change state. */ + node->run(node); +- VLOG_DBG("node: %s, changed: %d", node->name, node->changed); +- return true; +- } +- +- for (size_t i = 0; i < node->n_inputs; i++) { +- if (!engine_run(node->inputs[i].node, run_id)) { +- return false; +- } ++ return; + } + +- bool need_compute = false; +- + if (engine_force_recompute) { +- return engine_recompute(node, true, !engine_abort_recompute); ++ engine_recompute(node, true, recompute_allowed); ++ return; + } + + /* If any of the inputs updated data but there is no change_handler, then + * recompute the current node too. + */ ++ bool need_compute = false; + for (size_t i = 0; i < node->n_inputs; i++) { +- if (node->inputs[i].node->changed) { ++ if (node->inputs[i].node->state == EN_UPDATED) { + need_compute = true; + + /* Trigger a recompute if we don't have a change handler. */ + if (!node->inputs[i].change_handler) { +- return engine_recompute(node, false, !engine_abort_recompute); ++ engine_recompute(node, false, recompute_allowed); ++ return; + } + } + } +@@ -227,33 +302,55 @@ bool engine_run(struct engine_node *node, uint64_t run_id) + /* If we couldn't compute the node we either aborted or triggered + * a full recompute. In any case, stop processing. + */ +- return engine_compute(node, !engine_abort_recompute); ++ if (!engine_compute(node, recompute_allowed)) { ++ return; ++ } + } + +- VLOG_DBG("node: %s, changed: %d", node->name, node->changed); +- return true; ++ /* If we reached this point, either the node was updated or its state is ++ * still valid. ++ */ ++ if (!engine_node_changed(node)) { ++ engine_set_node_state(node, EN_VALID); ++ } + } + +-bool +-engine_need_run(struct engine_node *node, uint64_t run_id) ++void ++engine_run(bool recompute_allowed) + { +- size_t i; +- +- if (node->run_id == run_id) { +- return false; ++ /* If the last run was aborted skip the incremental run because a ++ * recompute is needed first. ++ */ ++ if (!recompute_allowed && engine_run_aborted) { ++ return; + } + +- if (!node->n_inputs) { +- node->run(node); +- VLOG_DBG("input node: %s, changed: %d", node->name, node->changed); +- return node->changed; ++ engine_run_aborted = false; ++ for (size_t i = 0; i < engine_n_nodes; i++) { ++ engine_run_node(engine_nodes[i], recompute_allowed); ++ ++ if (engine_nodes[i]->state == EN_ABORTED) { ++ engine_run_aborted = true; ++ return; ++ } + } ++} + +- for (i = 0; i < node->n_inputs; i++) { +- if (engine_need_run(node->inputs[i].node, run_id)) { ++bool ++engine_need_run(void) ++{ ++ for (size_t i = 0; i < engine_n_nodes; i++) { ++ /* Check only leaf nodes for updates. */ ++ if (engine_nodes[i]->n_inputs) { ++ continue; ++ } ++ ++ engine_nodes[i]->run(engine_nodes[i]); ++ VLOG_DBG("input node: %s, state: %s", engine_nodes[i]->name, ++ engine_node_state_name[engine_nodes[i]->state]); ++ if (engine_nodes[i]->state == EN_UPDATED) { + return true; + } + } +- + return false; + } +diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h +index a4151ca..2f3ff62 100644 +--- a/ovn/lib/inc-proc-eng.h ++++ b/ovn/lib/inc-proc-eng.h +@@ -82,10 +82,21 @@ struct engine_node_input { + bool (*change_handler)(struct engine_node *node); + }; + +-struct engine_node { +- /* A unique id to distinguish each iteration of the engine_run(). */ +- uint64_t run_id; ++enum engine_node_state { ++ EN_STALE, /* Data in the node is not up to date with the DB. */ ++ EN_UPDATED, /* Data in the node is valid but was updated during the ++ * last run. ++ */ ++ EN_VALID, /* Data in the node is valid and didn't change during the ++ * last run. ++ */ ++ EN_ABORTED, /* During the last run, processing was aborted for ++ * this node. ++ */ ++ EN_STATE_MAX, ++}; + ++struct engine_node { + /* A unique name for each node. */ + char *name; + +@@ -102,8 +113,8 @@ struct engine_node { + * node. */ + void *data; + +- /* Whether the data changed in the last engine run. */ +- bool changed; ++ /* State of the node after the last engine run. */ ++ enum engine_node_state state; + + /* Method to initialize data. It may be NULL. */ + void (*init)(struct engine_node *); +@@ -116,23 +127,29 @@ struct engine_node { + void (*run)(struct engine_node *); + }; + +-/* Initialize the data for the engine nodes recursively. It calls each node's ++/* Initialize the data for the engine nodes. It calls each node's + * init() method if not NULL. It should be called before the main loop. */ +-void engine_init(struct engine_node *); ++void engine_init(struct engine_node *node); + +-/* Execute the processing recursively, which should be called in the main +- * loop. Returns true if the execution is compelte, false if it is aborted, +- * which could happen when engine_abort_recompute is set. */ +-bool engine_run(struct engine_node *, uint64_t run_id); ++/* Initialize the engine nodes for a new run. It should be called in the ++ * main processing loop before every potential engine_run(). ++ */ ++void engine_init_run(void); ++ ++/* Execute the processing, which should be called in the main loop. ++ * Updates the engine node's states accordingly. If 'recompute_allowed' is ++ * false and a recompute is required by the current engine run then the engine ++ * aborts. ++ */ ++void engine_run(bool recompute_allowed); + +-/* Clean up the data for the engine nodes recursively. It calls each node's ++/* Clean up the data for the engine nodes. It calls each node's + * cleanup() method if not NULL. It should be called before the program + * terminates. */ +-void engine_cleanup(struct engine_node *); ++void engine_cleanup(void); + + /* Check if engine needs to run but didn't. */ +-bool +-engine_need_run(struct engine_node *, uint64_t run_id); ++bool engine_need_run(void); + + /* Get the input node with for */ + struct engine_node * engine_get_input(const char *input_name, +@@ -151,16 +168,26 @@ void engine_add_input(struct engine_node *node, struct engine_node *input, + * iteration, and the change can't be tracked across iterations */ + void engine_set_force_recompute(bool val); + +-/* Set the flag to cause engine execution to be aborted when there +- * is any recompute to be triggered in any node. */ +-void engine_set_abort_recompute(bool val); +- + const struct engine_context * engine_get_context(void); + + void engine_set_context(const struct engine_context *); + +-/* Return true if the engine has run for 'node' in the 'run_id' iteration. */ +-bool engine_has_run(struct engine_node *node, uint64_t run_id); ++void engine_set_node_state_at(struct engine_node *node, ++ enum engine_node_state state, ++ const char *where); ++ ++/* Return true if during the last iteration the node's data was updated. */ ++bool engine_node_changed(struct engine_node *node); ++ ++/* Return true if the engine has run in the last iteration. */ ++bool engine_has_run(void); ++ ++/* Returns true if during the last engine run we had to abort processing. */ ++bool engine_aborted(void); ++ ++/* Set the state of the node and log changes. */ ++#define engine_set_node_state(node, state) \ ++ engine_set_node_state_at(node, state, OVS_SOURCE_LOCATOR) + + struct ed_ovsdb_index { + const char *name; +@@ -187,6 +214,7 @@ void engine_ovsdb_node_add_index(struct engine_node *, const char *name, + struct engine_node en_##NAME = { \ + .name = NAME_STR, \ + .data = &ed_##NAME, \ ++ .state = EN_STALE, \ + .init = en_##NAME##_init, \ + .run = en_##NAME##_run, \ + .cleanup = en_##NAME##_cleanup, \ +@@ -201,10 +229,10 @@ en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \ + const struct DB_NAME##rec_##TBL_NAME##_table *table = \ + EN_OVSDB_GET(node); \ + if (DB_NAME##rec_##TBL_NAME##_table_track_get_first(table)) { \ +- node->changed = true; \ ++ engine_set_node_state(node, EN_UPDATED); \ + return; \ + } \ +- node->changed = false; \ ++ engine_set_node_state(node, EN_VALID); \ + } \ + static void (*en_##DB_NAME##_##TBL_NAME##_init)(struct engine_node *node) \ + = NULL; \ +-- +1.8.3.1 + diff --git a/SOURCES/0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch b/SOURCES/0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch new file mode 100644 index 0000000..b59c935 --- /dev/null +++ b/SOURCES/0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch @@ -0,0 +1,255 @@ +From f781f1bdb099c0bfc45e6bf732940f076f28c026 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 12 Sep 2019 16:14:20 +0200 +Subject: [PATCH 2/4] ovn-controller: Minimize SB DB port_binding lookups. + +Instead of storing only peer_ports in struct local_datapath, store both +local-remote mappings for patch ports. Also, it's useful to directly +store sbrec_port_binding pointers for all datapath ports as we avoid +doing costly sbrec_port_binding index lookups by port name. + +Change-Id: Id8df886cab6bb4a81cd105d2ac52dd7e9aa03326 +Acked-by: Mark Michelson +Acked-by: Han Zhou +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique + +(cherry-picked from upstream commit 89f5048f960c67e9dac3d45c6a9b25fa260e1a46) +--- + ovn/controller/binding.c | 19 +++++++++--- + ovn/controller/ovn-controller.h | 11 ++++++- + ovn/controller/physical.c | 17 ++++++----- + ovn/controller/pinctrl.c | 53 ++++++++------------------------- + 4 files changed, 48 insertions(+), 52 deletions(-) + +diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c +index c71731a1e..1380a3e6e 100644 +--- a/ovn/controller/binding.c ++++ b/ovn/controller/binding.c +@@ -161,13 +161,24 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, + peer->datapath, false, + depth + 1, local_datapaths); + ld->n_peer_ports++; +- ld->peer_ports = xrealloc(ld->peer_ports, +- ld->n_peer_ports * +- sizeof *ld->peer_ports); +- ld->peer_ports[ld->n_peer_ports - 1] = peer; ++ if (ld->n_peer_ports > ld->n_allocated_peer_ports) { ++ ld->peer_ports = ++ x2nrealloc(ld->peer_ports, ++ &ld->n_allocated_peer_ports, ++ sizeof *ld->peer_ports); ++ } ++ ld->peer_ports[ld->n_peer_ports - 1].local = pb; ++ ld->peer_ports[ld->n_peer_ports - 1].remote = peer; + } + } + } ++ ++ ld->n_ports++; ++ if (ld->n_ports > ld->n_allocated_ports) { ++ ld->ports = x2nrealloc(ld->ports, &ld->n_allocated_ports, ++ sizeof *ld->ports); ++ } ++ ld->ports[ld->n_ports - 1] = pb; + } + sbrec_port_binding_index_destroy_row(target); + } +diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h +index 078c9eabe..09ef4b632 100644 +--- a/ovn/controller/ovn-controller.h ++++ b/ovn/controller/ovn-controller.h +@@ -60,8 +60,17 @@ struct local_datapath { + * hypervisor. */ + bool has_local_l3gateway; + +- const struct sbrec_port_binding **peer_ports; ++ const struct sbrec_port_binding **ports; ++ size_t n_ports; ++ size_t n_allocated_ports; ++ ++ struct { ++ const struct sbrec_port_binding *local; ++ const struct sbrec_port_binding *remote; ++ } *peer_ports; ++ + size_t n_peer_ports; ++ size_t n_allocated_peer_ports; + }; + + struct local_datapath *get_local_datapath(const struct hmap *, +diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c +index 95cbaba49..b439a4852 100644 +--- a/ovn/controller/physical.c ++++ b/ovn/controller/physical.c +@@ -259,11 +259,12 @@ put_remote_port_redirect_bridged(const struct + + uint32_t ls_dp_key = 0; + for (int i = 0; i < ld->n_peer_ports; i++) { +- const struct sbrec_port_binding *sport_binding = ld->peer_ports[i]; +- const char *sport_peer_name = smap_get(&sport_binding->options, +- "peer"); +- const char *distributed_port = smap_get(&binding->options, +- "distributed-port"); ++ const struct sbrec_port_binding *sport_binding = ++ ld->peer_ports[i].remote; ++ const char *sport_peer_name = ++ smap_get(&sport_binding->options, "peer"); ++ const char *distributed_port = ++ smap_get(&binding->options, "distributed-port"); + + if (!strcmp(sport_peer_name, distributed_port)) { + ls_dp_key = sport_binding->datapath->tunnel_key; +@@ -525,7 +526,8 @@ put_replace_chassis_mac_flows(const struct simap *ct_zones, + struct zone_ids zone_ids = get_zone_ids(localnet_port, ct_zones); + + for (int i = 0; i < ld->n_peer_ports; i++) { +- const struct sbrec_port_binding *rport_binding = ld->peer_ports[i]; ++ const struct sbrec_port_binding *rport_binding = ++ ld->peer_ports[i].remote; + struct eth_addr router_port_mac; + char *err_str = NULL; + struct match match; +@@ -632,7 +634,8 @@ put_replace_router_port_mac_flows(struct ovsdb_idl_index + } + + for (int i = 0; i < ld->n_peer_ports; i++) { +- const struct sbrec_port_binding *rport_binding = ld->peer_ports[i]; ++ const struct sbrec_port_binding *rport_binding = ++ ld->peer_ports[i].remote; + struct eth_addr router_port_mac; + struct match match; + struct ofpact_mac *replace_mac; +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 8bc99df4e..5636fc1e0 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -175,8 +175,7 @@ static struct pinctrl pinctrl; + static void init_buffered_packets_map(void); + static void destroy_buffered_packets_map(void); + static void +-run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath, +- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, ++run_buffered_binding(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, + const struct hmap *local_datapaths) + OVS_REQUIRES(pinctrl_mutex); + +@@ -240,10 +239,7 @@ static void wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn); + static void init_ipv6_ras(void); + static void destroy_ipv6_ras(void); + static void ipv6_ra_wait(long long int send_ipv6_ra_time); +-static void prepare_ipv6_ras( +- struct ovsdb_idl_index *sbrec_port_binding_by_datapath, +- struct ovsdb_idl_index *sbrec_port_binding_by_name, +- const struct hmap *local_datapaths) ++static void prepare_ipv6_ras(const struct hmap *local_datapaths) + OVS_REQUIRES(pinctrl_mutex); + static void send_ipv6_ras(struct rconn *swconn, + long long int *send_ipv6_ra_time) +@@ -2216,8 +2212,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, + send_garp_prepare(sbrec_port_binding_by_datapath, + sbrec_port_binding_by_name, br_int, chassis, + local_datapaths, active_tunnels); +- prepare_ipv6_ras(sbrec_port_binding_by_datapath, +- sbrec_port_binding_by_name, local_datapaths); ++ prepare_ipv6_ras(local_datapaths); + sync_dns_cache(dns_table); + controller_event_run(ovnsb_idl_txn, ce_table, chassis); + ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths, +@@ -2225,8 +2220,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, + sbrec_port_binding_by_key, + sbrec_igmp_groups, + sbrec_ip_multicast_opts); +- run_buffered_binding(sbrec_port_binding_by_datapath, +- sbrec_mac_binding_by_lport_ip, ++ run_buffered_binding(sbrec_mac_binding_by_lport_ip, + local_datapaths); + sync_svc_monitors(ovnsb_idl_txn, svc_mon_table, sbrec_port_binding_by_name, + chassis); +@@ -2669,9 +2663,7 @@ send_ipv6_ras(struct rconn *swconn, long long int *send_ipv6_ra_time) + /* Called by pinctrl_run(). Runs with in the main ovn-controller + * thread context. */ + static void +-prepare_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath, +- struct ovsdb_idl_index *sbrec_port_binding_by_name, +- const struct hmap *local_datapaths) ++prepare_ipv6_ras(const struct hmap *local_datapaths) + OVS_REQUIRES(pinctrl_mutex) + { + struct shash_node *iter, *iter_next; +@@ -2684,25 +2676,12 @@ prepare_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath, + bool changed = false; + const struct local_datapath *ld; + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { +- struct sbrec_port_binding *target = sbrec_port_binding_index_init_row( +- sbrec_port_binding_by_datapath); +- sbrec_port_binding_index_set_datapath(target, ld->datapath); + +- struct sbrec_port_binding *pb; +- SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, +- sbrec_port_binding_by_datapath) { +- if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", false)) { +- continue; +- } ++ for (size_t i = 0; i < ld->n_peer_ports; i++) { ++ const struct sbrec_port_binding *peer = ld->peer_ports[i].remote; ++ const struct sbrec_port_binding *pb = ld->peer_ports[i].local; + +- const char *peer_s = smap_get(&pb->options, "peer"); +- if (!peer_s) { +- continue; +- } +- +- const struct sbrec_port_binding *peer +- = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s); +- if (!peer) { ++ if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", false)) { + continue; + } + +@@ -2741,7 +2720,6 @@ prepare_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath, + + /* pinctrl_handler thread will send the IPv6 RAs. */ + } +- sbrec_port_binding_index_destroy_row(target); + } + + /* Remove those that are no longer in the SB database */ +@@ -2987,8 +2965,7 @@ run_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn, + } + + static void +-run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath, +- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, ++run_buffered_binding(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, + const struct hmap *local_datapaths) + OVS_REQUIRES(pinctrl_mutex) + { +@@ -2996,13 +2973,10 @@ run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath, + bool notify = false; + + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { +- struct sbrec_port_binding *target = sbrec_port_binding_index_init_row( +- sbrec_port_binding_by_datapath); +- sbrec_port_binding_index_set_datapath(target, ld->datapath); + +- const struct sbrec_port_binding *pb; +- SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, +- sbrec_port_binding_by_datapath) { ++ for (size_t i = 0; i < ld->n_ports; i++) { ++ ++ const struct sbrec_port_binding *pb = ld->ports[i]; + struct buffered_packets *cur_qp, *next_qp; + HMAP_FOR_EACH_SAFE (cur_qp, next_qp, hmap_node, + &buffered_packets_map) { +@@ -3020,7 +2994,6 @@ run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath, + ds_destroy(&ip_s); + } + } +- sbrec_port_binding_index_destroy_row(target); + } + buffered_packets_map_gc(); + +-- +2.26.2 + diff --git a/SOURCES/0002-ovn-northd-Add-IGMP-Relay-support.patch b/SOURCES/0002-ovn-northd-Add-IGMP-Relay-support.patch new file mode 100644 index 0000000..11eb346 --- /dev/null +++ b/SOURCES/0002-ovn-northd-Add-IGMP-Relay-support.patch @@ -0,0 +1,1313 @@ +From 5e165afe5eca8e093879df006a6d61be2cbdda53 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Fri, 16 Aug 2019 14:20:45 +0200 +Subject: [PATCH 2/4] ovn-northd: Add IGMP Relay support + +Add a new configuration option 'mcast_relay' to the Logical_Router:options +in the OVN Northbound database. + +If a router is configured with 'mcast_relay' enabled then ovn-northd +will install Logical_Flows to allow IP multicast traffic to be routed +between Logical_Switches. The logical router will aggregate all IGMP +groups from attached logical switches and modify the routing pipeline in +the following way: +- Table S_ROUTER_IN_IP_INPUT: add flow allowing IP multicast traffic + if mcast_relay is enabled on the datapath. +- Table S_ROUTER_IN_IP_ROUTING: add flow matching the group address, + update TTL and set outport=" associated with the + IGMP group". Continue to next table. +- Table S_ROUTER_IN_ARP_RESOLVE: bypass ARP resolve for IP multicast + traffic and continue to next table. +- Table S_ROUTER_OUT_DELIVERY: add flow matching IP multicast traffic + and set ETH.SRC to the MAC address of the logical port on which + traffic is forwarded. + +Signed-off-by: Dumitru Ceara +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique + +(cherry-picked from ovn commit 5d1527b11e9420a36adc2410f78be6b6674c098e) +--- + NEWS | 1 + + ovn/lib/logical-fields.c | 2 + + ovn/lib/mcast-group-index.h | 13 +- + ovn/northd/ovn-northd.8.xml | 79 +++++- + ovn/northd/ovn-northd.c | 499 +++++++++++++++++++++++++++--------- + ovn/ovn-nb.xml | 6 + + ovn/ovn-sb.xml | 2 + + tests/ovn.at | 199 ++++++++++++-- + 8 files changed, 650 insertions(+), 151 deletions(-) + +diff --git a/NEWS b/NEWS +index c81890b7e..754ac2e71 100644 +--- a/NEWS ++++ b/NEWS +@@ -64,6 +64,7 @@ v2.12.0 - 03 Sep 2019 + members of the same transport zone(s). + * Support for IGMP Snooping and IGMP Querier. + * Support for new logical switch port type - 'virtual'. ++ * Support for IGMP Snooping/Querier and Relay. + - New QoS type "linux-netem" on Linux. + - Added support for TLS Server Name Indication (SNI). + - Linux datapath: +diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c +index 4ad5bf481..8fb591c0a 100644 +--- a/ovn/lib/logical-fields.c ++++ b/ovn/lib/logical-fields.c +@@ -156,6 +156,8 @@ ovn_init_symtab(struct shash *symtab) + + expr_symtab_add_field(symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false); + expr_symtab_add_field(symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false); ++ expr_symtab_add_predicate(symtab, "ip4.src_mcast", ++ "ip4.src[28..31] == 0xe"); + expr_symtab_add_predicate(symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe"); + + expr_symtab_add_predicate(symtab, "icmp4", "ip4 && ip.proto == 1"); +diff --git a/ovn/lib/mcast-group-index.h b/ovn/lib/mcast-group-index.h +index 859e6a72f..6249cac99 100644 +--- a/ovn/lib/mcast-group-index.h ++++ b/ovn/lib/mcast-group-index.h +@@ -20,8 +20,17 @@ struct ovsdb_idl; + + struct sbrec_datapath_binding; + +-#define OVN_MCAST_FLOOD_TUNNEL_KEY 65535 +-#define OVN_MCAST_UNKNOWN_TUNNEL_KEY (OVN_MCAST_FLOOD_TUNNEL_KEY - 1) ++#define OVN_MIN_MULTICAST 32768 ++#define OVN_MAX_MULTICAST 65535 ++ ++enum ovn_mcast_tunnel_keys { ++ ++ OVN_MCAST_FLOOD_TUNNEL_KEY = OVN_MIN_MULTICAST, ++ OVN_MCAST_UNKNOWN_TUNNEL_KEY, ++ OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY, ++ OVN_MIN_IP_MULTICAST, ++ OVN_MAX_IP_MULTICAST = OVN_MAX_MULTICAST, ++}; + + struct ovsdb_idl_index *mcast_group_index_create(struct ovsdb_idl *); + const struct sbrec_multicast_group * +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 0435dae26..ec2b6454c 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -952,10 +952,40 @@ output; + +
          +
        • +- A priority-100 flow that outputs all packets with an Ethernet broadcast ++ A priority-100 flow that punts all IGMP packets to ++ ovn-controller if IGMP snooping is enabled on the ++ logical switch. ++
        • ++ ++
        • ++ Priority-90 flows that forward registered IP multicast traffic to ++ their corresponding multicast group, which ovn-northd ++ creates based on learnt ++ entries. The flows also forward packets to the ++ MC_MROUTER_FLOOD multicast group, which ++ ovn-nortdh populates with all the logical ports that ++ are connected to logical routers with ++ :mcast_relay='true'. ++
        • ++ ++
        • ++ A priority-85 flow that forwards all IP multicast traffic destined to ++ 224.0.0.X to the MC_FLOOD multicast group, which ++ ovn-northd populates with all enabled logical ports. ++
        • ++ ++
        • ++ A priority-80 flow that forwards all unregistered IP multicast traffic ++ to the MC_MROUTER_FLOOD multicast group, if any. ++ Otherwise the flow drops all unregistered IP multicast packets. This ++ flow is added only if :mcast_flood_unregistered='false'. ++
        • ++ ++
        • ++ A priority-70 flow that outputs all packets with an Ethernet broadcast + or multicast eth.dst to the MC_FLOOD +- multicast group, which ovn-northd populates with all +- enabled logical ports. ++ multicast group. +
        • + +
        • +@@ -1236,6 +1266,14 @@ output; +
        +
      • + ++
      • ++

        ++ A priority-95 flow allows IP multicast traffic if ++ :mcast_relay='true', ++ otherwise drops it. ++

        ++
      • ++ +
      • +

        + ICMP echo reply. These flows reply to ICMP echo requests received +@@ -1982,6 +2020,16 @@ output; +

        + +
          ++
        • ++

          ++ Priority-500 flows that match IP multicast traffic destined to ++ groups registered on any of the attached switches and sets ++ outport to the associated multicast group that will ++ eventually flood the traffic to all interested attached logical ++ switches. The flows also decrement TTL. ++

          ++
        • ++ +
        • +

          + For distributed logical routers where one of the logical router +@@ -2115,6 +2163,15 @@ next; +

          + +
            ++
          • ++

            ++ A priority-500 flow that matches IP multicast traffic that was ++ allowed in the routing pipeline. For this kind of traffic the ++ outport was already set so the flow just advances to ++ the next table. ++

            ++
          • ++ +
          • +

            + For distributed logical routers where one of the logical router +@@ -2684,9 +2741,19 @@ clone { +

            Egress Table 3: Delivery

            + +

            +- Packets that reach this table are ready for delivery. It contains +- priority-100 logical flows that match packets on each enabled logical +- router port, with action output;. ++ Packets that reach this table are ready for delivery. It contains: ++

              ++
            • ++ Priority-110 logical flows that match IP multicast packets on each ++ enabled logical router port and modify the Ethernet source address ++ of the packets to the Ethernet address of the port and then execute ++ action output;. ++
            • ++
            • ++ Priority-100 logical flows that match packets on each enabled ++ logical router port, with action output;. ++
            • ++
            +

            + + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index d81cfd893..83a7ec14f 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -440,32 +440,52 @@ struct ipam_info { + bool mac_only; + }; + +-#define OVN_MIN_MULTICAST 32768 +-#define OVN_MAX_MULTICAST OVN_MCAST_FLOOD_TUNNEL_KEY +-BUILD_ASSERT_DECL(OVN_MIN_MULTICAST < OVN_MAX_MULTICAST); +- +-#define OVN_MIN_IP_MULTICAST OVN_MIN_MULTICAST +-#define OVN_MAX_IP_MULTICAST (OVN_MCAST_UNKNOWN_TUNNEL_KEY - 1) +-BUILD_ASSERT_DECL(OVN_MAX_IP_MULTICAST >= OVN_MIN_MULTICAST); +- + /* + * Multicast snooping and querier per datapath configuration. + */ ++struct mcast_switch_info { ++ ++ bool enabled; /* True if snooping enabled. */ ++ bool querier; /* True if querier enabled. */ ++ bool flood_unregistered; /* True if unregistered multicast should be ++ * flooded. ++ */ ++ bool flood_relay; /* True if the switch is connected to a ++ * multicast router and unregistered multicast ++ * should be flooded to the mrouter. Only ++ * applicable if flood_unregistered == false. ++ */ ++ ++ int64_t table_size; /* Max number of IP multicast groups. */ ++ int64_t idle_timeout; /* Timeout after which an idle group is ++ * flushed. ++ */ ++ int64_t query_interval; /* Interval between multicast queries. */ ++ char *eth_src; /* ETH src address of the multicast queries. */ ++ char *ipv4_src; /* IP src address of the multicast queries. */ ++ int64_t query_max_response; /* Expected time after which reports should ++ * be received for queries that were sent out. ++ */ ++ ++ uint32_t active_flows; /* Current number of active IP multicast ++ * flows. ++ */ ++}; ++ ++struct mcast_router_info { ++ bool relay; /* True if the router should relay IP multicast. */ ++}; ++ + struct mcast_info { +- bool enabled; +- bool querier; +- bool flood_unregistered; +- +- int64_t table_size; +- int64_t idle_timeout; +- int64_t query_interval; +- char *eth_src; +- char *ipv4_src; +- int64_t query_max_response; +- +- struct hmap group_tnlids; +- uint32_t group_tnlid_hint; +- uint32_t active_flows; ++ ++ struct hmap group_tnlids; /* Group tunnel IDs in use on this DP. */ ++ uint32_t group_tnlid_hint; /* Hint for allocating next group tunnel ID. */ ++ struct ovs_list groups; /* List of groups learnt on this DP. */ ++ ++ union { ++ struct mcast_switch_info sw; /* Switch specific multicast info. */ ++ struct mcast_router_info rtr; /* Router specific multicast info. */ ++ }; + }; + + static uint32_t +@@ -566,6 +586,7 @@ ovn_datapath_create(struct hmap *datapaths, const struct uuid *key, + } + + static void ovn_ls_port_group_destroy(struct hmap *nb_pgs); ++static void destroy_mcast_info_for_datapath(struct ovn_datapath *od); + + static void + ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od) +@@ -579,12 +600,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od) + bitmap_free(od->ipam_info.allocated_ipv4s); + free(od->router_ports); + ovn_ls_port_group_destroy(&od->nb_pgs); +- +- if (od->nbs) { +- free(od->mcast_info.eth_src); +- free(od->mcast_info.ipv4_src); +- destroy_tnlids(&od->mcast_info.group_tnlids); +- } ++ destroy_mcast_info_for_datapath(od); + + free(od); + } +@@ -721,23 +737,28 @@ init_ipam_info_for_datapath(struct ovn_datapath *od) + } + + static void +-init_mcast_info_for_datapath(struct ovn_datapath *od) ++init_mcast_info_for_router_datapath(struct ovn_datapath *od) + { +- if (!od->nbs) { +- return; +- } ++ struct mcast_router_info *mcast_rtr_info = &od->mcast_info.rtr; + +- struct mcast_info *mcast_info = &od->mcast_info; ++ mcast_rtr_info->relay = smap_get_bool(&od->nbr->options, "mcast_relay", ++ false); ++} + +- mcast_info->enabled = ++static void ++init_mcast_info_for_switch_datapath(struct ovn_datapath *od) ++{ ++ struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw; ++ ++ mcast_sw_info->enabled = + smap_get_bool(&od->nbs->other_config, "mcast_snoop", false); +- mcast_info->querier = ++ mcast_sw_info->querier = + smap_get_bool(&od->nbs->other_config, "mcast_querier", true); +- mcast_info->flood_unregistered = ++ mcast_sw_info->flood_unregistered = + smap_get_bool(&od->nbs->other_config, "mcast_flood_unregistered", + false); + +- mcast_info->table_size = ++ mcast_sw_info->table_size = + smap_get_ullong(&od->nbs->other_config, "mcast_table_size", + OVN_MCAST_DEFAULT_MAX_ENTRIES); + +@@ -749,54 +770,94 @@ init_mcast_info_for_datapath(struct ovn_datapath *od) + } else if (idle_timeout > OVN_MCAST_MAX_IDLE_TIMEOUT_S) { + idle_timeout = OVN_MCAST_MAX_IDLE_TIMEOUT_S; + } +- mcast_info->idle_timeout = idle_timeout; ++ mcast_sw_info->idle_timeout = idle_timeout; + + uint32_t query_interval = + smap_get_ullong(&od->nbs->other_config, "mcast_query_interval", +- mcast_info->idle_timeout / 2); ++ mcast_sw_info->idle_timeout / 2); + if (query_interval < OVN_MCAST_MIN_QUERY_INTERVAL_S) { + query_interval = OVN_MCAST_MIN_QUERY_INTERVAL_S; + } else if (query_interval > OVN_MCAST_MAX_QUERY_INTERVAL_S) { + query_interval = OVN_MCAST_MAX_QUERY_INTERVAL_S; + } +- mcast_info->query_interval = query_interval; ++ mcast_sw_info->query_interval = query_interval; + +- mcast_info->eth_src = ++ mcast_sw_info->eth_src = + nullable_xstrdup(smap_get(&od->nbs->other_config, "mcast_eth_src")); +- mcast_info->ipv4_src = ++ mcast_sw_info->ipv4_src = + nullable_xstrdup(smap_get(&od->nbs->other_config, "mcast_ip4_src")); + +- mcast_info->query_max_response = ++ mcast_sw_info->query_max_response = + smap_get_ullong(&od->nbs->other_config, "mcast_query_max_response", + OVN_MCAST_DEFAULT_QUERY_MAX_RESPONSE_S); + +- hmap_init(&mcast_info->group_tnlids); +- mcast_info->group_tnlid_hint = OVN_MIN_IP_MULTICAST; +- mcast_info->active_flows = 0; ++ mcast_sw_info->active_flows = 0; ++} ++ ++static void ++init_mcast_info_for_datapath(struct ovn_datapath *od) ++{ ++ if (!od->nbr && !od->nbs) { ++ return; ++ } ++ ++ hmap_init(&od->mcast_info.group_tnlids); ++ od->mcast_info.group_tnlid_hint = OVN_MIN_IP_MULTICAST; ++ ovs_list_init(&od->mcast_info.groups); ++ ++ if (od->nbs) { ++ init_mcast_info_for_switch_datapath(od); ++ } else { ++ init_mcast_info_for_router_datapath(od); ++ } ++} ++ ++static void ++destroy_mcast_info_for_switch_datapath(struct ovn_datapath *od) ++{ ++ struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw; ++ ++ free(mcast_sw_info->eth_src); ++ free(mcast_sw_info->ipv4_src); ++} ++ ++static void ++destroy_mcast_info_for_datapath(struct ovn_datapath *od) ++{ ++ if (!od->nbr && !od->nbs) { ++ return; ++ } ++ ++ if (od->nbs) { ++ destroy_mcast_info_for_switch_datapath(od); ++ } ++ ++ destroy_tnlids(&od->mcast_info.group_tnlids); + } + + static void +-store_mcast_info_for_datapath(const struct sbrec_ip_multicast *sb, +- struct ovn_datapath *od) ++store_mcast_info_for_switch_datapath(const struct sbrec_ip_multicast *sb, ++ struct ovn_datapath *od) + { +- struct mcast_info *mcast_info = &od->mcast_info; ++ struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw; + + sbrec_ip_multicast_set_datapath(sb, od->sb); +- sbrec_ip_multicast_set_enabled(sb, &mcast_info->enabled, 1); +- sbrec_ip_multicast_set_querier(sb, &mcast_info->querier, 1); +- sbrec_ip_multicast_set_table_size(sb, &mcast_info->table_size, 1); +- sbrec_ip_multicast_set_idle_timeout(sb, &mcast_info->idle_timeout, 1); ++ sbrec_ip_multicast_set_enabled(sb, &mcast_sw_info->enabled, 1); ++ sbrec_ip_multicast_set_querier(sb, &mcast_sw_info->querier, 1); ++ sbrec_ip_multicast_set_table_size(sb, &mcast_sw_info->table_size, 1); ++ sbrec_ip_multicast_set_idle_timeout(sb, &mcast_sw_info->idle_timeout, 1); + sbrec_ip_multicast_set_query_interval(sb, +- &mcast_info->query_interval, 1); ++ &mcast_sw_info->query_interval, 1); + sbrec_ip_multicast_set_query_max_resp(sb, +- &mcast_info->query_max_response, 1); ++ &mcast_sw_info->query_max_response, ++ 1); + +- if (mcast_info->eth_src) { +- sbrec_ip_multicast_set_eth_src(sb, mcast_info->eth_src); ++ if (mcast_sw_info->eth_src) { ++ sbrec_ip_multicast_set_eth_src(sb, mcast_sw_info->eth_src); + } + +- if (mcast_info->ipv4_src) { +- sbrec_ip_multicast_set_ip4_src(sb, mcast_info->ipv4_src); ++ if (mcast_sw_info->ipv4_src) { ++ sbrec_ip_multicast_set_ip4_src(sb, mcast_sw_info->ipv4_src); + } + } + +@@ -913,6 +974,7 @@ join_datapaths(struct northd_context *ctx, struct hmap *datapaths, + NULL, nbr, NULL); + ovs_list_push_back(nb_only, &od->list); + } ++ init_mcast_info_for_datapath(od); + ovs_list_push_back(lr_list, &od->lr_list); + } + } +@@ -2006,6 +2068,13 @@ join_logical_ports(struct northd_context *ctx, + break; + } + } ++ ++ /* If the router is multicast enabled then set relay on the switch ++ * datapath. ++ */ ++ if (peer->od && peer->od->mcast_info.rtr.relay) { ++ op->od->mcast_info.sw.flood_relay = true; ++ } + } else if (op->nbrp && op->nbrp->peer && !op->derived) { + struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer); + if (peer) { +@@ -2861,6 +2930,10 @@ struct multicast_group { + static const struct multicast_group mc_flood = + { MC_FLOOD, OVN_MCAST_FLOOD_TUNNEL_KEY }; + ++#define MC_MROUTER_FLOOD "_MC_mrouter_flood" ++static const struct multicast_group mc_mrouter_flood = ++ { MC_MROUTER_FLOOD, OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY }; ++ + #define MC_UNKNOWN "_MC_unknown" + static const struct multicast_group mc_unknown = + { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY }; +@@ -2970,7 +3043,8 @@ ovn_multicast_update_sbrec(const struct ovn_multicast *mc, + */ + struct ovn_igmp_group_entry { + struct ovs_list list_node; /* Linkage in the list of entries. */ +- const struct sbrec_igmp_group *sb; ++ size_t n_ports; ++ struct ovn_port **ports; + }; + + /* +@@ -2979,12 +3053,13 @@ struct ovn_igmp_group_entry { + */ + struct ovn_igmp_group { + struct hmap_node hmap_node; /* Index on 'datapath' and 'address'. */ ++ struct ovs_list list_node; /* Linkage in the per-dp igmp group list. */ + + struct ovn_datapath *datapath; + struct in6_addr address; /* Multicast IPv6-mapped-IPv4 or IPv4 address. */ + struct multicast_group mcgroup; + +- struct ovs_list sb_entries; /* List of SB entries for this group. */ ++ struct ovs_list entries; /* List of SB entries for this group. */ + }; + + static uint32_t +@@ -3012,77 +3087,120 @@ ovn_igmp_group_find(struct hmap *igmp_groups, + return NULL; + } + +-static void ++static struct ovn_igmp_group * + ovn_igmp_group_add(struct northd_context *ctx, struct hmap *igmp_groups, + struct ovn_datapath *datapath, +- const struct sbrec_igmp_group *sb_igmp_group) ++ const struct in6_addr *address, ++ const char *address_s) + { +- struct in6_addr group_address; +- ovs_be32 ipv4; +- +- if (ip_parse(sb_igmp_group->address, &ipv4)) { +- group_address = in6_addr_mapped_ipv4(ipv4); +- } else if (!ipv6_parse(sb_igmp_group->address, &group_address)) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_WARN_RL(&rl, "invalid IGMP group address: %s", +- sb_igmp_group->address); +- return; +- } +- + struct ovn_igmp_group *igmp_group = +- ovn_igmp_group_find(igmp_groups, datapath, &group_address); ++ ovn_igmp_group_find(igmp_groups, datapath, address); + + if (!igmp_group) { + igmp_group = xmalloc(sizeof *igmp_group); + + const struct sbrec_multicast_group *mcgroup = +- mcast_group_lookup(ctx->sbrec_mcast_group_by_name_dp, +- sb_igmp_group->address, datapath->sb); ++ mcast_group_lookup(ctx->sbrec_mcast_group_by_name_dp, address_s, ++ datapath->sb); + + igmp_group->datapath = datapath; +- igmp_group->address = group_address; ++ igmp_group->address = *address; + if (mcgroup) { + igmp_group->mcgroup.key = mcgroup->tunnel_key; + add_tnlid(&datapath->mcast_info.group_tnlids, mcgroup->tunnel_key); + } else { + igmp_group->mcgroup.key = 0; + } +- igmp_group->mcgroup.name = sb_igmp_group->address; +- ovs_list_init(&igmp_group->sb_entries); ++ igmp_group->mcgroup.name = address_s; ++ ovs_list_init(&igmp_group->entries); + + hmap_insert(igmp_groups, &igmp_group->hmap_node, +- ovn_igmp_group_hash(datapath, &group_address)); ++ ovn_igmp_group_hash(datapath, address)); ++ ovs_list_push_back(&datapath->mcast_info.groups, ++ &igmp_group->list_node); + } + ++ return igmp_group; ++} ++ ++static bool ++ovn_igmp_group_get_address(const struct sbrec_igmp_group *sb_igmp_group, ++ struct in6_addr *address) ++{ ++ ovs_be32 ipv4; ++ ++ if (ip_parse(sb_igmp_group->address, &ipv4)) { ++ *address = in6_addr_mapped_ipv4(ipv4); ++ return true; ++ } ++ if (!ipv6_parse(sb_igmp_group->address, address)) { ++ return false; ++ } ++ return true; ++} ++ ++static struct ovn_port ** ++ovn_igmp_group_get_ports(const struct sbrec_igmp_group *sb_igmp_group, ++ size_t *n_ports, struct hmap *ovn_ports) ++{ ++ struct ovn_port **ports = xmalloc(sb_igmp_group->n_ports * sizeof *ports); ++ ++ *n_ports = 0; ++ for (size_t i = 0; i < sb_igmp_group->n_ports; i++) { ++ ports[(*n_ports)] = ++ ovn_port_find(ovn_ports, sb_igmp_group->ports[i]->logical_port); ++ if (ports[(*n_ports)]) { ++ (*n_ports)++; ++ } ++ } ++ ++ return ports; ++} ++ ++static void ++ovn_igmp_group_add_entry(struct ovn_igmp_group *igmp_group, ++ struct ovn_port **ports, size_t n_ports) ++{ + struct ovn_igmp_group_entry *entry = xmalloc(sizeof *entry); + +- entry->sb = sb_igmp_group; +- ovs_list_push_back(&igmp_group->sb_entries , &entry->list_node); ++ entry->ports = ports; ++ entry->n_ports = n_ports; ++ ovs_list_push_back(&igmp_group->entries, &entry->list_node); ++} ++ ++static void ++ovn_igmp_group_destroy_entry(struct ovn_igmp_group_entry *entry) ++{ ++ free(entry->ports); ++} ++ ++static bool ++ovn_igmp_group_allocate_id(struct ovn_igmp_group *igmp_group) ++{ ++ if (igmp_group->mcgroup.key == 0) { ++ struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info; ++ igmp_group->mcgroup.key = ovn_mcast_group_allocate_key(mcast_info); ++ } ++ ++ if (igmp_group->mcgroup.key == 0) { ++ return false; ++ } ++ ++ return true; + } + + static void + ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *igmp_group, +- struct hmap *ovn_ports, + struct hmap *mcast_groups) + { + struct ovn_igmp_group_entry *entry; + +- LIST_FOR_EACH_POP (entry, list_node, &igmp_group->sb_entries) { +- size_t n_oports = 0; +- struct ovn_port **oports = +- xmalloc(entry->sb->n_ports * sizeof *oports); +- +- for (size_t i = 0; i < entry->sb->n_ports; i++) { +- oports[n_oports] = +- ovn_port_find(ovn_ports, entry->sb->ports[i]->logical_port); +- if (oports[n_oports]) { +- n_oports++; +- } +- } +- ++ LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { + ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, +- &igmp_group->mcgroup, oports, n_oports); +- free(oports); ++ &igmp_group->mcgroup, entry->ports, ++ entry->n_ports); ++ ++ ovn_igmp_group_destroy_entry(entry); + free(entry); + } + } +@@ -3094,10 +3212,12 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups, + if (igmp_group) { + struct ovn_igmp_group_entry *entry; + +- LIST_FOR_EACH_POP (entry, list_node, &igmp_group->sb_entries) { ++ LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { ++ ovn_igmp_group_destroy_entry(entry); + free(entry); + } + hmap_remove(igmp_groups, &igmp_group->hmap_node); ++ ovs_list_remove(&igmp_group->list_node); + free(igmp_group); + } + } +@@ -5404,7 +5524,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + +- if (od->mcast_info.enabled) { ++ struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw; ++ ++ if (mcast_sw_info->enabled) { + /* Punt IGMP traffic to controller. */ + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100, + "ip4 && ip.proto == 2", "igmp;"); +@@ -5417,9 +5539,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + "outport = \""MC_FLOOD"\"; output;"); + + /* Drop unregistered IP multicast if not allowed. */ +- if (!od->mcast_info.flood_unregistered) { +- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80, +- "ip4 && ip4.mcast", "drop;"); ++ if (!mcast_sw_info->flood_unregistered) { ++ /* Forward unregistered IP multicast to mrouter (if any). */ ++ if (mcast_sw_info->flood_relay) { ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80, ++ "ip4 && ip4.mcast", ++ "outport = \""MC_MROUTER_FLOOD"\"; output;"); ++ } else { ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80, ++ "ip4 && ip4.mcast", "drop;"); ++ } + } + } + +@@ -5436,18 +5565,26 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + +- struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info; ++ struct mcast_switch_info *mcast_sw_info = ++ &igmp_group->datapath->mcast_info.sw; + +- if (mcast_info->active_flows >= mcast_info->table_size) { ++ if (mcast_sw_info->active_flows >= mcast_sw_info->table_size) { + continue; + } +- mcast_info->active_flows++; ++ mcast_sw_info->active_flows++; + + ds_clear(&match); + ds_clear(&actions); + + ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ", + igmp_group->mcgroup.name); ++ /* Also flood traffic to all multicast routers with relay enabled. */ ++ if (mcast_sw_info->flood_relay) { ++ ds_put_cstr(&actions, ++ "clone { " ++ "outport = \""MC_MROUTER_FLOOD "\"; output; " ++ "};"); ++ } + ds_put_format(&actions, "outport = \"%s\"; output; ", + igmp_group->mcgroup.name); + +@@ -6293,7 +6430,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * source or destination, and zero network source or destination + * (priority 100). */ + ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 100, +- "ip4.mcast || " ++ "ip4.src_mcast ||" + "ip4.src == 255.255.255.255 || " + "ip4.src == 127.0.0.0/8 || " + "ip4.dst == 127.0.0.0/8 || " +@@ -6301,6 +6438,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + "ip4.dst == 0.0.0.0/8", + "drop;"); + ++ /* Allow multicast if relay enabled (priority 95). */ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 95, "ip4.mcast", ++ od->mcast_info.rtr.relay ? "next;" : "drop;"); ++ + /* ARP reply handling. Use ARP replies to populate the logical + * router's ARP table. */ + ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "arp.op == 2", +@@ -7608,6 +7749,27 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + } + } + ++ /* IP Multicast lookup. Here we set the output port, adjust TTL and ++ * advance to next table (priority 500). ++ */ ++ HMAP_FOR_EACH (od, key_node, datapaths) { ++ if (!od->nbr || !od->mcast_info.rtr.relay) { ++ continue; ++ } ++ struct ovn_igmp_group *igmp_group; ++ ++ LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) { ++ ds_clear(&match); ++ ds_clear(&actions); ++ ds_put_format(&match, "ip4 && ip4.dst == %s ", ++ igmp_group->mcgroup.name); ++ ds_put_format(&actions, "outport = \"%s\"; ip.ttl--; next;", ++ igmp_group->mcgroup.name); ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 500, ++ ds_cstr(&match), ds_cstr(&actions)); ++ } ++ } ++ + /* Logical router ingress table 8: Policy. + * + * A packet that arrives at this table is an IP packet that should be +@@ -7638,10 +7800,24 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + /* Local router ingress table 9: ARP Resolution. + * +- * Any packet that reaches this table is an IP packet whose next-hop IP +- * address is in reg0. (ip4.dst is the final destination.) This table +- * resolves the IP address in reg0 into an output port in outport and an +- * Ethernet address in eth.dst. */ ++ * Multicast packets already have the outport set so just advance to next ++ * table (priority 500). */ ++ HMAP_FOR_EACH (od, key_node, datapaths) { ++ if (!od->nbr) { ++ continue; ++ } ++ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 500, ++ "ip4.mcast", "next;"); ++ } ++ ++ /* Local router ingress table 9: ARP Resolution. ++ * ++ * Any unicast packet that reaches this table is an IP packet whose ++ * next-hop IP address is in reg0. (ip4.dst is the final destination.) ++ * This table resolves the IP address in reg0 into an output port in ++ * outport and an Ethernet address in eth.dst. ++ */ + HMAP_FOR_EACH (op, key_node, ports) { + if (op->nbsp && !lsp_is_enabled(op->nbsp)) { + continue; +@@ -8123,9 +8299,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;"); + } + +- /* Logical router egress table 1: Delivery (priority 100). ++ /* Logical router egress table 1: Delivery (priority 100-110). + * +- * Priority 100 rules deliver packets to enabled logical ports. */ ++ * Priority 100 rules deliver packets to enabled logical ports. ++ * Priority 110 rules match multicast packets and update the source ++ * mac before delivering to enabled logical ports. IP multicast traffic ++ * bypasses S_ROUTER_IN_IP_ROUTING route lookups. ++ */ + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbrp) { + continue; +@@ -8145,6 +8325,20 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + ++ /* If multicast relay is enabled then also adjust source mac for IP ++ * multicast traffic. ++ */ ++ if (op->od->mcast_info.rtr.relay) { ++ ds_clear(&match); ++ ds_clear(&actions); ++ ds_put_format(&match, "ip4.mcast && outport == %s", ++ op->json_key); ++ ds_put_format(&actions, "eth.src = %s; output;", ++ op->lrp_networks.ea_s); ++ ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 110, ++ ds_cstr(&match), ds_cstr(&actions)); ++ } ++ + ds_clear(&match); + ds_put_format(&match, "outport == %s", op->json_key); + ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 100, +@@ -8696,7 +8890,7 @@ build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths) + if (!ip_mcast) { + ip_mcast = sbrec_ip_multicast_insert(ctx->ovnsb_txn); + } +- store_mcast_info_for_datapath(ip_mcast, od); ++ store_mcast_info_for_switch_datapath(ip_mcast, od); + } + + /* Delete southbound records without northbound matches. */ +@@ -8728,6 +8922,14 @@ build_mcast_groups(struct northd_context *ctx, + + if (lsp_is_enabled(op->nbsp)) { + ovn_multicast_add(mcast_groups, &mc_flood, op); ++ ++ /* If this port is connected to a multicast router then add it ++ * to the MC_MROUTER_FLOOD group. ++ */ ++ if (op->od->mcast_info.sw.flood_relay && op->peer && ++ op->peer->od && op->peer->od->mcast_info.rtr.relay) { ++ ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op); ++ } + } + } + +@@ -8750,10 +8952,61 @@ build_mcast_groups(struct northd_context *ctx, + continue; + } + ++ struct in6_addr group_address; ++ if (!ovn_igmp_group_get_address(sb_igmp, &group_address)) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); ++ VLOG_WARN_RL(&rl, "invalid IGMP group address: %s", ++ sb_igmp->address); ++ continue; ++ } ++ + /* Add the IGMP group entry. Will also try to allocate an ID for it + * if the multicast group already exists. + */ +- ovn_igmp_group_add(ctx, igmp_groups, od, sb_igmp); ++ struct ovn_igmp_group *igmp_group = ++ ovn_igmp_group_add(ctx, igmp_groups, od, &group_address, ++ sb_igmp->address); ++ ++ /* Extract the IGMP group ports from the SB entry and store them ++ * in the IGMP group. ++ */ ++ size_t n_igmp_ports; ++ struct ovn_port **igmp_ports = ++ ovn_igmp_group_get_ports(sb_igmp, &n_igmp_ports, ports); ++ ovn_igmp_group_add_entry(igmp_group, igmp_ports, n_igmp_ports); ++ } ++ ++ /* Build IGMP groups for multicast routers with relay enabled. The router ++ * IGMP groups are based on the groups learnt by their multicast enabled ++ * peers. ++ */ ++ struct ovn_datapath *od; ++ HMAP_FOR_EACH (od, key_node, datapaths) { ++ ++ if (ovs_list_is_empty(&od->mcast_info.groups)) { ++ continue; ++ } ++ ++ for (size_t i = 0; i < od->n_router_ports; i++) { ++ struct ovn_port *router_port = od->router_ports[i]->peer; ++ ++ if (!router_port || !router_port->od || ++ !router_port->od->mcast_info.rtr.relay) { ++ continue; ++ } ++ ++ struct ovn_igmp_group *igmp_group; ++ LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) { ++ struct ovn_igmp_group *igmp_group_rtr = ++ ovn_igmp_group_add(ctx, igmp_groups, router_port->od, ++ &igmp_group->address, ++ igmp_group->mcgroup.name); ++ struct ovn_port **router_igmp_ports = ++ xmalloc(sizeof *router_igmp_ports); ++ router_igmp_ports[0] = router_port; ++ ovn_igmp_group_add_entry(igmp_group_rtr, router_igmp_ports, 1); ++ } ++ } + } + + /* Walk the aggregated IGMP groups and allocate IDs for new entries. +@@ -8761,21 +9014,17 @@ build_mcast_groups(struct northd_context *ctx, + */ + struct ovn_igmp_group *igmp_group, *igmp_group_next; + HMAP_FOR_EACH_SAFE (igmp_group, igmp_group_next, hmap_node, igmp_groups) { +- if (igmp_group->mcgroup.key == 0) { +- struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info; +- igmp_group->mcgroup.key = ovn_mcast_group_allocate_key(mcast_info); +- } + +- /* If we ran out of keys just destroy the entry. */ +- if (igmp_group->mcgroup.key == 0) { ++ if (!ovn_igmp_group_allocate_id(igmp_group)) { ++ /* If we ran out of keys just destroy the entry. */ + ovn_igmp_group_destroy(igmp_groups, igmp_group); + continue; + } + +- /* Aggregate the ports from all SB entries corresponding to this ++ /* Aggregate the ports from all entries corresponding to this + * group. + */ +- ovn_igmp_group_aggregate_ports(igmp_group, ports, mcast_groups); ++ ovn_igmp_group_aggregate_ports(igmp_group, mcast_groups); + } + } + +diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml +index b457b6efc..c2472a04a 100644 +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -1535,6 +1535,12 @@ + address. +

            + ++ ++

            ++ Enables/disables IP multicast relay between logical switches ++ connected to the logical router. Default: False. ++

            ++
            + + + +diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml +index 71bd08665..477e7bc7a 100644 +--- a/ovn/ovn-sb.xml ++++ b/ovn/ovn-sb.xml +@@ -1017,6 +1017,8 @@ +
          • eth.mcast expands to eth.dst[40]
          • +
          • vlan.present expands to vlan.tci[12]
          • +
          • ip4 expands to eth.type == 0x800
          • ++
          • ip4.src_mcast expands to ++ ip4.src[28..31] == 0xe
          • +
          • ip4.mcast expands to ip4.dst[28..31] == 0xe
          • +
          • ip6 expands to eth.type == 0x86dd
          • +
          • ip expands to ip4 || ip6
          • +diff --git a/tests/ovn.at b/tests/ovn.at +index 3f7e06cf5..df41a7549 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -15900,12 +15900,12 @@ AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP + +-AT_SETUP([ovn -- IGMP snoop/querier]) ++AT_SETUP([ovn -- IGMP snoop/querier/relay]) + AT_SKIP_IF([test $HAVE_PYTHON = no]) + ovn_start + + # Logical network: +-# Two independent logical switches (sw1 and sw2). ++# Three logical switches (sw1-sw3) connected to a logical router (rtr). + # sw1: + # - subnet 10.0.0.0/8 + # - 2 ports bound on hv1 (sw1-p11, sw1-p12) +@@ -15915,6 +15915,10 @@ ovn_start + # - 1 port bound on hv1 (sw2-p1) + # - 1 port bound on hv2 (sw2-p2) + # - IGMP Querier from 20.0.0.254 ++# sw3: ++# - subnet 30.0.0.0/8 ++# - 1 port bound on hv1 (sw3-p1) ++# - 1 port bound on hv2 (sw3-p2) + + reset_pcap_file() { + local iface=$1 +@@ -15991,29 +15995,47 @@ store_igmp_v3_query() { + } + + # +-# send_ip_multicast_pkt INPORT HV ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN +-# IP_PROTO DATA OUTFILE ++# send_ip_multicast_pkt INPORT HV ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN TTL ++# IP_CHKSUM IP_PROTO DATA + # + # This shell function causes an IP multicast packet to be received on INPORT + # of HV. + # The hexdump of the packet is stored in OUTFILE. + # + send_ip_multicast_pkt() { +- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ip_src=$5 ip_dst=$6 +- local ip_len=$7 ip_chksum=$8 proto=$9 data=${10} outfile=${11} +- +- local ip_ttl=20 ++ local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ++ local ip_src=$5 ip_dst=$6 ip_len=$7 ip_ttl=$8 ip_chksum=$9 proto=${10} ++ local data=${11} + + local eth=${eth_dst}${eth_src}0800 + local ip=450000${ip_len}95f14000${ip_ttl}${proto}${ip_chksum}${ip_src}${ip_dst} + local packet=${eth}${ip}${data} + + as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet} ++} ++ ++# ++# store_ip_multicast_pkt ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN TTL ++# IP_CHKSUM IP_PROTO DATA OUTFILE ++# ++# This shell function builds an IP multicast packet and stores the hexdump of ++# the packet in OUTFILE. ++# ++store_ip_multicast_pkt() { ++ local eth_src=$1 eth_dst=$2 ++ local ip_src=$3 ip_dst=$4 ip_len=$5 ip_ttl=$6 ip_chksum=$7 proto=$8 ++ local data=$9 outfile=${10} ++ ++ local eth=${eth_dst}${eth_src}0800 ++ local ip=450000${ip_len}95f14000${ip_ttl}${proto}${ip_chksum}${ip_src}${ip_dst} ++ local packet=${eth}${ip}${data} ++ + echo ${packet} >> ${outfile} + } + + ovn-nbctl ls-add sw1 + ovn-nbctl ls-add sw2 ++ovn-nbctl ls-add sw3 + + ovn-nbctl lsp-add sw1 sw1-p11 + ovn-nbctl lsp-add sw1 sw1-p12 +@@ -16021,6 +16043,26 @@ ovn-nbctl lsp-add sw1 sw1-p21 + ovn-nbctl lsp-add sw1 sw1-p22 + ovn-nbctl lsp-add sw2 sw2-p1 + ovn-nbctl lsp-add sw2 sw2-p2 ++ovn-nbctl lsp-add sw3 sw3-p1 ++ovn-nbctl lsp-add sw3 sw3-p2 ++ ++ovn-nbctl lr-add rtr ++ovn-nbctl lrp-add rtr rtr-sw1 00:00:00:00:01:00 10.0.0.254/24 ++ovn-nbctl lrp-add rtr rtr-sw2 00:00:00:00:02:00 20.0.0.254/24 ++ovn-nbctl lrp-add rtr rtr-sw3 00:00:00:00:03:00 30.0.0.254/24 ++ ++ovn-nbctl lsp-add sw1 sw1-rtr \ ++ -- lsp-set-type sw1-rtr router \ ++ -- lsp-set-addresses sw1-rtr 00:00:00:00:01:00 \ ++ -- lsp-set-options sw1-rtr router-port=rtr-sw1 ++ovn-nbctl lsp-add sw2 sw2-rtr \ ++ -- lsp-set-type sw2-rtr router \ ++ -- lsp-set-addresses sw2-rtr 00:00:00:00:02:00 \ ++ -- lsp-set-options sw2-rtr router-port=rtr-sw2 ++ovn-nbctl lsp-add sw3 sw3-rtr \ ++ -- lsp-set-type sw3-rtr router \ ++ -- lsp-set-addresses sw3-rtr 00:00:00:00:03:00 \ ++ -- lsp-set-options sw3-rtr router-port=rtr-sw3 + + net_add n1 + sim_add hv1 +@@ -16042,6 +16084,11 @@ ovs-vsctl -- add-port br-int hv1-vif3 -- \ + options:tx_pcap=hv1/vif3-tx.pcap \ + options:rxq_pcap=hv1/vif3-rx.pcap \ + ofport-request=1 ++ovs-vsctl -- add-port br-int hv1-vif4 -- \ ++ set interface hv1-vif4 external-ids:iface-id=sw3-p1 \ ++ options:tx_pcap=hv1/vif4-tx.pcap \ ++ options:rxq_pcap=hv1/vif4-rx.pcap \ ++ ofport-request=1 + + sim_add hv2 + as hv2 +@@ -16062,12 +16109,18 @@ ovs-vsctl -- add-port br-int hv2-vif3 -- \ + options:tx_pcap=hv2/vif3-tx.pcap \ + options:rxq_pcap=hv2/vif3-rx.pcap \ + ofport-request=1 ++ovs-vsctl -- add-port br-int hv2-vif4 -- \ ++ set interface hv2-vif4 external-ids:iface-id=sw3-p2 \ ++ options:tx_pcap=hv2/vif4-tx.pcap \ ++ options:rxq_pcap=hv2/vif4-rx.pcap \ ++ ofport-request=1 + + OVN_POPULATE_ARP + + # Enable IGMP snooping on sw1. +-ovn-nbctl set Logical_Switch sw1 other_config:mcast_querier="false" +-ovn-nbctl set Logical_Switch sw1 other_config:mcast_snoop="true" ++ovn-nbctl set Logical_Switch sw1 \ ++ other_config:mcast_querier="false" \ ++ other_config:mcast_snoop="true" + + # No IGMP query should be generated by sw1 (mcast_querier="false"). + truncate -s 0 expected +@@ -16100,9 +16153,12 @@ truncate -s 0 expected + truncate -s 0 expected_empty + send_ip_multicast_pkt hv1-vif2 hv1 \ + 000000000001 01005e000144 \ +- $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \ +- e518e518000a3b3a0000 \ +- expected ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 ++store_ip_multicast_pkt \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 expected + + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected]) + OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) +@@ -16123,17 +16179,19 @@ OVS_WAIT_UNTIL([ + test "${total_entries}" = "1" + ]) + +-# Send traffic traffic and make sure it gets forwarded only on the port that +-# joined. ++# Send traffic and make sure it gets forwarded only on the port that joined. + as hv1 reset_pcap_file hv1-vif1 hv1/vif1 + as hv2 reset_pcap_file hv2-vif1 hv2/vif1 + truncate -s 0 expected + truncate -s 0 expected_empty + send_ip_multicast_pkt hv1-vif2 hv1 \ + 000000000001 01005e000144 \ +- $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \ +- e518e518000a3b3a0000 \ +- expected ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 ++store_ip_multicast_pkt \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 expected + + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) +@@ -16167,5 +16225,110 @@ sleep 1 + OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected]) + OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected]) + ++# Dissable IGMP querier on sw2. ++ovn-nbctl set Logical_Switch sw2 \ ++ other_config:mcast_querier="false" ++ ++# Enable IGMP snooping on sw3. ++ovn-nbctl set Logical_Switch sw3 \ ++ other_config:mcast_querier="false" \ ++ other_config:mcast_snoop="true" ++ ++# Send traffic from sw3 and make sure rtr doesn't relay it. ++truncate -s 0 expected_empty ++ ++as hv1 reset_pcap_file hv1-vif1 hv1/vif1 ++as hv1 reset_pcap_file hv1-vif2 hv1/vif2 ++as hv1 reset_pcap_file hv1-vif3 hv1/vif3 ++as hv1 reset_pcap_file hv1-vif4 hv1/vif4 ++as hv2 reset_pcap_file hv2-vif1 hv2/vif1 ++as hv2 reset_pcap_file hv2-vif2 hv2/vif2 ++as hv2 reset_pcap_file hv2-vif3 hv2/vif3 ++as hv2 reset_pcap_file hv2-vif4 hv2/vif4 ++ ++send_ip_multicast_pkt hv2-vif4 hv2 \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 ++ ++# Sleep a bit to make sure no traffic is received and then check. ++sleep 1 ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty]) ++ ++# Enable IGMP relay on rtr ++ovn-nbctl set logical_router rtr \ ++ options:mcast_relay="true" ++ ++# Inject IGMP Join for 239.0.1.68 on sw1-p11. ++send_igmp_v3_report hv1-vif1 hv1 \ ++ 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ ++ $(ip_to_hex 239 0 1 68) 04 e9b9 \ ++ /dev/null ++# Inject IGMP Join for 239.0.1.68 on sw2-p2. ++send_igmp_v3_report hv2-vif3 hv2 \ ++ 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ ++ $(ip_to_hex 239 0 1 68) 04 e9b9 \ ++ /dev/null ++# Inject IGMP Join for 239.0.1.68 on sw3-p1. ++send_igmp_v3_report hv1-vif4 hv1 \ ++ 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ ++ $(ip_to_hex 239 0 1 68) 04 e9b9 \ ++ /dev/null ++ ++# Check that the IGMP Group is learned by all switches. ++OVS_WAIT_UNTIL([ ++ total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` ++ test "${total_entries}" = "3" ++]) ++ ++# Send traffic from sw3 and make sure it is relayed by rtr. ++# and ports that joined. ++truncate -s 0 expected_routed_sw1 ++truncate -s 0 expected_routed_sw2 ++truncate -s 0 expected_switched ++truncate -s 0 expected_empty ++ ++as hv1 reset_pcap_file hv1-vif1 hv1/vif1 ++as hv1 reset_pcap_file hv1-vif2 hv1/vif2 ++as hv1 reset_pcap_file hv1-vif3 hv1/vif3 ++as hv1 reset_pcap_file hv1-vif4 hv1/vif4 ++as hv2 reset_pcap_file hv2-vif1 hv2/vif1 ++as hv2 reset_pcap_file hv2-vif2 hv2/vif2 ++as hv2 reset_pcap_file hv2-vif3 hv2/vif3 ++as hv2 reset_pcap_file hv2-vif4 hv2/vif4 ++ ++send_ip_multicast_pkt hv2-vif4 hv2 \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 ++store_ip_multicast_pkt \ ++ 000000000100 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \ ++ e518e518000a3b3a0000 expected_routed_sw1 ++store_ip_multicast_pkt \ ++ 000000000200 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \ ++ e518e518000a3b3a0000 expected_routed_sw2 ++store_ip_multicast_pkt \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 expected_switched ++ ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1]) ++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2]) ++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_switched]) ++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty]) ++ + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP +-- +2.21.0 + diff --git a/SOURCES/0002-ovn-northd-Consider-load-balancer-active-backends-in.patch b/SOURCES/0002-ovn-northd-Consider-load-balancer-active-backends-in.patch new file mode 100644 index 0000000..42baae3 --- /dev/null +++ b/SOURCES/0002-ovn-northd-Consider-load-balancer-active-backends-in.patch @@ -0,0 +1,586 @@ +From 14de43e68f38fab76e50c21d3773f44c6e119d0e Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Mon, 27 Jan 2020 18:36:24 +0530 +Subject: [PATCH 2/2] ovn-northd: Consider load balancer active backends in + router pipeline +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The commit [1] which added lood balancer health check support +missed out on updating the logical flows to consider only +active backends in the logical router pipeline if a load balancer +is associated. This patch fixes it. It also refactors the code +a bit. + +Without this, an offline backend may be chosen for load balancing, +resulting in the packet loss. + +This patch also adds logical flows in ingress ACL and egress ACL logical +switch datapath pipeline to skip the ACL policies for service monitor health +check traffic. + +[1] - ba0d6eae960d("ovn-northd: Add support for Load Balancer health check") + +Reported-by: Maciej Józefczyk +Fixes - ba0d6eae960d("ovn-northd: Add support for Load Balancer health check") +Signed-off-by: Numan Siddique +Acked-by: Dumitru Ceara + +(cherry-picked from upstream commit bb9f2b9ce56c9319066524c0ad89034adae6b168) +Conflicts: + tests/ovn.at + +Change-Id: Ibacbaace0d710aa4d3e0b1d077e032443dd2a9e6 +--- + ovn/northd/ovn-northd.8.xml | 22 +++ + ovn/northd/ovn-northd.c | 268 +++++++++++++++++------------------- + tests/ovn.at | 27 ++++ + 3 files changed, 172 insertions(+), 145 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 11ef65964..50f7d941b 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -410,6 +410,17 @@ + in the request direction are skipped here to let a newly created + ACL re-allow this connection. + ++ ++
          • ++ A priority 34000 logical flow is added for each logical switch datapath ++ with the match eth.dst = E to allow the service ++ monitor reply packet destined to ovn-controller ++ with the action next, where E is the ++ service monitor mac defined in the ++ colum of table. ++
          • +
          + +

          Ingress Table 7: from-lport QoS Marking

          +@@ -1220,6 +1231,17 @@ output; + to allow the DNS reply packet from the + Ingress Table 15:DNS responses. +
        • ++ ++
        • ++ A priority 34000 logical flow is added for each logical switch datapath ++ with the match eth.src = E to allow the service ++ monitor request packet generated by ovn-controller ++ with the action next, where E is the ++ service monitor mac defined in the ++ colum of table. ++
        • +
        + +

        Egress Table 5: to-lport QoS Marking

        +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index b71ae7a94..7139143b3 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -3008,6 +3008,7 @@ struct lb_vip { + struct lb_vip_backend { + char *ip; + uint16_t port; ++ int addr_family; + + struct ovn_port *op; /* Logical port to which the ip belong to. */ + bool health_check; +@@ -3163,6 +3164,7 @@ ovn_lb_create(struct northd_context *ctx, struct hmap *lbs, + + lb->vips[n_vips].backends[i].ip = backend_ip; + lb->vips[n_vips].backends[i].port = backend_port; ++ lb->vips[n_vips].backends[i].addr_family = addr_family; + lb->vips[n_vips].backends[i].op = op; + lb->vips[n_vips].backends[i].svc_mon_src_ip = svc_mon_src_ip; + +@@ -3226,6 +3228,38 @@ ovn_lb_destroy(struct ovn_lb *lb) + free(lb->vips); + } + ++static void build_lb_vip_ct_lb_actions(struct lb_vip *lb_vip, ++ struct ds *action) ++{ ++ if (lb_vip->health_check) { ++ ds_put_cstr(action, "ct_lb("); ++ ++ size_t n_active_backends = 0; ++ for (size_t k = 0; k < lb_vip->n_backends; k++) { ++ struct lb_vip_backend *backend = &lb_vip->backends[k]; ++ if (backend->health_check && backend->sbrec_monitor && ++ backend->sbrec_monitor->status && ++ strcmp(backend->sbrec_monitor->status, "online")) { ++ continue; ++ } ++ ++ n_active_backends++; ++ ds_put_format(action, "%s:%"PRIu16",", ++ backend->ip, backend->port); ++ } ++ ++ if (!n_active_backends) { ++ ds_clear(action); ++ ds_put_cstr(action, "drop;"); ++ } else { ++ ds_chomp(action, ','); ++ ds_put_cstr(action, ");"); ++ } ++ } else { ++ ds_put_format(action, "ct_lb(%s);", lb_vip->backend_ips); ++ } ++} ++ + static void + build_ovn_lbs(struct northd_context *ctx, struct hmap *ports, + struct hmap *lbs) +@@ -4579,11 +4613,11 @@ ls_has_dns_records(const struct nbrec_logical_switch *nbs) + + static void + build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows, +- struct smap_node *node, char *ip_address, +- struct nbrec_load_balancer *lb, uint16_t port, +- int addr_family, int pl, struct shash *meter_groups) ++ struct lb_vip *lb_vip, ++ struct nbrec_load_balancer *lb, ++ int pl, struct shash *meter_groups) + { +- if (!controller_event_en || node->value[0]) { ++ if (!controller_event_en || lb_vip->n_backends) { + return; + } + +@@ -4594,32 +4628,35 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows, + meter = "event-elb"; + } + +- if (addr_family == AF_INET) { +- ds_put_format(&match, "ip4.dst == %s && %s", +- ip_address, lb->protocol); +- } else { +- ds_put_format(&match, "ip6.dst == %s && %s", +- ip_address, lb->protocol); +- } +- if (port) { ++ ds_put_format(&match, "ip%s.dst == %s && %s", ++ lb_vip->addr_family == AF_INET ? "4": "6", ++ lb_vip->vip, lb->protocol); ++ ++ char *vip = lb_vip->vip; ++ if (lb_vip->vip_port) { + ds_put_format(&match, " && %s.dst == %u", lb->protocol, +- port); ++ lb_vip->vip_port); ++ vip = xasprintf("%s:%u", lb_vip->vip, lb_vip->vip_port); + } ++ + action = xasprintf("trigger_event(event = \"%s\", " + "meter = \"%s\", vip = \"%s\", " + "protocol = \"%s\", " + "load_balancer = \"" UUID_FMT "\");", + event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS), +- meter, node->key, lb->protocol, ++ meter, vip, lb->protocol, + UUID_ARGS(&lb->header_.uuid)); + ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action); + ds_destroy(&match); ++ if (lb_vip->vip_port) { ++ free(vip); ++ } + free(action); + } + + static void + build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, +- struct shash *meter_groups) ++ struct shash *meter_groups, struct hmap *lbs) + { + /* Do not send ND packets to conntrack */ + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110, +@@ -4643,40 +4680,29 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, + struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); + bool vip_configured = false; + for (int i = 0; i < od->nbs->n_load_balancer; i++) { +- struct nbrec_load_balancer *lb = od->nbs->load_balancer[i]; +- struct smap *vips = &lb->vips; +- struct smap_node *node; +- int addr_family = AF_INET; +- +- SMAP_FOR_EACH (node, vips) { +- vip_configured = true; +- +- /* node->key contains IP:port or just IP. */ +- char *ip_address = NULL; +- uint16_t port; +- ip_address_and_port_from_lb_key(node->key, &ip_address, &port, +- &addr_family); +- if (!ip_address) { +- continue; +- } ++ struct nbrec_load_balancer *nb_lb = od->nbs->load_balancer[i]; ++ struct ovn_lb *lb = ++ ovn_lb_find(lbs, &nb_lb->header_.uuid); ++ ovs_assert(lb); + +- if (addr_family == AF_INET) { +- sset_add(&all_ips_v4, ip_address); ++ for (size_t j = 0; j < lb->n_vips; j++) { ++ struct lb_vip *lb_vip = &lb->vips[j]; ++ if (lb_vip->addr_family == AF_INET) { ++ sset_add(&all_ips_v4, lb_vip->vip); + } else { +- sset_add(&all_ips_v6, ip_address); ++ sset_add(&all_ips_v6, lb_vip->vip); + } + +- build_empty_lb_event_flow(od, lflows, node, ip_address, lb, +- port, addr_family, S_SWITCH_IN_PRE_LB, +- meter_groups); +- +- free(ip_address); ++ build_empty_lb_event_flow(od, lflows, lb_vip, nb_lb, ++ S_SWITCH_IN_PRE_LB, meter_groups); + + /* Ignore L4 port information in the key because fragmented packets + * may not have L4 information. The pre-stateful table will send + * the packet through ct() action to de-fragment. In stateful + * table, we will eventually look at L4 information. */ + } ++ ++ vip_configured = !!lb->n_vips; + } + + /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send +@@ -5224,6 +5250,20 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, + lflows, od, S_SWITCH_OUT_ACL, 34000, "udp.src == 53", + actions); + } ++ ++ /* Add a 34000 priority flow to advance the service monitor reply ++ * packets to skip applying ingress ACLs. */ ++ char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 34000, svc_check_match, ++ "next;"); ++ free(svc_check_match); ++ ++ /* Add a 34000 priority flow to advance the service monitor packets ++ * generated by ovn-controller to skip applying egress ACLs. */ ++ svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac); ++ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 34000, svc_check_match, ++ "next;"); ++ free(svc_check_match); + } + + static void +@@ -5349,36 +5389,7 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs) + struct lb_vip *lb_vip = &lb->vips[j]; + /* New connections in Ingress table. */ + struct ds action = DS_EMPTY_INITIALIZER; +- if (lb_vip->health_check) { +- ds_put_cstr(&action, "ct_lb("); +- +- size_t n_active_backends = 0; +- for (size_t k = 0; k < lb_vip->n_backends; k++) { +- struct lb_vip_backend *backend = &lb_vip->backends[k]; +- bool is_up = true; +- if (backend->health_check && backend->sbrec_monitor && +- backend->sbrec_monitor->status && +- strcmp(backend->sbrec_monitor->status, "online")) { +- is_up = false; +- } +- +- if (is_up) { +- n_active_backends++; +- ds_put_format(&action, "%s:%"PRIu16",", +- backend->ip, backend->port); +- } +- } +- +- if (!n_active_backends) { +- ds_clear(&action); +- ds_put_cstr(&action, "drop;"); +- } else { +- ds_chomp(&action, ','); +- ds_put_cstr(&action, ");"); +- } +- } else { +- ds_put_format(&action, "ct_lb(%s);", lb_vip->backend_ips); +- } ++ build_lb_vip_ct_lb_actions(lb_vip, &action); + + struct ds match = DS_EMPTY_INITIALIZER; + if (lb_vip->addr_family == AF_INET) { +@@ -5699,7 +5710,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + } + + build_pre_acls(od, lflows); +- build_pre_lb(od, lflows, meter_groups); ++ build_pre_lb(od, lflows, meter_groups, lbs); + build_pre_stateful(od, lflows); + build_acls(od, lflows, port_groups); + build_qos(od, lflows); +@@ -6963,15 +6974,11 @@ get_force_snat_ip(struct ovn_datapath *od, const char *key_type, + static void + add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, + struct ds *match, struct ds *actions, int priority, +- const char *lb_force_snat_ip, struct smap_node *lb_info, +- bool is_udp, int addr_family, char *ip_addr, +- uint16_t l4_port, struct nbrec_load_balancer *lb, ++ const char *lb_force_snat_ip, struct lb_vip *lb_vip, ++ bool is_udp, struct nbrec_load_balancer *lb, + struct shash *meter_groups) + { +- char *backend_ips = lb_info->value; +- +- build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb, +- l4_port, addr_family, S_ROUTER_IN_DNAT, ++ build_empty_lb_event_flow(od, lflows, lb_vip, lb, S_ROUTER_IN_DNAT, + meter_groups); + + /* A match and actions for new connections. */ +@@ -7000,7 +7007,7 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, + free(new_match); + free(est_match); + +- if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips) { ++ if (!od->l3dgw_port || !od->l3redirect_port || !lb_vip->n_backends) { + return; + } + +@@ -7009,46 +7016,28 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, + * router has a gateway router port associated. + */ + struct ds undnat_match = DS_EMPTY_INITIALIZER; +- if (addr_family == AF_INET) { ++ if (lb_vip->addr_family == AF_INET) { + ds_put_cstr(&undnat_match, "ip4 && ("); + } else { + ds_put_cstr(&undnat_match, "ip6 && ("); + } +- char *start, *next, *ip_str; +- start = next = xstrdup(backend_ips); +- ip_str = strsep(&next, ","); +- bool backend_ips_found = false; +- while (ip_str && ip_str[0]) { +- char *ip_address = NULL; +- uint16_t port = 0; +- int addr_family_; +- ip_address_and_port_from_lb_key(ip_str, &ip_address, &port, +- &addr_family_); +- if (!ip_address) { +- break; +- } + +- if (addr_family_ == AF_INET) { +- ds_put_format(&undnat_match, "(ip4.src == %s", ip_address); ++ for (size_t i = 0; i < lb_vip->n_backends; i++) { ++ struct lb_vip_backend *backend = &lb_vip->backends[i]; ++ if (backend->addr_family == AF_INET) { ++ ds_put_format(&undnat_match, "(ip4.src == %s", backend->ip); + } else { +- ds_put_format(&undnat_match, "(ip6.src == %s", ip_address); ++ ds_put_format(&undnat_match, "(ip6.src == %s", backend->ip); + } +- free(ip_address); +- if (port) { ++ ++ if (backend->port) { + ds_put_format(&undnat_match, " && %s.src == %d) || ", +- is_udp ? "udp" : "tcp", port); ++ is_udp ? "udp" : "tcp", backend->port); + } else { + ds_put_cstr(&undnat_match, ") || "); + } +- ip_str = strsep(&next, ","); +- backend_ips_found = true; + } + +- free(start); +- if (!backend_ips_found) { +- ds_destroy(&undnat_match); +- return; +- } + ds_chomp(&undnat_match, ' '); + ds_chomp(&undnat_match, '|'); + ds_chomp(&undnat_match, '|'); +@@ -7166,7 +7155,8 @@ lrouter_nat_is_stateless(const struct nbrec_nat *nat) + + static void + build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, +- struct hmap *lflows, struct shash *meter_groups) ++ struct hmap *lflows, struct shash *meter_groups, ++ struct hmap *lbs) + { + /* This flow table structure is documented in ovn-northd(8), so please + * update ovn-northd.8.xml if you change anything. */ +@@ -8546,24 +8536,18 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + struct sset all_ips = SSET_INITIALIZER(&all_ips); + + for (int i = 0; i < od->nbr->n_load_balancer; i++) { +- struct nbrec_load_balancer *lb = od->nbr->load_balancer[i]; +- struct smap *vips = &lb->vips; +- struct smap_node *node; +- +- SMAP_FOR_EACH (node, vips) { +- uint16_t port = 0; +- int addr_family; +- +- /* node->key contains IP:port or just IP. */ +- char *ip_address = NULL; +- ip_address_and_port_from_lb_key(node->key, &ip_address, &port, +- &addr_family); +- if (!ip_address) { +- continue; +- } ++ struct nbrec_load_balancer *nb_lb = od->nbr->load_balancer[i]; ++ struct ovn_lb *lb = ++ ovn_lb_find(lbs, &nb_lb->header_.uuid); ++ ovs_assert(lb); ++ ++ for (size_t j = 0; j < lb->n_vips; j++) { ++ struct lb_vip *lb_vip = &lb->vips[j]; ++ ds_clear(&actions); ++ build_lb_vip_ct_lb_actions(lb_vip, &actions); + +- if (!sset_contains(&all_ips, ip_address)) { +- sset_add(&all_ips, ip_address); ++ if (!sset_contains(&all_ips, lb_vip->vip)) { ++ sset_add(&all_ips, lb_vip->vip); + /* If there are any load balancing rules, we should send + * the packet to conntrack for defragmentation and + * tracking. This helps with two things. +@@ -8573,12 +8557,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * 2. If there are L4 ports in load balancing rules, we + * need the defragmentation to match on L4 ports. */ + ds_clear(&match); +- if (addr_family == AF_INET) { ++ if (lb_vip->addr_family == AF_INET) { + ds_put_format(&match, "ip && ip4.dst == %s", +- ip_address); +- } else if (addr_family == AF_INET6) { ++ lb_vip->vip); ++ } else if (lb_vip->addr_family == AF_INET6) { + ds_put_format(&match, "ip && ip6.dst == %s", +- ip_address); ++ lb_vip->vip); + } + ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, + 100, ds_cstr(&match), "ct_next;"); +@@ -8589,28 +8573,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * via add_router_lb_flow(). One flow is for specific matching + * on ct.new with an action of "ct_lb($targets);". The other + * flow is for ct.est with an action of "ct_dnat;". */ +- ds_clear(&actions); +- ds_put_format(&actions, "ct_lb(%s);", node->value); +- + ds_clear(&match); +- if (addr_family == AF_INET) { ++ if (lb_vip->addr_family == AF_INET) { + ds_put_format(&match, "ip && ip4.dst == %s", +- ip_address); +- } else if (addr_family == AF_INET6) { ++ lb_vip->vip); ++ } else if (lb_vip->addr_family == AF_INET6) { + ds_put_format(&match, "ip && ip6.dst == %s", +- ip_address); ++ lb_vip->vip); + } + + int prio = 110; +- bool is_udp = lb->protocol && !strcmp(lb->protocol, "udp") ? +- true : false; +- if (port) { ++ bool is_udp = nullable_string_is_equal(nb_lb->protocol, "udp"); ++ ++ if (lb_vip->vip_port) { + if (is_udp) { + ds_put_format(&match, " && udp && udp.dst == %d", +- port); ++ lb_vip->vip_port); + } else { + ds_put_format(&match, " && tcp && tcp.dst == %d", +- port); ++ lb_vip->vip_port); + } + prio = 120; + } +@@ -8620,11 +8601,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + od->l3redirect_port->json_key); + } + add_router_lb_flow(lflows, od, &match, &actions, prio, +- lb_force_snat_ip, node, is_udp, +- addr_family, ip_address, port, lb, +- meter_groups); +- +- free(ip_address); ++ lb_force_snat_ip, lb_vip, is_udp, ++ nb_lb, meter_groups); + } + } + sset_destroy(&all_ips); +@@ -9431,7 +9409,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths, + + build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups, + igmp_groups, meter_groups, lbs); +- build_lrouter_flows(datapaths, ports, &lflows, meter_groups); ++ build_lrouter_flows(datapaths, ports, &lflows, meter_groups, lbs); + + /* Push changes to the Logical_Flow table to database. */ + const struct sbrec_logical_flow *sbflow, *next_sbflow; +diff --git a/tests/ovn.at b/tests/ovn.at +index e2565f274..3a5ecf211 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -17007,6 +17007,22 @@ ovn-nbctl --wait=sb ls-lb-add sw0 lb1 + ovn-nbctl --wait=sb ls-lb-add sw1 lb1 + ovn-nbctl --wait=sb lr-lb-add lr0 lb1 + ++ovn-nbctl ls-add public ++ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 ++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=public ++ ++# schedule the gw router port to a chassis. Change the name of the chassis ++ovn-nbctl --wait=hv lrp-set-gateway-chassis lr0-public hv1 20 ++ + OVN_POPULATE_ARP + ovn-nbctl --wait=hv sync + +@@ -17018,6 +17034,11 @@ AT_CHECK([cat lflows.txt], [0], [dnl + table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) + ]) + ++ovn-sbctl dump-flows lr0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=6 (lr_in_dnat ), priority=120 , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++]) ++ + # get the svc monitor mac. + svc_mon_src_mac=`ovn-nbctl get NB_Global . options:svc_monitor_mac | \ + sed s/":"//g | sed s/\"//g` +@@ -17051,5 +17072,11 @@ AT_CHECK([cat lflows.txt], [0], [dnl + table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;) + ]) + ++ovn-sbctl dump-flows lr0 | grep lr_in_dnat | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=6 (lr_in_dnat ), priority=120 , match=(ct.est && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) ++ table=6 (lr_in_dnat ), priority=120 , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(drop;) ++]) ++ + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP +-- +2.24.1 + diff --git a/SOURCES/0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch b/SOURCES/0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch new file mode 100644 index 0000000..cedb1c2 --- /dev/null +++ b/SOURCES/0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch @@ -0,0 +1,739 @@ +From fb226499b9222e0fe9b9ac3b6be9aaa4023b8abe Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Wed, 13 Nov 2019 10:39:58 -0800 +Subject: [PATCH ovn 2/3] ovn-northd: Limit ARP/ND broadcast domain whenever + possible. + +ARP request and ND NS packets for router owned IPs were being +flooded in the complete L2 domain (using the MC_FLOOD multicast group). +However this creates a scaling issue in scenarios where aggregation +logical switches are connected to more logical routers (~350). The +logical pipelines of all routers would have to be executed before the +packet is finally replied to by a single router, the owner of the IP +address. + +This commit limits the broadcast domain by bypassing the L2 Lookup stage +for ARP requests that will be replied by a single router. The packets +are forwarded only to the router port that owns the target IP address. + +IPs that are owned by the routers and for which this fix applies are: +- IP addresses configured on the router ports. +- VIPs. +- NAT IPs. + +Reported-at: https://bugzilla.redhat.com/1756945 +Reported-by: Anil Venkata +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry-picked from upstream commit 32f5ebb06226e3433e53e05bdc75d16752859a0e) + +Change-Id: I3b63e970593a26cbbfc6f4a495db0487c4002419 +--- + ovn/northd/ovn-northd.8.xml | 14 ++ + ovn/northd/ovn-northd.c | 230 +++++++++++++++++++++++++++++---- + ovn/ovn-architecture.7.xml | 19 +++ + tests/ovn.at | 307 ++++++++++++++++++++++++++++++++++++++++++-- + 4 files changed, 530 insertions(+), 40 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 8f8fd5c..dc5fdf4 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1005,6 +1005,20 @@ output; +
      • + +
      • ++ Priority-80 flows for each port connected to a logical router ++ matching self originated GARP/ARP request/ND packets. These packets ++ are flooded to the MC_FLOOD which contains all logical ++ ports. ++
      • ++ ++
      • ++ Priority-75 flows for each IP address/VIP/NAT address owned by a ++ router port connected to the switch. These flows match ARP requests ++ and ND packets for the specific IP addresses. Matched packets are ++ forwarded only to the router that owns the IP address. ++
      • ++ ++
      • + A priority-70 flow that outputs all packets with an Ethernet broadcast + or multicast eth.dst to the MC_FLOOD + multicast group. +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 81c313f..03eb7a4 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -210,6 +210,8 @@ enum ovn_stage { + #define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]" + #define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]" + ++#define FLAGBIT_NOT_VXLAN "flags[1] == 0" ++ + /* Returns an "enum ovn_stage" built from the arguments. */ + static enum ovn_stage + ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline, +@@ -1202,6 +1204,34 @@ ovn_port_allocate_key(struct ovn_datapath *od) + 1, (1u << 15) - 1, &od->port_key_hint); + } + ++/* Returns true if the logical switch port 'enabled' column is empty or ++ * set to true. Otherwise, returns false. */ ++static bool ++lsp_is_enabled(const struct nbrec_logical_switch_port *lsp) ++{ ++ return !lsp->n_enabled || *lsp->enabled; ++} ++ ++/* Returns true only if the logical switch port 'up' column is set to true. ++ * Otherwise, if the column is not set or set to false, returns false. */ ++static bool ++lsp_is_up(const struct nbrec_logical_switch_port *lsp) ++{ ++ return lsp->n_up && *lsp->up; ++} ++ ++static bool ++lsp_is_external(const struct nbrec_logical_switch_port *nbsp) ++{ ++ return !strcmp(nbsp->type, "external"); ++} ++ ++static bool ++lrport_is_enabled(const struct nbrec_logical_router_port *lrport) ++{ ++ return !lrport->enabled || *lrport->enabled; ++} ++ + static char * + chassis_redirect_name(const char *port_name) + { +@@ -3744,28 +3774,6 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op, + + } + +-/* Returns true if the logical switch port 'enabled' column is empty or +- * set to true. Otherwise, returns false. */ +-static bool +-lsp_is_enabled(const struct nbrec_logical_switch_port *lsp) +-{ +- return !lsp->n_enabled || *lsp->enabled; +-} +- +-/* Returns true only if the logical switch port 'up' column is set to true. +- * Otherwise, if the column is not set or set to false, returns false. */ +-static bool +-lsp_is_up(const struct nbrec_logical_switch_port *lsp) +-{ +- return lsp->n_up && *lsp->up; +-} +- +-static bool +-lsp_is_external(const struct nbrec_logical_switch_port *nbsp) +-{ +- return !strcmp(nbsp->type, "external"); +-} +- + static bool + build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, + struct ds *options_action, struct ds *response_action, +@@ -5168,6 +5176,170 @@ build_lrouter_groups(struct hmap *ports, struct ovs_list *lr_list) + } + } + ++/* ++ * Ingress table 17: Flows that flood self originated ARP/ND packets in the ++ * switching domain. ++ */ ++static void ++build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, ++ uint32_t priority, ++ struct ovn_datapath *od, ++ struct hmap *lflows) ++{ ++ struct ds match = DS_EMPTY_INITIALIZER; ++ struct ds eth_src = DS_EMPTY_INITIALIZER; ++ ++ /* Self originated (G)ARP requests/ND need to be flooded as usual. ++ * Determine that packets are self originated by also matching on ++ * source MAC. Matching on ingress port is not reliable in case this ++ * is a VLAN-backed network. ++ * Priority: 80. ++ */ ++ ds_put_format(ð_src, "{ %s, ", op->lrp_networks.ea_s); ++ for (size_t i = 0; i < op->od->nbr->n_nat; i++) { ++ const struct nbrec_nat *nat = op->od->nbr->nat[i]; ++ ++ if (!nat->external_mac) { ++ continue; ++ } ++ ++ ds_put_format(ð_src, "%s, ", nat->external_mac); ++ } ++ ds_chomp(ð_src, ' '); ++ ds_chomp(ð_src, ','); ++ ds_put_cstr(ð_src, "}"); ++ ++ ds_put_format(&match, "eth.src == %s && (arp.op == 1 || nd_ns)", ++ ds_cstr(ð_src)); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, ++ ds_cstr(&match), ++ "outport = \""MC_FLOOD"\"; output;"); ++ ++ ds_destroy(&match); ++ ds_destroy(ð_src); ++} ++ ++/* ++ * Ingress table 17: Flows that forward ARP/ND requests only to the routers ++ * that own the addresses. Other ARP/ND packets are still flooded in the ++ * switching domain as regular broadcast. ++ */ ++static void ++build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, ++ int addr_family, ++ struct ovn_port *patch_op, ++ struct ovn_datapath *od, ++ uint32_t priority, ++ struct hmap *lflows) ++{ ++ struct ds match = DS_EMPTY_INITIALIZER; ++ struct ds actions = DS_EMPTY_INITIALIZER; ++ ++ /* Packets received from VXLAN tunnels have already been through the ++ * router pipeline so we should skip them. Normally this is done by the ++ * multicast_group implementation (VXLAN packets skip table 32 which ++ * delivers to patch ports) but we're bypassing multicast_groups. ++ */ ++ ds_put_cstr(&match, FLAGBIT_NOT_VXLAN " && "); ++ ++ if (addr_family == AF_INET) { ++ ds_put_cstr(&match, "arp.op == 1 && arp.tpa == { "); ++ } else { ++ ds_put_cstr(&match, "nd_ns && nd.target == { "); ++ } ++ ++ const char *ip_address; ++ SSET_FOR_EACH (ip_address, ips) { ++ ds_put_format(&match, "%s, ", ip_address); ++ } ++ ++ ds_chomp(&match, ' '); ++ ds_chomp(&match, ','); ++ ds_put_cstr(&match, "}"); ++ ++ /* Send a the packet only to the router pipeline and skip flooding it ++ * in the broadcast domain. ++ */ ++ ds_put_format(&actions, "outport = %s; output;", patch_op->json_key); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, ++ ds_cstr(&match), ds_cstr(&actions)); ++ ++ ds_destroy(&match); ++ ds_destroy(&actions); ++} ++ ++/* ++ * Ingress table 17: Flows that forward ARP/ND requests only to the routers ++ * that own the addresses. ++ * Priorities: ++ * - 80: self originated GARPs that need to follow regular processing. ++ * - 75: ARP requests to router owned IPs (interface IP/LB/NAT). ++ */ ++static void ++build_lswitch_rport_arp_req_flows(struct ovn_port *op, ++ struct ovn_datapath *sw_od, ++ struct ovn_port *sw_op, ++ struct hmap *lflows) ++{ ++ if (!op || !op->nbrp) { ++ return; ++ } ++ ++ if (!lrport_is_enabled(op->nbrp)) { ++ return; ++ } ++ ++ /* Self originated (G)ARP requests/ND need to be flooded as usual. ++ * Priority: 80. ++ */ ++ build_lswitch_rport_arp_req_self_orig_flow(op, 80, sw_od, lflows); ++ ++ /* Forward ARP requests for owned IP addresses (L3, VIP, NAT) only to this ++ * router port. ++ * Priority: 75. ++ */ ++ struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); ++ struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); ++ ++ for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { ++ sset_add(&all_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s); ++ } ++ for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { ++ sset_add(&all_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s); ++ } ++ ++ get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); ++ ++ for (size_t i = 0; i < op->od->nbr->n_nat; i++) { ++ const struct nbrec_nat *nat = op->od->nbr->nat[i]; ++ ++ if (!strcmp(nat->type, "snat")) { ++ continue; ++ } ++ ++ ovs_be32 ip; ++ ovs_be32 mask; ++ struct in6_addr ipv6; ++ struct in6_addr mask_v6; ++ ++ if (ip_parse_masked(nat->external_ip, &ip, &mask)) { ++ if (!ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6)) { ++ sset_add(&all_ips_v6, nat->external_ip); ++ } ++ } else { ++ sset_add(&all_ips_v4, nat->external_ip); ++ } ++ } ++ ++ build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op, ++ sw_od, 75, lflows); ++ build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op, ++ sw_od, 75, lflows); ++ ++ sset_destroy(&all_ips_v4); ++ sset_destroy(&all_ips_v6); ++} ++ + static void + build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + struct hmap *port_groups, struct hmap *lflows, +@@ -5755,6 +5927,14 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + ++ /* For ports connected to logical routers add flows to bypass the ++ * broadcast flooding of ARP/ND requests in table 17. We direct the ++ * requests only to the router port that owns the IP address. ++ */ ++ if (!strcmp(op->nbsp->type, "router")) { ++ build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows); ++ } ++ + for (size_t i = 0; i < op->nbsp->n_addresses; i++) { + /* Addresses are owned by the logical port. + * Ethernet address followed by zero or more IPv4 +@@ -5886,12 +6066,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + ds_destroy(&actions); + } + +-static bool +-lrport_is_enabled(const struct nbrec_logical_router_port *lrport) +-{ +- return !lrport->enabled || *lrport->enabled; +-} +- + /* Returns a string of the IP address of the router port 'op' that + * overlaps with 'ip_s". If one is not found, returns NULL. + * +diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml +index 2a375c8..771edb8 100644 +--- a/ovn/ovn-architecture.7.xml ++++ b/ovn/ovn-architecture.7.xml +@@ -1390,6 +1390,25 @@ + http://docs.openvswitch.org/en/latest/topics/high-availability. +

        + ++

        ARP request and ND NS packet processing

        ++ ++

        ++ Due to the fact that ARP requests and ND NA packets are usually broadcast ++ packets, for performance reasons, OVN deals with requests that target OVN ++ owned IP addresses (i.e., IP addresses configured on the router ports, ++ VIPs, NAT IPs) in a specific way and only forwards them to the logical ++ router that owns the target IP address. This behavior is different than ++ that of traditional swithces and implies that other routers/hosts ++ connected to the logical switch will not learn the MAC/IP binding from ++ the request packet. ++

        ++ ++

        ++ All other ARP and ND packets are flooded in the L2 broadcast domain and ++ to all attached logical patch ports. ++

        ++ ++ +

        Multiple localnet logical switches connected to a Logical Router

        + +

        +diff --git a/tests/ovn.at b/tests/ovn.at +index a95a057..33989e6 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -2861,7 +2861,7 @@ test_ip() { + done + } + +-# test_arp INPORT SHA SPA TPA [REPLY_HA] ++# test_arp INPORT SHA SPA TPA FLOOD [REPLY_HA] + # + # Causes a packet to be received on INPORT. The packet is an ARP + # request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then +@@ -2872,21 +2872,25 @@ test_ip() { + # SHA and REPLY_HA are each 12 hex digits. + # SPA and TPA are each 8 hex digits. + test_arp() { +- local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 ++ local inport=$1 sha=$2 spa=$3 tpa=$4 flood=$5 reply_ha=$6 + local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} + hv=hv`vif_to_hv $inport` + as $hv ovs-appctl netdev-dummy/receive vif$inport $request + as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request + + # Expect to receive the broadcast ARP on the other logical switch ports if +- # IP address is not configured to the switch patch port. ++ # IP address is not configured on the switch patch port or on the router ++ # port (i.e, $flood == 1). + local i=`vif_to_ls $inport` + local j k + for j in 1 2 3; do + for k in 1 2 3; do +- # 192.168.33.254 is configured to the switch patch port for lrp33, +- # so no ARP flooding expected for it. +- if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then ++ # Skip ingress port. ++ if test $i$j$k == $inport; then ++ continue ++ fi ++ ++ if test X$flood == X1; then + echo $request >> $i$j$k.expected + fi + done +@@ -3023,9 +3027,9 @@ for i in 1 2 3; do + otherip=`ip_to_hex 192 168 $i$j 55` # Some other IP in subnet + externalip=`ip_to_hex 1 2 3 4` # Some other IP not in subnet + +- test_arp $i$j$k $smac $sip $rip $rmac #4 +- test_arp $i$j$k $smac $otherip $rip $rmac #5 +- test_arp $i$j$k $smac $sip $otherip #6 ++ test_arp $i$j$k $smac $sip $rip 0 $rmac #4 ++ test_arp $i$j$k $smac $otherip $rip 0 $rmac #5 ++ test_arp $i$j$k $smac $sip $otherip 1 #6 + + # When rip is 192.168.33.254, ARP request from externalip won't be + # filtered, because 192.168.33.254 is configured to switch peer port +@@ -3034,7 +3038,7 @@ for i in 1 2 3; do + if test $i = 3 && test $j = 3; then + lrp33_rsp=$rmac + fi +- test_arp $i$j$k $smac $externalip $rip $lrp33_rsp #7 ++ test_arp $i$j$k $smac $externalip $rip 0 $lrp33_rsp #7 + + # MAC binding should be learned from ARP request. + host_mac_pretty=f0:00:00:00:0$i:$j$k +@@ -9579,7 +9583,7 @@ ovn-nbctl --wait=hv --timeout=3 sync + # Check that there is a logical flow in logical switch foo's pipeline + # to set the outport to rp-foo (which is expected). + OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \ +-grep rp-foo | grep -v is_chassis_resident | wc -l`]) ++grep rp-foo | grep -v is_chassis_resident | grep priority=50 -c`]) + + # Set the option 'reside-on-redirect-chassis' for foo + ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true +@@ -9587,7 +9591,7 @@ ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true + # to set the outport to rp-foo with the condition is_chassis_redirect. + ovn-sbctl dump-flows foo + OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \ +-grep rp-foo | grep is_chassis_resident | wc -l`]) ++grep rp-foo | grep is_chassis_resident | grep priority=50 -c`]) + + echo "---------NB dump-----" + ovn-nbctl show +@@ -15678,3 +15682,282 @@ AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) + + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP ++ ++AT_SETUP([ovn -- ARP/ND request broadcast limiting]) ++AT_SKIP_IF([test $HAVE_PYTHON = no]) ++ovn_start ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++send_arp_request() { ++ local hv=$1 inport=$2 eth_src=$3 spa=$4 tpa=$5 ++ local eth_dst=ffffffffffff ++ local eth_type=0806 ++ local eth=${eth_dst}${eth_src}${eth_type} ++ ++ local arp=0001080006040001${eth_src}${spa}${eth_dst}${tpa} ++ ++ local request=${eth}${arp} ++ as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request ++} ++ ++send_nd_ns() { ++ local hv=$1 inport=$2 eth_src=$3 spa=$4 tpa=$5 cksum=$6 ++ ++ local eth_dst=ffffffffffff ++ local eth_type=86dd ++ local eth=${eth_dst}${eth_src}${eth_type} ++ ++ local ip_vhlen=60000000 ++ local ip_plen=0020 ++ local ip_next=3a ++ local ip_ttl=ff ++ local ip=${ip_vhlen}${ip_plen}${ip_next}${ip_ttl}${spa}${tpa} ++ ++ # Neighbor Solicitation ++ local icmp6_type=87 ++ local icmp6_code=00 ++ local icmp6_rsvd=00000000 ++ # ICMPv6 source lla option ++ local icmp6_opt=01 ++ local icmp6_optlen=01 ++ local icmp6=${icmp6_type}${icmp6_code}${cksum}${icmp6_rsvd}${tpa}${icmp6_opt}${icmp6_optlen}${eth_src} ++ ++ local request=${eth}${ip}${icmp6} ++ ++ as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request ++} ++ ++src_mac=000000000001 ++ ++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 -- add-port br-int hv1-vif1 -- \ ++ set interface hv1-vif1 external-ids:iface-id=sw-agg-ext \ ++ options:tx_pcap=hv1/vif1-tx.pcap \ ++ options:rxq_pcap=hv1/vif1-rx.pcap \ ++ ofport-request=1 ++ ++# One Aggregation Switch connected to two Logical networks (routers). ++ovn-nbctl ls-add sw-agg ++ovn-nbctl lsp-add sw-agg sw-agg-ext \ ++ -- lsp-set-addresses sw-agg-ext 00:00:00:00:00:01 ++ ++ovn-nbctl lsp-add sw-agg sw-rtr1 \ ++ -- lsp-set-type sw-rtr1 router \ ++ -- lsp-set-addresses sw-rtr1 00:00:00:00:01:00 \ ++ -- lsp-set-options sw-rtr1 router-port=rtr1-sw ++ovn-nbctl lsp-add sw-agg sw-rtr2 \ ++ -- lsp-set-type sw-rtr2 router \ ++ -- lsp-set-addresses sw-rtr2 00:00:00:00:02:00 \ ++ -- lsp-set-options sw-rtr2 router-port=rtr2-sw ++ ++# Configure L3 interface IPv4 & IPv6 on both routers ++ovn-nbctl lr-add rtr1 ++ovn-nbctl lrp-add rtr1 rtr1-sw 00:00:00:00:01:00 10.0.0.1/24 10::1/64 ++ ++ovn-nbctl lr-add rtr2 ++ovn-nbctl lrp-add rtr2 rtr2-sw 00:00:00:00:02:00 10.0.0.2/24 10::2/64 ++ ++OVN_POPULATE_ARP ++ovn-nbctl --wait=hv sync ++ ++sw_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding sw-agg) ++sw_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw-agg) ++ ++r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1) ++r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2) ++ ++mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${sw_dp_uuid} name="_MC_flood") ++mc_key=$(printf "%04x" $mc_key) ++ ++match_sw_metadata="metadata=0x${sw_dp_key}" ++ ++# Inject ARP request for first router owned IP address. ++send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1) ++ ++# Verify that the ARP request is sent only to rtr1. ++match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1" ++match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15" ++match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \ ++ grep n_packets=1 -c) ++ test "1" = "${pkts_to_rtr1}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \ ++ grep n_packets=1 -c) ++ test "0" = "${pkts_to_rtr2}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_flooded=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) ++ test "0" = "${pkts_flooded}" ++]) ++ ++# Inject ND_NS for ofirst router owned IP address. ++src_ipv6=00100000000000000000000000000254 ++dst_ipv6=00100000000000000000000000000001 ++send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d ++ ++# Verify that the ND_NS is sent only to rtr1. ++match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \ ++ grep n_packets=1 -c) ++ test "1" = "${pkts_to_rtr1}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \ ++ grep n_packets=1 -c) ++ test "0" = "${pkts_to_rtr2}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_flooded=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) ++ test "0" = "${pkts_flooded}" ++]) ++ ++# Configure load balancing on both routers. ++ovn-nbctl lb-add lb1-v4 10.0.0.11 42.42.42.1 ++ovn-nbctl lb-add lb1-v6 10::11 42::1 ++ovn-nbctl lr-lb-add rtr1 lb1-v4 ++ovn-nbctl lr-lb-add rtr1 lb1-v6 ++ ++ovn-nbctl lb-add lb2-v4 10.0.0.22 42.42.42.2 ++ovn-nbctl lb-add lb2-v6 10::22 42::2 ++ovn-nbctl lr-lb-add rtr2 lb2-v4 ++ovn-nbctl lr-lb-add rtr2 lb2-v6 ++ovn-nbctl --wait=hv sync ++ ++# Inject ARP request for first router owned VIP address. ++send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11) ++ ++# Verify that the ARP request is sent only to rtr1. ++match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1" ++match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15" ++match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \ ++ grep n_packets=1 -c) ++ test "1" = "${pkts_to_rtr1}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \ ++ grep n_packets=1 -c) ++ test "0" = "${pkts_to_rtr2}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_flooded=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) ++ test "0" = "${pkts_flooded}" ++]) ++ ++# Inject ND_NS for first router owned VIP address. ++src_ipv6=00100000000000000000000000000254 ++dst_ipv6=00100000000000000000000000000011 ++send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d ++ ++# Verify that the ND_NS is sent only to rtr1. ++match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \ ++ grep n_packets=1 -c) ++ test "1" = "${pkts_to_rtr1}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \ ++ grep n_packets=1 -c) ++ test "0" = "${pkts_to_rtr2}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_flooded=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) ++ test "0" = "${pkts_flooded}" ++]) ++ ++# Configure NAT on both routers ++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1 ++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::111 42::1 ++ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10.0.0.222 42.42.42.2 ++ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10::222 42::2 ++ ++# Inject ARP request for first router owned NAT address. ++send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111) ++ ++# Verify that the ARP request is sent only to rtr1. ++match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1" ++match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15" ++match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \ ++ grep n_packets=1 -c) ++ test "1" = "${pkts_to_rtr1}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \ ++ grep n_packets=1 -c) ++ test "0" = "${pkts_to_rtr2}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_flooded=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) ++ test "0" = "${pkts_flooded}" ++]) ++ ++# Inject ND_NS for first router owned IP address. ++src_ipv6=00100000000000000000000000000254 ++dst_ipv6=00100000000000000000000000000111 ++send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d ++ ++# Verify that the ND_NS is sent only to rtr1. ++match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111" ++ ++as hv1 ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \ ++ grep n_packets=1 -c) ++ test "1" = "${pkts_to_rtr1}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \ ++ grep n_packets=1 -c) ++ test "0" = "${pkts_to_rtr2}" ++]) ++OVS_WAIT_UNTIL([ ++ pkts_flooded=$(ovs-ofctl dump-flows br-int | \ ++ grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) ++ test "0" = "${pkts_flooded}" ++]) ++ ++OVN_CLEANUP([hv1]) ++AT_CLEANUP +-- +1.8.3.1 + diff --git a/SOURCES/0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch b/SOURCES/0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch new file mode 100644 index 0000000..68b190c --- /dev/null +++ b/SOURCES/0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch @@ -0,0 +1,113 @@ +From 96f740501f7eb026f9fbbf7de921c28dca2c9693 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Tue, 24 Mar 2020 20:37:17 +0100 +Subject: [PATCH 2/2] ovn.at: Fix ARP test that fails due to timing. + +The test for "ARP/ND request broadcast limiting" checks that injected +ARP packets are not flooded using the MC_FLOOD multicast group. However, +this introduces a race condition in the test because GARPs generated by +OVN would also hit the same openflow rules. + +Remove the checks that use the MC_FLOOD group. They are also redundant +as the rest of the test checks that packets are forwarded according to +the newly added, higher priority rules. + +Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.") +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique +(cherry picked from upstream commit 598a07cd240d7d01de3d7f04ca7abc58a33977a1) + +Change-Id: Ie526595b017bcfcc4744a014f19db351d98ee658 +--- + tests/ovn.at | 33 --------------------------------- + 1 file changed, 33 deletions(-) + +diff --git a/tests/ovn.at b/tests/ovn.at +index 551c399..72f4ebd 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -15943,9 +15943,6 @@ r1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding rtr1) + r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1) + r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2) + +-mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${sw_dp_uuid} name="_MC_flood") +-mc_key=$(printf "%04x" $mc_key) +- + match_sw_metadata="metadata=0x${sw_dp_key}" + match_r1_metadata="metadata=0x${r1_dp_key}" + +@@ -15970,11 +15967,6 @@ OVS_WAIT_UNTIL([ + grep n_packets=1 -c) + test "0" = "${pkts_to_rtr2}" + ]) +-OVS_WAIT_UNTIL([ +- pkts_flooded=$(ovs-ofctl dump-flows br-int | \ +- grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) +- test "0" = "${pkts_flooded}" +-]) + + # Inject ND_NS for ofirst router owned IP address. + src_ipv6=00100000000000000000000000000254 +@@ -15997,11 +15989,6 @@ OVS_WAIT_UNTIL([ + grep n_packets=1 -c) + test "0" = "${pkts_to_rtr2}" + ]) +-OVS_WAIT_UNTIL([ +- pkts_flooded=$(ovs-ofctl dump-flows br-int | \ +- grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) +- test "0" = "${pkts_flooded}" +-]) + + # Configure load balancing on both routers. + ovn-nbctl lb-add lb1-v4 10.0.0.11 42.42.42.1 +@@ -16036,11 +16023,6 @@ OVS_WAIT_UNTIL([ + grep n_packets=1 -c) + test "0" = "${pkts_to_rtr2}" + ]) +-OVS_WAIT_UNTIL([ +- pkts_flooded=$(ovs-ofctl dump-flows br-int | \ +- grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) +- test "0" = "${pkts_flooded}" +-]) + + # Inject ND_NS for first router owned VIP address. + src_ipv6=00100000000000000000000000000254 +@@ -16063,11 +16045,6 @@ OVS_WAIT_UNTIL([ + grep n_packets=1 -c) + test "0" = "${pkts_to_rtr2}" + ]) +-OVS_WAIT_UNTIL([ +- pkts_flooded=$(ovs-ofctl dump-flows br-int | \ +- grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) +- test "0" = "${pkts_flooded}" +-]) + + # Configure NAT on both routers. + ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1 +@@ -16103,11 +16080,6 @@ OVS_WAIT_UNTIL([ + grep n_packets=1 -c) + test "0" = "${pkts_to_rtr2}" + ]) +-OVS_WAIT_UNTIL([ +- pkts_flooded=$(ovs-ofctl dump-flows br-int | \ +- grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) +- test "0" = "${pkts_flooded}" +-]) + + # Inject ARP request for FIP1. + send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121) +@@ -16170,11 +16142,6 @@ OVS_WAIT_UNTIL([ + grep n_packets=1 -c) + test "0" = "${pkts_to_rtr2}" + ]) +-OVS_WAIT_UNTIL([ +- pkts_flooded=$(ovs-ofctl dump-flows br-int | \ +- grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) +- test "0" = "${pkts_flooded}" +-]) + + # Inject ND_NS for FIP1. + src_ipv6=00100000000000000000000000000254 +-- +1.8.3.1 + diff --git a/SOURCES/0003-Learn-the-mac-binding-only-if-required.patch b/SOURCES/0003-Learn-the-mac-binding-only-if-required.patch new file mode 100644 index 0000000..0dc98a0 --- /dev/null +++ b/SOURCES/0003-Learn-the-mac-binding-only-if-required.patch @@ -0,0 +1,1548 @@ +From 036599bdfa4c10d69c1bcae407fb75ea841fa834 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Thu, 5 Sep 2019 00:15:38 +0530 +Subject: [PATCH 3/4] Learn the mac binding only if required + +OVN has the actions - put_arp and put_nd to learn the mac bindings from the +ARP/ND packets. These actions update the Southbound MAC_Binding table. +These actions translates to controller actions. Whenever pinctrl thread +receives such packets, it wakes up the main ovn-controller thread. +If the MAC_Binding table is already upto date, this results +in unnecessary CPU cyles. There are some security implications as well. +A rogue VM can flood broadcast ARP request/reply packets and this +could cause DoS issues. A physical switch may send periodic GARPs +and these packets hit ovn-controllers. + +This patch solves these problems by learning the mac bindings only if +required. There is no need to apply the put_arp/put_nd action if the +Southbound MAC_Binding row is upto date. + +New actions - lookup_arp and lookup_nd are added which looks up the +IP, MAC pair in the mac_binding table and stores the result in a +register. 1 if lookup is successful, 0 otherwise. + +ovn-northd adds 2 new stages - LOOKUP_NEIGHBOR and LEARN_NEIGHBOR before +IP_INPUT in the router ingress pipeline.c. The LOOKUP_NEIGHBOR stage +adds flows to do the lookup in the mac_binding table and the LEARN_NEIGHBOR +adds flows to learn the neighbors only if require. + +The lflow module of ovn-controller adds OF flows in table 67 (OFTABLE_MAC_LOOKUP) +for each mac_binding entry with the match reg0 = ip && eth.src = mac with +the action - load:1->reg10[6] + +Eg: +table=31, priority=100,arp,reg0=0xaca8006f,reg14=0x3,metadata=0x3,dl_src=00:44:00:00:00:04 + actions=load:1->NXM_NX_REG10[6] + +This patch should also address the issue reported in 'Reported-at' + +Reported-at: https://bugzilla.redhat.com/1729846 +Reported-by: Haidong Li +CC: Han ZHou +CC: Dumitru Ceara +Acked-by: Han ZHou +Tested-by: Dumitru Ceara +Signed-off-by: Numan Siddique +--- + include/ovn/actions.h | 13 ++ + include/ovn/logical-fields.h | 4 + + ovn/controller/lflow.c | 37 ++++- + ovn/controller/lflow.h | 1 + + ovn/lib/actions.c | 114 ++++++++++++++ + ovn/northd/ovn-northd.8.xml | 212 ++++++++++++++++--------- + ovn/northd/ovn-northd.c | 205 ++++++++++++++----------- + ovn/ovn-architecture.7.xml | 18 +++ + ovn/ovn-sb.xml | 57 +++++++ + ovn/utilities/ovn-trace.c | 69 +++++++++ + tests/ovn.at | 290 ++++++++++++++++++++++++++++++++++- + tests/test-ovn.c | 1 + + 12 files changed, 844 insertions(+), 177 deletions(-) + +diff --git a/include/ovn/actions.h b/include/ovn/actions.h +index 145f27f25..4e2f4d28d 100644 +--- a/include/ovn/actions.h ++++ b/include/ovn/actions.h +@@ -73,8 +73,10 @@ struct ovn_extend_table; + OVNACT(ND_NA_ROUTER, ovnact_nest) \ + OVNACT(GET_ARP, ovnact_get_mac_bind) \ + OVNACT(PUT_ARP, ovnact_put_mac_bind) \ ++ OVNACT(LOOKUP_ARP, ovnact_lookup_mac_bind) \ + OVNACT(GET_ND, ovnact_get_mac_bind) \ + OVNACT(PUT_ND, ovnact_put_mac_bind) \ ++ OVNACT(LOOKUP_ND, ovnact_lookup_mac_bind) \ + OVNACT(PUT_DHCPV4_OPTS, ovnact_put_opts) \ + OVNACT(PUT_DHCPV6_OPTS, ovnact_put_opts) \ + OVNACT(SET_QUEUE, ovnact_set_queue) \ +@@ -266,6 +268,15 @@ struct ovnact_put_mac_bind { + struct expr_field mac; /* 48-bit Ethernet address. */ + }; + ++/* OVNACT_LOOKUP_ARP, OVNACT_LOOKUP_ND. */ ++struct ovnact_lookup_mac_bind { ++ struct ovnact ovnact; ++ struct expr_field dst; /* 1-bit destination field. */ ++ struct expr_field port; /* Logical port name. */ ++ struct expr_field ip; /* 32-bit or 128-bit IP address. */ ++ struct expr_field mac; /* 48-bit Ethernet address. */ ++}; ++ + struct ovnact_gen_option { + const struct gen_opts_map *option; + struct expr_constant_set value; +@@ -628,6 +639,8 @@ struct ovnact_encode_params { + uint8_t output_ptable; /* OpenFlow table for 'output' to resubmit. */ + uint8_t mac_bind_ptable; /* OpenFlow table for 'get_arp'/'get_nd' to + resubmit. */ ++ uint8_t mac_lookup_ptable; /* OpenFlow table for ++ 'lookup_arp'/'lookup_nd' to resubmit. */ + }; + + void ovnacts_encode(const struct ovnact[], size_t ovnacts_len, +diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h +index 9bac8e027..9b7c34fb7 100644 +--- a/include/ovn/logical-fields.h ++++ b/include/ovn/logical-fields.h +@@ -56,6 +56,7 @@ enum mff_log_flags_bits { + MLF_FORCE_SNAT_FOR_LB_BIT = 3, + MLF_LOCAL_ONLY_BIT = 4, + MLF_NESTED_CONTAINER_BIT = 5, ++ MLF_LOOKUP_MAC_BIT = 6, + }; + + /* MFF_LOG_FLAGS_REG flag assignments */ +@@ -84,6 +85,9 @@ enum mff_log_flags { + + /* Indicate that a packet was received from a nested container. */ + MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT), ++ ++ /* Indicate that the lookup in the mac binding table was successful. */ ++ MLF_LOOKUP_MAC = (1 << MLF_LOOKUP_MAC_BIT), + }; + + /* OVN logical fields +diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c +index 1aafafb33..733564fd1 100644 +--- a/ovn/controller/lflow.c ++++ b/ovn/controller/lflow.c +@@ -687,6 +687,7 @@ consider_logical_flow( + .egress_ptable = OFTABLE_LOG_EGRESS_PIPELINE, + .output_ptable = output_ptable, + .mac_bind_ptable = OFTABLE_MAC_BINDING, ++ .mac_lookup_ptable = OFTABLE_MAC_LOOKUP, + }; + ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts); + ovnacts_free(ovnacts.data, ovnacts.size); +@@ -777,7 +778,9 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name, + return; + } + +- struct match match = MATCH_CATCHALL_INITIALIZER; ++ struct match get_arp_match = MATCH_CATCHALL_INITIALIZER; ++ struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER; ++ + if (strchr(b->ip, '.')) { + ovs_be32 ip; + if (!ip_parse(b->ip, &ip)) { +@@ -785,7 +788,9 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name, + VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip); + return; + } +- match_set_reg(&match, 0, ntohl(ip)); ++ match_set_reg(&get_arp_match, 0, ntohl(ip)); ++ match_set_reg(&lookup_arp_match, 0, ntohl(ip)); ++ match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP)); + } else { + struct in6_addr ip6; + if (!ipv6_parse(b->ip, &ip6)) { +@@ -795,17 +800,35 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name, + } + ovs_be128 value; + memcpy(&value, &ip6, sizeof(value)); +- match_set_xxreg(&match, 0, ntoh128(value)); ++ match_set_xxreg(&get_arp_match, 0, ntoh128(value)); ++ ++ match_set_xxreg(&lookup_arp_match, 0, ntoh128(value)); ++ match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_IPV6)); ++ match_set_nw_proto(&lookup_arp_match, 58); ++ match_set_icmp_code(&lookup_arp_match, 0); + } + +- match_set_metadata(&match, htonll(pb->datapath->tunnel_key)); +- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key); ++ match_set_metadata(&get_arp_match, htonll(pb->datapath->tunnel_key)); ++ match_set_reg(&get_arp_match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key); ++ ++ match_set_metadata(&lookup_arp_match, htonll(pb->datapath->tunnel_key)); ++ match_set_reg(&lookup_arp_match, MFF_LOG_INPORT - MFF_REG0, ++ pb->tunnel_key); + + uint64_t stub[1024 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); + put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts); +- ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts, +- &b->header_.uuid); ++ ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &get_arp_match, ++ &ofpacts, &b->header_.uuid); ++ ++ ofpbuf_clear(&ofpacts); ++ uint8_t value = 1; ++ put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, ++ &ofpacts); ++ match_set_dl_src(&lookup_arp_match, mac); ++ ofctrl_add_flow(flow_table, OFTABLE_MAC_LOOKUP, 100, 0, &lookup_arp_match, ++ &ofpacts, &b->header_.uuid); ++ + ofpbuf_uninit(&ofpacts); + } + +diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h +index 4e1086eb6..9cb2afb86 100644 +--- a/ovn/controller/lflow.h ++++ b/ovn/controller/lflow.h +@@ -65,6 +65,7 @@ struct uuid; + #define OFTABLE_SAVE_INPORT 64 + #define OFTABLE_LOG_TO_PHY 65 + #define OFTABLE_MAC_BINDING 66 ++#define OFTABLE_MAC_LOOKUP 67 + + /* The number of tables for the ingress and egress pipelines. */ + #define LOG_PIPELINE_LEN 24 +diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c +index 0bb76cb27..45f8715cf 100644 +--- a/ovn/lib/actions.c ++++ b/ovn/lib/actions.c +@@ -1607,6 +1607,112 @@ ovnact_put_mac_bind_free(struct ovnact_put_mac_bind *put_mac OVS_UNUSED) + { + } + ++static void format_lookup_mac(const struct ovnact_lookup_mac_bind *lookup_mac, ++ struct ds *s, const char *name) ++{ ++ expr_field_format(&lookup_mac->dst, s); ++ ds_put_format(s, " = %s(", name); ++ expr_field_format(&lookup_mac->port, s); ++ ds_put_cstr(s, ", "); ++ expr_field_format(&lookup_mac->ip, s); ++ ds_put_cstr(s, ", "); ++ expr_field_format(&lookup_mac->mac, s); ++ ds_put_cstr(s, ");"); ++} ++ ++static void ++format_LOOKUP_ARP(const struct ovnact_lookup_mac_bind *lookup_mac, ++ struct ds *s) ++{ ++ format_lookup_mac(lookup_mac, s, "lookup_arp"); ++} ++ ++static void ++format_LOOKUP_ND(const struct ovnact_lookup_mac_bind *lookup_mac, ++ struct ds *s) ++{ ++ format_lookup_mac(lookup_mac, s, "lookup_nd"); ++} ++ ++static void ++encode_lookup_mac(const struct ovnact_lookup_mac_bind *lookup_mac, ++ enum mf_field_id ip_field, ++ const struct ovnact_encode_params *ep, ++ struct ofpbuf *ofpacts) ++{ ++ const struct arg args[] = { ++ { expr_resolve_field(&lookup_mac->port), MFF_LOG_INPORT }, ++ { expr_resolve_field(&lookup_mac->ip), ip_field }, ++ { expr_resolve_field(&lookup_mac->mac), MFF_ETH_SRC}, ++ }; ++ ++ encode_setup_args(args, ARRAY_SIZE(args), ofpacts); ++ ++ struct mf_subfield dst = expr_resolve_field(&lookup_mac->dst); ++ ovs_assert(dst.field); ++ ++ put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, ofpacts); ++ emit_resubmit(ofpacts, ep->mac_lookup_ptable); ++ ++ struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts); ++ orm->dst = dst; ++ orm->src.field = mf_from_id(MFF_LOG_FLAGS); ++ orm->src.ofs = MLF_LOOKUP_MAC_BIT; ++ orm->src.n_bits = 1; ++ ++ encode_restore_args(args, ARRAY_SIZE(args), ofpacts); ++} ++ ++static void ++encode_LOOKUP_ARP(const struct ovnact_lookup_mac_bind *lookup_mac, ++ const struct ovnact_encode_params *ep, ++ struct ofpbuf *ofpacts) ++{ ++ encode_lookup_mac(lookup_mac, MFF_REG0, ep, ofpacts); ++} ++ ++static void ++encode_LOOKUP_ND(const struct ovnact_lookup_mac_bind *lookup_mac, ++ const struct ovnact_encode_params *ep, ++ struct ofpbuf *ofpacts) ++{ ++ encode_lookup_mac(lookup_mac, MFF_XXREG0, ep, ofpacts); ++} ++ ++static void ++parse_lookup_mac_bind(struct action_context *ctx, ++ const struct expr_field *dst, ++ int width, ++ struct ovnact_lookup_mac_bind *lookup_mac) ++{ ++ /* Validate that the destination is a 1-bit, modifiable field. */ ++ char *error = expr_type_check(dst, 1, true); ++ if (error) { ++ lexer_error(ctx->lexer, "%s", error); ++ free(error); ++ return; ++ } ++ ++ lexer_get(ctx->lexer); /* Skip lookup_arp/lookup_nd. */ ++ lexer_get(ctx->lexer); /* Skip '('. * */ ++ ++ action_parse_field(ctx, 0, false, &lookup_mac->port); ++ lexer_force_match(ctx->lexer, LEX_T_COMMA); ++ action_parse_field(ctx, width, false, &lookup_mac->ip); ++ lexer_force_match(ctx->lexer, LEX_T_COMMA); ++ action_parse_field(ctx, 48, false, &lookup_mac->mac); ++ lexer_force_match(ctx->lexer, LEX_T_RPAREN); ++ lookup_mac->dst = *dst; ++} ++ ++static void ++ovnact_lookup_mac_bind_free( ++ struct ovnact_lookup_mac_bind *lookup_mac OVS_UNUSED) ++{ ++ ++} ++ ++ + static void + parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o, + const struct hmap *gen_opts, const char *opts_type) +@@ -2722,6 +2828,14 @@ parse_set_action(struct action_context *ctx) + && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { + parse_check_pkt_larger(ctx, &lhs, + ovnact_put_CHECK_PKT_LARGER(ctx->ovnacts)); ++ } else if (!strcmp(ctx->lexer->token.s, "lookup_arp") ++ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { ++ parse_lookup_mac_bind(ctx, &lhs, 32, ++ ovnact_put_LOOKUP_ARP(ctx->ovnacts)); ++ } else if (!strcmp(ctx->lexer->token.s, "lookup_nd") ++ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { ++ parse_lookup_mac_bind(ctx, &lhs, 128, ++ ovnact_put_LOOKUP_ND(ctx->ovnacts)); + } else { + parse_assignment_action(ctx, false, &lhs); + } +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index ec2b6454c..ec9020d2a 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1223,7 +1223,126 @@ output; + Other packets are implicitly dropped. +

        + +-

        Ingress Table 1: IP Input

        ++

        Ingress Table 1: Neighbor lookup

        ++ ++

        ++ For ARP and IPv6 Neighbor Discovery packets, this table looks into the ++ records to determine ++ if OVN needs to learn the mac bindings. Following flows are added: ++

        ++ ++
          ++
        • ++

          ++ For each router port P that owns IP address A, ++ which belongs to subnet S with prefix length L, ++ a priority-100 flow is added which matches ++ inport == P && ++ arp.spa == S/L && arp.op == 1 ++ (ARP request) with the ++ following actions: ++

          ++ ++
          ++reg9[4] = lookup_arp(inport, arp.spa, arp.sha);
          ++next;
          ++        
          ++ ++

          ++ If the logical router port P is a distributed gateway ++ router port, additional match ++ is_chassis_resident(cr-P) is added so that ++ the resident gateway chassis handles the neighbor lookup. ++

          ++
        • ++ ++
        • ++

          ++ A priority-100 flow which matches on ARP reply packets and applies ++ the actions: ++

          ++ ++
          ++reg9[4] = lookup_arp(inport, arp.spa, arp.sha);
          ++next;
          ++        
          ++
        • ++ ++
        • ++

          ++ A priority-100 flow which matches on IPv6 Neighbor Discovery ++ advertisement packet and applies the actions: ++

          ++ ++
          ++reg9[4] = lookup_nd(inport, nd.target, nd.tll);
          ++next;
          ++        
          ++
        • ++ ++
        • ++

          ++ A priority-100 flow which matches on IPv6 Neighbor Discovery ++ solicitation packet and applies the actions: ++

          ++ ++
          ++reg9[4] = lookup_nd(inport, ip6.src, nd.sll);
          ++next;
          ++        
          ++
        • ++ ++
        • ++ A priority-0 fallback flow that matches all packets and applies ++ the action reg9[5] = 1; next; ++ advancing the packet to the next table. ++
        • ++
        ++ ++

        Ingress Table 2: Neighbor learning

        ++ ++

        ++ This table adds flows to learn the mac bindings from the ARP and ++ IPv6 Neighbor Solicitation/Advertisement packets if ARP/ND lookup ++ failed in the previous table. ++

        ++ ++

        ++ reg9[4] will be 1 if the lookup_arp/lookup_nd ++ in the previous table was successful. ++

        ++ ++

        ++ reg9[5] will be 1 if there was no need to do the lookup. ++

        ++ ++
          ++
        • ++ A priority-100 flow with the match ++ reg9[4] == 1 || reg9[5] == 1 and advances the packet ++ to the next table as there is no need to learn the neighbor. ++
        • ++ ++
        • ++ A priority-90 flow with the match arp and ++ applies the action ++ put_arp(inport, arp.spa, arp.sha); next; ++
        • ++ ++
        • ++ A priority-90 flow with the match nd_na and ++ applies the action ++ put_nd(inport, nd.target, nd.tll); next; ++
        • ++ ++
        • ++ A priority-90 flow with the match nd_ns and ++ applies the action ++ put_nd(inport, ip6.src, nd.sll); next; ++
        • ++
        ++ ++

        Ingress Table 3: IP Input

        + +

        + This table is the core of the logical router datapath functionality. It +@@ -1320,8 +1439,7 @@ next; +

        + +

        +- These flows reply to ARP requests for the router's own IP address +- and populates mac binding table of the logical router port. ++ These flows reply to ARP requests for the router's own IP address. + The ARP requests are handled only if the requestor's IP belongs + to the same subnets of the logical router port. + For each router port P that owns IP address A, +@@ -1334,7 +1452,6 @@ next; +

        + +
        +-put_arp(inport, arp.spa, arp.sha);
        + eth.dst = eth.src;
        + eth.src = E;
        + arp.op = 2; /* ARP reply. */
        +@@ -1370,17 +1487,6 @@ output;
        +         

        +
      • + +-
      • +-

        +- These flows handles ARP requests not for router's own IP address. +- They use the SPA and SHA to populate the logical router port's +- mac binding table, with priority 80. The typical use case of +- these flows are GARP requests handling. For the gateway port +- on a distributed logical router, these flows are only programmed +- on the gateway port instance on the redirect-chassis. +-

        +-
      • +- +
      • +

        + These flows reply to ARP requests for the virtual IP addresses +@@ -1451,36 +1557,6 @@ arp.sha = external_mac; +

      +
    • + +-
    • +-

      +- ARP reply handling. Following flows are added to handle ARP replies. +-

      +- +-

      +- For each distributed gateway logical router port a priority-92 flow +- with match inport == P && +- is_chassis_resident(cr-P) && eth.bcast && +- arp.op == 2 && arp.spa == I with the +- action put_arp(inport, arp.spa, arp.sha); so that the +- resident gateway chassis can learn the GARP reply, where +- P is the distributed gateway router port name, +- I is the logical router port's network address. +-

      +- +-

      +- For each distributed gateway logical router port a priority-92 flow +- with match inport == P && +- !is_chassis_resident(cr-P) && eth.bcast && +- arp.op == 2 && arp.spa == I with the action +- drop; so that other chassis drop this packet. +-

      +- +-

      +- A priority-90 flow with match arp.op == 2 has actions +- put_arp(inport, arp.spa, arp.sha);. +-

      +-
    • +- +
    • +

      + Reply to IPv6 Neighbor Solicitations. These flows reply to +@@ -1499,7 +1575,6 @@ arp.sha = external_mac; +

      + +
      +-put_nd(inport, ip6.src, nd.sll);
      + nd_na_router {
      +     eth.src = E;
      +     ip6.src = A;
      +@@ -1521,7 +1596,6 @@ nd_na_router {
      +         

      + +
      +-put_nd(inport, ip6.src, nd.sll);
      + nd_na {
      +     eth.src = E;
      +     ip6.src = A;
      +@@ -1546,20 +1620,8 @@ nd_na {
      +       
    • + +
    • +- IPv6 neighbor advertisement handling. This flow uses neighbor +- advertisements to populate the logical router's mac binding +- table. A priority-90 flow with match nd_na +- has actions put_nd(inport, nd.target, nd.tll);. +-
    • +- +-
    • +- IPv6 neighbor solicitation for non-hosted addresses handling. +- This flow uses neighbor solicitations to populate the logical +- router's mac binding table (ones that were directed at the +- logical router would have matched the priority-90 neighbor +- solicitation flow already). A priority-80 flow with match +- nd_ns has actions +- put_nd(inport, ip6.src, nd.sll);. ++ Priority-85 flows which drops the ARP and IPv6 Neighbor Discovery ++ packets. +
    • + +
    • +@@ -1675,7 +1737,7 @@ icmp6 { +
    • +
    + +-

    Ingress Table 2: DEFRAG

    ++

    Ingress Table 4: DEFRAG

    + +

    + This is to send packets to connection tracker for tracking and +@@ -1733,7 +1795,7 @@ icmp6 { +

  • + + +-

    Ingress Table 3: UNSNAT on Distributed Routers

    ++

    Ingress Table 5: UNSNAT on Distributed Routers

    + +
      +
    • +@@ -1772,7 +1834,7 @@ icmp6 { +
    • +
    + +-

    Ingress Table 4: DNAT

    ++

    Ingress Table 6: DNAT

    + +

    + Packets enter the pipeline with destination IP address that needs to +@@ -1780,7 +1842,7 @@ icmp6 { + in the reverse direction needs to be unDNATed. +

    + +-

    Ingress Table 4: Load balancing DNAT rules

    ++

    Ingress Table 6: Load balancing DNAT rules

    + +

    + Following load balancing DNAT flows are added for Gateway router or +@@ -1851,7 +1913,7 @@ icmp6 { + + + +-

    Ingress Table 4: DNAT on Gateway Routers

    ++

    Ingress Table 6: DNAT on Gateway Routers

    + +
      +
    • +@@ -1877,7 +1939,7 @@ icmp6 { +
    • +
    + +-

    Ingress Table 4: DNAT on Distributed Routers

    ++

    Ingress Table 6: DNAT on Distributed Routers

    + +

    + On distributed routers, the DNAT table only handles packets +@@ -1924,7 +1986,7 @@ icmp6 { + + + +-

    Ingress Table 5: IPv6 ND RA option processing

    ++

    Ingress Table 7: IPv6 ND RA option processing

    + +
      +
    • +@@ -1954,7 +2016,7 @@ reg0[5] = put_nd_ra_opts(options);next; +
    • +
    + +-

    Ingress Table 6: IPv6 ND RA responder

    ++

    Ingress Table 8: IPv6 ND RA responder

    + +

    + This table implements IPv6 ND RA responder for the IPv6 ND RA replies +@@ -1999,7 +2061,7 @@ output; + + + +-

    Ingress Table 7: IP Routing

    ++

    Ingress Table 9: IP Routing

    + +

    + A packet that arrives at this table is an IP packet that should be +@@ -2149,7 +2211,7 @@ next; + + + +-

    Ingress Table 8: ARP/ND Resolution

    ++

    Ingress Table 10: ARP/ND Resolution

    + +

    + Any packet that reaches this table is an IP packet whose next-hop +@@ -2284,7 +2346,7 @@ next; + + + +-

    Ingress Table 9: Check packet length

    ++

    Ingress Table 11: Check packet length

    + +

    + For distributed logical routers with distributed gateway port configured +@@ -2314,7 +2376,7 @@ REGBIT_PKT_LARGER = check_pkt_larger(L); next; + and advances to the next table. +

    + +-

    Ingress Table 10: Handle larger packets

    ++

    Ingress Table 12: Handle larger packets

    + +

    + For distributed logical routers with distributed gateway port configured +@@ -2363,7 +2425,7 @@ icmp4 { + and advances to the next table. +

    + +-

    Ingress Table 11: Gateway Redirect

    ++

    Ingress Table 13: Gateway Redirect

    + +

    + For distributed logical routers where one of the logical router +@@ -2425,7 +2487,7 @@ icmp4 { + + + +-

    Ingress Table 12: ARP Request

    ++

    Ingress Table 14: ARP Request

    + +

    + In the common case where the Ethernet destination has been resolved, this +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 83a7ec14f..2f6826f17 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -144,20 +144,22 @@ enum ovn_stage { + PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2, 9, "ls_out_port_sec_l2") \ + \ + /* Logical router ingress stages. */ \ +- PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \ +- PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "lr_in_ip_input") \ +- PIPELINE_STAGE(ROUTER, IN, DEFRAG, 2, "lr_in_defrag") \ +- PIPELINE_STAGE(ROUTER, IN, UNSNAT, 3, "lr_in_unsnat") \ +- PIPELINE_STAGE(ROUTER, IN, DNAT, 4, "lr_in_dnat") \ +- PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 5, "lr_in_nd_ra_options") \ +- PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 6, "lr_in_nd_ra_response") \ +- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 7, "lr_in_ip_routing") \ +- PIPELINE_STAGE(ROUTER, IN, POLICY, 8, "lr_in_policy") \ +- PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 9, "lr_in_arp_resolve") \ +- PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 10, "lr_in_chk_pkt_len") \ +- PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 11,"lr_in_larger_pkts") \ +- PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 12, "lr_in_gw_redirect") \ +- PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 13, "lr_in_arp_request") \ ++ PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \ ++ PIPELINE_STAGE(ROUTER, IN, LOOKUP_NEIGHBOR, 1, "lr_in_lookup_neighbor") \ ++ PIPELINE_STAGE(ROUTER, IN, LEARN_NEIGHBOR, 2, "lr_in_learn_neighbor") \ ++ PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") \ ++ PIPELINE_STAGE(ROUTER, IN, DEFRAG, 4, "lr_in_defrag") \ ++ PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \ ++ PIPELINE_STAGE(ROUTER, IN, DNAT, 6, "lr_in_dnat") \ ++ PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 7, "lr_in_nd_ra_options") \ ++ PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 8, "lr_in_nd_ra_response") \ ++ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 9, "lr_in_ip_routing") \ ++ PIPELINE_STAGE(ROUTER, IN, POLICY, 10, "lr_in_policy") \ ++ PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 11, "lr_in_arp_resolve") \ ++ PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 12, "lr_in_chk_pkt_len") \ ++ PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 13,"lr_in_larger_pkts") \ ++ PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 14, "lr_in_gw_redirect") \ ++ PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 15, "lr_in_arp_request") \ + \ + /* Logical router egress stages. */ \ + PIPELINE_STAGE(ROUTER, OUT, UNDNAT, 0, "lr_out_undnat") \ +@@ -196,6 +198,8 @@ enum ovn_stage { + #define REGBIT_DISTRIBUTED_NAT "reg9[2]" + /* Register to store the result of check_pkt_larger action. */ + #define REGBIT_PKT_LARGER "reg9[3]" ++#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]" ++#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]" + + /* Returns an "enum ovn_stage" built from the arguments. */ + static enum ovn_stage +@@ -6420,7 +6424,96 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_cstr(&match), "next;"); + } + +- /* Logical router ingress table 1: IP Input. */ ++ /* Logical router ingress table 1: LOOKUP_NEIGHBOR and ++ * table 2: LEARN_NEIGHBOR. */ ++ HMAP_FOR_EACH (od, key_node, datapaths) { ++ if (!od->nbr) { ++ continue; ++ } ++ ++ /* Learn MAC bindings from ARP/IPv6 ND. ++ * ++ * For ARP packets, table LOOKUP_NEIGHBOR does a lookup for the ++ * (arp.spa, arp.sha) in the mac binding table using the 'lookup_arp' ++ * action and stores the result in REGBIT_LOOKUP_NEIGHBOR_RESULT bit. ++ * ++ * For IPv6 ND NA packets, table LOOKUP_NEIGHBOR does a lookup ++ * for the (nd.target, nd.tll) in the mac binding table using the ++ * 'lookup_nd' action and stores the result in ++ * REGBIT_LOOKUP_NEIGHBOR_RESULT bit. ++ * ++ * For IPv6 ND NS packets, table LOOKUP_NEIGHBOR does a lookup ++ * for the (ip6.src, nd.sll) in the mac binding table using the ++ * 'lookup_nd' action and stores the result in ++ * REGBIT_LOOKUP_NEIGHBOR_RESULT bit. ++ * ++ * Table LEARN_NEIGHBOR learns the mac-binding using the action ++ * - 'put_arp/put_nd' only if REGBIT_LOOKUP_NEIGHBOR_RESULT bit ++ * is not set. ++ * ++ * */ ++ ++ /* Flows for LOOKUP_NEIGHBOR. */ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, ++ "arp.op == 2", ++ REGBIT_LOOKUP_NEIGHBOR_RESULT" = " ++ "lookup_arp(inport, arp.spa, arp.sha); next;"); ++ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_na", ++ REGBIT_LOOKUP_NEIGHBOR_RESULT" = " ++ "lookup_nd(inport, nd.target, nd.tll); next;"); ++ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_ns", ++ REGBIT_LOOKUP_NEIGHBOR_RESULT" = " ++ "lookup_nd(inport, ip6.src, nd.sll); next;"); ++ ++ /* For other packet types, we can skip neighbor learning. ++ * So set REGBIT_SKIP_LOOKUP_NEIGHBOR to 1. */ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 0, "1", ++ REGBIT_SKIP_LOOKUP_NEIGHBOR" = 1; next;"); ++ ++ /* Flows for LEARN_NEIGHBOR. */ ++ /* Skip Neighbor learning if not required. */ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100, ++ REGBIT_SKIP_LOOKUP_NEIGHBOR" == 1 || " ++ REGBIT_LOOKUP_NEIGHBOR_RESULT" == 1", "next;"); ++ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, ++ "arp", "put_arp(inport, arp.spa, arp.sha); next;"); ++ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, ++ "nd_na", "put_nd(inport, nd.target, nd.tll); next;"); ++ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, ++ "nd_ns", "put_nd(inport, ip6.src, nd.sll); next;"); ++ } ++ ++ HMAP_FOR_EACH (op, key_node, ports) { ++ if (!op->nbrp) { ++ continue; ++ } ++ ++ /* Check if we need to learn mac-binding from ARP requests. */ ++ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { ++ ds_clear(&match); ++ ds_put_format(&match, ++ "inport == %s && arp.spa == %s/%u && arp.op == 1", ++ op->json_key, ++ op->lrp_networks.ipv4_addrs[i].network_s, ++ op->lrp_networks.ipv4_addrs[i].plen); ++ if (op->od->l3dgw_port && op == op->od->l3dgw_port ++ && op->od->l3redirect_port) { ++ ds_put_format(&match, " && is_chassis_resident(%s)", ++ op->od->l3redirect_port->json_key); ++ } ++ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, ++ ds_cstr(&match), ++ REGBIT_LOOKUP_NEIGHBOR_RESULT" = " ++ "lookup_arp(inport, arp.spa, arp.sha); next;"); ++ } ++ } ++ ++ /* Logical router ingress table 3: IP Input. */ + HMAP_FOR_EACH (od, key_node, datapaths) { + if (!od->nbr) { + continue; +@@ -6442,10 +6535,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 95, "ip4.mcast", + od->mcast_info.rtr.relay ? "next;" : "drop;"); + +- /* ARP reply handling. Use ARP replies to populate the logical +- * router's ARP table. */ +- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "arp.op == 2", +- "put_arp(inport, arp.spa, arp.sha);"); ++ /* Drop ARP packets (priority 85). ARP request packets for router's own ++ * IPs are handled with priority-90 flows. ++ * Drop IPv6 ND packets (priority 85). ND NA packets for router's own ++ * IPs are handled with priority-90 flows. ++ */ ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 85, ++ "arp || nd", "drop;"); + + /* Drop Ethernet local broadcast. By definition this traffic should + * not be forwarded.*/ +@@ -6458,23 +6554,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30, + ds_cstr(&match), "drop;"); + +- /* ND advertisement handling. Use advertisements to populate +- * the logical router's ARP/ND table. */ +- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "nd_na", +- "put_nd(inport, nd.target, nd.tll);"); +- +- /* Lean from neighbor solicitations that were not directed at +- * us. (A priority-90 flow will respond to requests to us and +- * learn the sender's mac address. */ +- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 80, "nd_ns", +- "put_nd(inport, ip6.src, nd.sll);"); +- + /* Pass other traffic not already handled to the next table for + * routing. */ + ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;"); + } + +- /* Logical router ingress table 1: IP Input for IPv4. */ ++ /* Logical router ingress table 3: IP Input for IPv4. */ + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbrp) { + continue; +@@ -6584,7 +6669,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + ds_clear(&actions); + ds_put_format(&actions, +- "put_arp(inport, arp.spa, arp.sha); " + "eth.dst = eth.src; " + "eth.src = %s; " + "arp.op = 2; /* ARP reply */ " +@@ -6603,62 +6687,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_cstr(&match), ds_cstr(&actions)); + } + +- /* Learn from ARP requests that were not directed at us. A typical +- * use case is GARP request handling. (A priority-90 flow will +- * respond to request to us and learn the sender's mac address.) */ +- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { +- ds_clear(&match); +- ds_put_format(&match, +- "inport == %s && arp.spa == %s/%u && arp.op == 1", +- op->json_key, +- op->lrp_networks.ipv4_addrs[i].network_s, +- op->lrp_networks.ipv4_addrs[i].plen); +- if (op->od->l3dgw_port && op == op->od->l3dgw_port +- && op->od->l3redirect_port) { +- ds_put_format(&match, " && is_chassis_resident(%s)", +- op->od->l3redirect_port->json_key); +- } +- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80, +- ds_cstr(&match), +- "put_arp(inport, arp.spa, arp.sha);"); +- +- } +- +- /* Handle GARP reply packets received on a distributed router gateway +- * port. GARP reply broadcast packets could be sent by external +- * switches. We don't want them to be handled by all the +- * ovn-controllers if they receive it. So add a priority-92 flow to +- * apply the put_arp action on a redirect chassis and drop it on +- * other chassis. +- * Note that we are already adding a priority-90 logical flow in the +- * table S_ROUTER_IN_IP_INPUT to apply the put_arp action if +- * arp.op == 2. +- * */ +- if (op->od->l3dgw_port && op == op->od->l3dgw_port +- && op->od->l3redirect_port) { +- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { +- ds_clear(&match); +- ds_put_format(&match, +- "inport == %s && is_chassis_resident(%s) && " +- "eth.bcast && arp.op == 2 && arp.spa == %s/%u", +- op->json_key, op->od->l3redirect_port->json_key, +- op->lrp_networks.ipv4_addrs[i].network_s, +- op->lrp_networks.ipv4_addrs[i].plen); +- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92, +- ds_cstr(&match), +- "put_arp(inport, arp.spa, arp.sha);"); +- ds_clear(&match); +- ds_put_format(&match, +- "inport == %s && !is_chassis_resident(%s) && " +- "eth.bcast && arp.op == 2 && arp.spa == %s/%u", +- op->json_key, op->od->l3redirect_port->json_key, +- op->lrp_networks.ipv4_addrs[i].network_s, +- op->lrp_networks.ipv4_addrs[i].plen); +- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92, +- ds_cstr(&match), "drop;"); +- } +- } +- + /* A set to hold all load-balancer vips that need ARP responses. */ + struct sset all_ips = SSET_INITIALIZER(&all_ips); + int addr_family; +@@ -6969,7 +6997,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + ds_clear(&actions); + ds_put_format(&actions, +- "put_nd(inport, ip6.src, nd.sll); " + "nd_na_router { " + "eth.src = %s; " + "ip6.src = %s; " +diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml +index c4099f25a..2a375c8b3 100644 +--- a/ovn/ovn-architecture.7.xml ++++ b/ovn/ovn-architecture.7.xml +@@ -970,6 +970,24 @@ + this temporary use.) +

    + ++ ++
    R = lookup_arp(P, A, M);
    ++
    R = lookup_nd(P, A, M);
    ++
    ++

    ++ Implemented by storing arguments into OpenFlow fields, then ++ resubmitting to table 67, which ovn-controller ++ populates with flows generated from the MAC_Binding ++ table in the OVN Southbound database. If there is a match in table ++ 67, then its actions set the logical flow flag MLF_LOOKUP_MAC. ++

    ++ ++

    ++ (The OpenFlow actions save and restore the OpenFlow fields used for ++ the arguments, so that the OVN actions do not have to be aware of ++ this temporary use.) ++

    ++
    + + + +diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml +index 477e7bc7a..e5fb51a9d 100644 +--- a/ovn/ovn-sb.xml ++++ b/ovn/ovn-sb.xml +@@ -1397,6 +1397,35 @@ +

    Example: put_arp(inport, arp.spa, arp.sha);

    + + ++
    ++ R = lookup_arp(P, A, M); ++
    ++ ++
    ++

    ++ Parameters: logical port string field P, 32-bit ++ IP address field A, 48-bit MAC address field ++ M. ++

    ++ ++

    ++ Result: stored to a 1-bit subfield R. ++

    ++ ++

    ++ Looks up A and M in P's mac ++ binding table. If an entry is found, stores 1 in ++ the 1-bit subfield R, else 0. ++

    ++ ++

    ++ Example: ++ ++ reg0[0] = lookup_arp(inport, arp.spa, arp.sha); ++ ++

    ++
    ++ +
    nd_ns { action; ... };
    +
    +

    +@@ -1553,6 +1582,34 @@ +

    Example: put_nd(inport, nd.target, nd.tll);

    +
    + ++
    R = lookup_nd(P, A, M); ++
    ++ ++
    ++

    ++ Parameters: logical port string field P, 128-bit ++ IP address field A, 48-bit MAC address field ++ M. ++

    ++ ++

    ++ Result: stored to a 1-bit subfield R. ++

    ++ ++

    ++ Looks up A and M in P's mac ++ binding table. If an entry is found, stores 1 in ++ the 1-bit subfield R, else 0. ++

    ++ ++

    ++ Example: ++ ++ reg0[0] = lookup_nd(inport, ip6.src, eth.src); ++ ++

    ++
    ++ +
    + R = put_dhcp_opts(D1 = V1, D2 = V2, ..., Dn = Vn); +
    +diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c +index b532b8eaf..103b25891 100644 +--- a/ovn/utilities/ovn-trace.c ++++ b/ovn/utilities/ovn-trace.c +@@ -556,6 +556,22 @@ ovntrace_mac_binding_find(const struct ovntrace_datapath *dp, + return NULL; + } + ++static const struct ovntrace_mac_binding * ++ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp, ++ uint16_t port_key, const struct in6_addr *ip, ++ struct eth_addr mac) ++{ ++ const struct ovntrace_mac_binding *bind; ++ HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key, ip), ++ &dp->mac_bindings) { ++ if (bind->port_key == port_key && ipv6_addr_equals(ip, &bind->ip) ++ && eth_addr_equals(bind->mac, mac)) { ++ return bind; ++ } ++ } ++ return NULL; ++} ++ + /* If 's' ends with a UUID, returns a copy of it with the UUID truncated to + * just the first 6 characters; otherwise, returns a copy of 's'. */ + static char * +@@ -1704,6 +1720,51 @@ execute_get_mac_bind(const struct ovnact_get_mac_bind *bind, + ETH_ADDR_ARGS(uflow->dl_dst)); + } + ++static void ++execute_lookup_mac(const struct ovnact_lookup_mac_bind *bind OVS_UNUSED, ++ const struct ovntrace_datapath *dp OVS_UNUSED, ++ struct flow *uflow OVS_UNUSED, ++ struct ovs_list *super OVS_UNUSED) ++{ ++ /* Get logical port number.*/ ++ struct mf_subfield port_sf = expr_resolve_field(&bind->port); ++ ovs_assert(port_sf.n_bits == 32); ++ uint32_t port_key = mf_get_subfield(&port_sf, uflow); ++ ++ /* Get IP address. */ ++ struct mf_subfield ip_sf = expr_resolve_field(&bind->ip); ++ ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128); ++ union mf_subvalue ip_sv; ++ mf_read_subfield(&ip_sf, uflow, &ip_sv); ++ struct in6_addr ip = (ip_sf.n_bits == 32 ++ ? in6_addr_mapped_ipv4(ip_sv.ipv4) ++ : ip_sv.ipv6); ++ ++ /* Get MAC. */ ++ struct mf_subfield mac_sf = expr_resolve_field(&bind->mac); ++ ovs_assert(mac_sf.n_bits == 48); ++ union mf_subvalue mac_sv; ++ mf_read_subfield(&mac_sf, uflow, &mac_sv); ++ ++ const struct ovntrace_mac_binding *binding ++ = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, mac_sv.mac); ++ ++ struct mf_subfield dst = expr_resolve_field(&bind->dst); ++ uint8_t val = 0; ++ ++ if (binding) { ++ val = 1; ++ ovntrace_node_append(super, OVNTRACE_NODE_ACTION, ++ "/* MAC binding to "ETH_ADDR_FMT" found. */", ++ ETH_ADDR_ARGS(uflow->dl_dst)); ++ } else { ++ ovntrace_node_append(super, OVNTRACE_NODE_ACTION, ++ "/* lookup failed - No MAC binding. */"); ++ } ++ union mf_subvalue sv = { .u8_val = val }; ++ mf_write_subfield_flow(&dst, &sv, uflow); ++} ++ + static void + execute_put_opts(const struct ovnact_put_opts *po, + const char *name, struct flow *uflow, +@@ -2072,6 +2133,14 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, + /* Nothing to do for tracing. */ + break; + ++ case OVNACT_LOOKUP_ARP: ++ execute_lookup_mac(ovnact_get_LOOKUP_ARP(a), dp, uflow, super); ++ break; ++ ++ case OVNACT_LOOKUP_ND: ++ execute_lookup_mac(ovnact_get_LOOKUP_ND(a), dp, uflow, super); ++ break; ++ + case OVNACT_PUT_DHCPV4_OPTS: + execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a), + "put_dhcp_opts", uflow, super); +diff --git a/tests/ovn.at b/tests/ovn.at +index df41a7549..d52c97541 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -2231,6 +2231,33 @@ put_arp(inport, arp.spa, arp.sha); + encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.01.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[] + has prereqs eth.type == 0x806 && eth.type == 0x806 + ++# lookup_arp ++reg0[0] = lookup_arp(inport, ip4.dst, eth.src); ++ encodes as push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],pop:NXM_NX_REG0[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[96],pop:NXM_NX_REG0[] ++ has prereqs eth.type == 0x800 ++reg1[1] = lookup_arp(inport, arp.spa, arp.sha); ++ encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[65],pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[] ++ has prereqs eth.type == 0x806 && eth.type == 0x806 ++ ++lookup_arp; ++ Syntax error at `lookup_arp' expecting action. ++reg0[0] = lookup_arp; ++ Syntax error at `lookup_arp' expecting field name. ++reg0[0] = lookup_arp(); ++ Syntax error at `)' expecting field name. ++reg0[0] = lookup_arp(inport); ++ Syntax error at `)' expecting `,'. ++reg0[0] = lookup_arp(inport ip4.dst); ++ Syntax error at `ip4.dst' expecting `,'. ++reg0[0] = lookup_arp(inport, ip4.dst; ++ Syntax error at `;' expecting `,'. ++reg0[0] = lookup_arp(inport, ip4.dst, eth.src; ++ Syntax error at `;' expecting `)'. ++reg0[0] = lookup_arp(inport, eth.dst); ++ Cannot use 48-bit field eth.dst[0..47] where 32-bit field is required. ++reg0[0] = lookup_arp(inport, ip4.src, ip4.dst); ++ Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is required. ++ + # put_dhcp_opts + reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1); + encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause) +@@ -2331,6 +2358,35 @@ reg1[0] = put_dhcpv6_opts(ia_addr="ae70::4"); + reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, domain_search=ae70::1); + DHCPv6 option domain_search requires string value. + ++# lookup_nd ++reg2[0] = lookup_nd(inport, ip6.dst, eth.src); ++ encodes as push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[32],pop:NXM_NX_XXREG0[] ++ has prereqs eth.type == 0x86dd ++reg3[0] = lookup_nd(inport, nd.target, nd.tll); ++ encodes as push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_TLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[0],pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[] ++ has prereqs (icmp6.type == 0x87 || icmp6.type == 0x88) && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) ++ ++lookup_nd; ++ Syntax error at `lookup_nd' expecting action. ++reg0[0] = lookup_nd; ++ Syntax error at `lookup_nd' expecting field name. ++reg0[0] = lookup_nd(); ++ Syntax error at `)' expecting field name. ++reg0[0] = lookup_nd(inport); ++ Syntax error at `)' expecting `,'. ++reg0[0] = lookup_nd(inport ip6.dst); ++ Syntax error at `ip6.dst' expecting `,'. ++reg0[0] = lookup_nd(inport, ip6.dst; ++ Syntax error at `;' expecting `,'. ++reg0[0] = lookup_nd(inport, ip6.dst, eth.src; ++ Syntax error at `;' expecting `)'. ++reg0[0] = lookup_nd(inport, eth.dst); ++ Cannot use 48-bit field eth.dst[0..47] where 128-bit field is required. ++reg0[0] = lookup_nd(inport, ip4.src, ip4.dst); ++ Cannot use 32-bit field ip4.src[0..31] where 128-bit field is required. ++reg0[0] = lookup_nd(inport, ip6.src, ip6.dst); ++ Cannot use 128-bit field ip6.dst[0..127] where 48-bit field is required. ++ + # set_queue + set_queue(0); + encodes as set_queue:0 +@@ -15608,7 +15664,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ + # Since the sw0-vir is not claimed by any chassis, eth.dst should be set to + # zero if the ip4.dst is the virtual ip in the router pipeline. + AT_CHECK([cat lflows.txt], [0], [dnl +- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) ++ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) + ]) + + ip_to_hex() { +@@ -15644,7 +15700,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ + # There should be an arp resolve flow to resolve the virtual_ip with the + # sw0-p1's MAC. + AT_CHECK([cat lflows.txt], [0], [dnl +- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) ++ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) + ]) + + # send the garp from sw0-p2 (in hv2). hv2 should claim sw0-vir +@@ -15667,7 +15723,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ + # There should be an arp resolve flow to resolve the virtual_ip with the + # sw0-p2's MAC. + AT_CHECK([cat lflows.txt], [0], [dnl +- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) ++ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) + ]) + + # Now send arp reply from sw0-p1. hv1 should claim sw0-vir +@@ -15688,7 +15744,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ + > lflows.txt + + AT_CHECK([cat lflows.txt], [0], [dnl +- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) ++ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;) + ]) + + # Delete hv1-vif1 port. hv1 should release sw0-vir +@@ -15706,7 +15762,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ + > lflows.txt + + AT_CHECK([cat lflows.txt], [0], [dnl +- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) ++ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;) + ]) + + # Now send arp reply from sw0-p2. hv2 should claim sw0-vir +@@ -15727,7 +15783,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \ + > lflows.txt + + AT_CHECK([cat lflows.txt], [0], [dnl +- table=9 (lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) ++ table=11(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;) + ]) + + # Delete sw0-p2 logical port +@@ -16332,3 +16388,225 @@ OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty]) + + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP ++ ++AT_SETUP([ovn -- ARP lookup before learning]) ++AT_KEYWORDS([virtual ports]) ++AT_SKIP_IF([test $HAVE_PYTHON = no]) ++ovn_start ++ ++send_garp() { ++ local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6 ++ local request=${eth_dst}${eth_src}08060001080006040001${eth_src}${spa}${eth_dst}${tpa} ++ as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request ++} ++ ++send_arp_reply() { ++ local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6 ++ local request=${eth_dst}${eth_src}08060001080006040002${eth_src}${spa}${eth_dst}${tpa} ++ as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request ++} ++ ++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 -- add-port br-int hv1-vif1 -- \ ++ set interface hv1-vif1 external-ids:iface-id=sw0-p1 \ ++ options:tx_pcap=hv1/vif1-tx.pcap \ ++ options:rxq_pcap=hv1/vif1-rx.pcap \ ++ ofport-request=1 ++ovs-vsctl -- add-port br-int hv1-vif2 -- \ ++ set interface hv1-vif2 external-ids:iface-id=sw0-p3 \ ++ options:tx_pcap=hv1/vif2-tx.pcap \ ++ options:rxq_pcap=hv1/vif2-rx.pcap \ ++ ofport-request=2 ++ ++sim_add hv2 ++as hv2 ++ovs-vsctl add-br br-phys ++ovn_attach n1 br-phys 192.168.0.2 ++ovs-vsctl -- add-port br-int hv2-vif1 -- \ ++ set interface hv2-vif1 external-ids:iface-id=sw1-p1 \ ++ options:tx_pcap=hv2/vif1-tx.pcap \ ++ options:rxq_pcap=hv2/vif1-rx.pcap \ ++ ofport-request=1 ++ ++ovn-nbctl ls-add sw0 ++ ++ovn-nbctl lsp-add sw0 sw0-p1 ++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03" ++ ++# Create the second logical switch with one port ++ovn-nbctl ls-add sw1 ++ovn-nbctl lsp-add sw1 sw1-p1 ++ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3" ++ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3" ++ ++# Create a logical router and attach both logical switches ++ovn-nbctl lr-add lr0 ++ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 ++ovn-nbctl lsp-add sw0 sw0-lr0 ++ovn-nbctl lsp-set-type sw0-lr0 router ++ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01 ++ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 ++ ++ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 ++ovn-nbctl lsp-add sw1 sw1-lr0 ++ovn-nbctl lsp-set-type sw1-lr0 router ++ovn-nbctl lsp-set-addresses sw1-lr0 00:00:00:00:ff:02 ++ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 ++ ++OVN_POPULATE_ARP ++ovn-nbctl --wait=hv sync ++ ++as hv1 ovs-appctl -t ovn-controller vlog/set dbg ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++# From sw0-p1 send GARP for 10.0.0.30. ++# ovn-controller should learn the ++# mac_binding entry ++# port - lr0-sw0 ++# ip - 10.0.0.30 ++# mac - 50:54:00:00:00:03 ++ ++AT_CHECK([test 0 = `ovn-sbctl list mac_binding | wc -l`]) ++eth_src=505400000003 ++eth_dst=ffffffffffff ++spa=$(ip_to_hex 10 0 0 30) ++tpa=$(ip_to_hex 10 0 0 30) ++send_garp 1 1 $eth_src $eth_dst $spa $tpa ++ ++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`]) ++ ++AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \ ++list mac_binding], [0], [lr0-sw0 ++10.0.0.30 ++50:54:00:00:00:03 ++]) ++ ++AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \ ++grep controller | grep -v n_packets=0 | wc -l`]) ++ ++# Wait for an entry in table=67 ++OVS_WAIT_UNTIL( ++ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep n_packets=0 \ ++| wc -l`] ++) ++ ++# Send garp again. This time the packet should not be sent to ovn-controller. ++send_garp 1 1 $eth_src $eth_dst $spa $tpa ++# Wait for an entry in table=67 ++OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep n_packets=1 | wc -l`]) ++ ++# The packet should not be sent to ovn-controller. The packet ++count should be 1 only. ++AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \ ++grep controller | grep -v n_packets=0 | wc -l`]) ++ ++# Now send garp packet with different mac. ++eth_src=505400000013 ++eth_dst=ffffffffffff ++spa=$(ip_to_hex 10 0 0 30) ++tpa=$(ip_to_hex 10 0 0 30) ++send_garp 1 1 $eth_src $eth_dst $spa $tpa ++ ++# The garp packet should be sent to ovn-controller and the mac_binding entry ++# should be updated. ++OVS_WAIT_UNTIL([test 2 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++ ++AT_CHECK([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`]) ++ ++AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \ ++list mac_binding], [0], [lr0-sw0 ++10.0.0.30 ++50:54:00:00:00:13 ++]) ++ ++# Send ARP request to lrp - lr0-sw1 (20.0.0.1) using src mac 50:54:00:00:00:33 ++# and src ip - 10.0.0.50.from sw0-p1. ++# ovn-controller should add the mac_binding entry ++# logical_port - lr0 ++# IP - 10.0.0.50 ++# MAC - 50:54:00:00:00:33 ++eth_src=505400000033 ++eth_dst=ffffffffffff ++spa=$(ip_to_hex 10 0 0 50) ++tpa=$(ip_to_hex 20 0 0 1) ++ ++send_garp 1 1 $eth_src $eth_dst $spa $tpa ++ ++# The garp packet should be sent to ovn-controller and the mac_binding entry ++# should be updated. ++OVS_WAIT_UNTIL([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++ ++OVS_WAIT_UNTIL( ++ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:33 \ ++| wc -l`] ++) ++ ++AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \ ++find mac_binding ip=10.0.0.50], [0], [lr0-sw0 ++10.0.0.50 ++50:54:00:00:00:33 ++]) ++ ++# Send the same packet again. ++send_garp 1 1 $eth_src $eth_dst $spa $tpa ++ ++OVS_WAIT_UNTIL( ++ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:33 \ ++| grep n_packets=1 | wc -l`] ++) ++ ++AT_CHECK([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++ ++# Now send ARP reply packet with IP - 10.0.0.40 and mac 505400000023 ++eth_src=505400000023 ++eth_dst=ffffffffffff ++spa=$(ip_to_hex 10 0 0 40) ++tpa=$(ip_to_hex 10 0 0 50) ++send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa ++ ++# ovn-controller should add the ++# mac_binding entry ++# port - lr0-sw0 ++# ip - 10.0.0.40 ++# mac - 50:54:00:00:00:23 ++ ++# The garp packet should be sent to ovn-controller and the mac_binding entry ++# should be updated. ++OVS_WAIT_UNTIL([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++ ++# Wait for an entry in table=67 for the learnt mac_binding entry. ++ ++OVS_WAIT_UNTIL( ++ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \ ++| wc -l`] ++) ++ ++# Send the same garp reply. This time it should not be sent to ovn-controller. ++send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa ++OVS_WAIT_UNTIL( ++ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \ ++| grep n_packets=1 | wc -l`] ++) ++ ++AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++ ++send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa ++OVS_WAIT_UNTIL( ++ [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \ ++| grep n_packets=2 | wc -l`] ++) ++ ++AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`]) ++ ++OVN_CLEANUP([hv1], [hv2]) ++AT_CLEANUP +diff --git a/tests/test-ovn.c b/tests/test-ovn.c +index cf1bc5432..a7adc1c9a 100644 +--- a/tests/test-ovn.c ++++ b/tests/test-ovn.c +@@ -1297,6 +1297,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED) + .egress_ptable = 40, + .output_ptable = 64, + .mac_bind_ptable = 65, ++ .mac_lookup_ptable = 67, + }; + struct ofpbuf ofpacts; + ofpbuf_init(&ofpacts, 0); +-- +2.21.0 + diff --git a/SOURCES/0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch b/SOURCES/0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch new file mode 100644 index 0000000..75b8fdf --- /dev/null +++ b/SOURCES/0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch @@ -0,0 +1,116 @@ +From e00d6083ad1ee972ec3705401dc9c8d43cd43f7d Mon Sep 17 00:00:00 2001 +From: Ankur Sharma +Date: Wed, 28 Aug 2019 01:55:29 +0000 +Subject: [PATCH 03/12] OVN: Vlan backed DVR N-S, avoid get_arp on non redirect + chassis. + +Background: +With 795d7f24ce0e ("OVN: Enable E-W Traffic, Vlan backed DVR"), we have added +support for E-W workflow for vlan backed DVRs. + +This series enables N-S workflow for vlan backed DVRs. + +Key difference between E-W and N-S traffic flow is that +N-S flow requires a gateway chassis. A gateway chassis +will be respondible for following: +a. Doing Network Address Translation (NAT). +b. Becoming entry and exit point for North->South + and South->North traffic respectively. + +OVN by default always uses overlay encapsulation to redirect +the packet to gateway chassis. This series will enable +the redirection to gateway chassis in the absence of encapsulation. + +This patch: +a. Make sure that ARP request for endpoint behind the gateway + router port is sent from gateway chassis only and not from + host(compute) chassis. + +b. This is achieved by adding a new logical flow in + lr_in_arp_resolve at priority=50. + +c. This flow run on non gateway chassis and sets the destination + mac to router port mac, if outport is a gateway chassis attached + router port and redirect-type is set as "bridged". + Example logical flow: + table=9 (lr_in_arp_resolve ), priority=50 , match=(outport == "router-to-underlay" && !is_chassis_resident("cr-router-to-underlay")), action=(eth.dst = 00:00:01:01:02:04; next;) + +d. This change is needed because other wise for non resolved ARPs, + we will end up doing get_arp in host chassis. Doing so will + have following issues: + i. We want all the interation with North bound endpoints via + gateway chassis only, doing so on host chassis will violate + that. + + ii. With get_arp, ovn-controller will generate the ARP using router + port's mac as source mac, which will lead us to the same issue, + where router port mac will be going through continous mac moves + in physical network. Worst, it would affect the redirection, + since it uses router port mac as destination mac. + +Signed-off-by: Ankur Sharma +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 12 ++++++++++++ + ovn/northd/ovn-northd.c | 22 ++++++++++++++++++++++ + 2 files changed, 34 insertions(+) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index dc5fdf471..83c90d680 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -2422,6 +2422,18 @@ next; + get_nd(outport, xxreg0); next;. +

    + ++ ++
  • ++

    ++ For logical router port with redirect-chassis and redirect-type ++ being set as bridged, a priority-50 flow will match ++ outport == "ROUTER_PORT" and !is_chassis_resident ++ ("cr-ROUTER_PORT") has actions eth.dst = E; ++ next;, where E is the ethernet address of the ++ logical router port. ++

    ++
  • ++ + + +

    Ingress Table 11: Check packet length

    +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index cd13f308e..27e90fcb2 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -8459,6 +8459,28 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + 100, ds_cstr(&match), ds_cstr(&actions)); + } + } ++ ++ if (!op->derived && op->od->l3redirect_port) { ++ const char *redirect_type = smap_get(&op->nbrp->options, ++ "redirect-type"); ++ if (redirect_type && !strcasecmp(redirect_type, "bridged")) { ++ /* Packet is on a non gateway chassis and ++ * has an unresolved ARP on a network behind gateway ++ * chassis attached router port. Since, redirect type ++ * is set to vlan, hence instead of calling "get_arp" ++ * on this node, we will redirect the packet to gateway ++ * chassis, by setting destination mac router port mac.*/ ++ ds_clear(&match); ++ ds_put_format(&match, "outport == %s && " ++ "!is_chassis_resident(%s)", op->json_key, ++ op->od->l3redirect_port->json_key); ++ ds_clear(&actions); ++ ds_put_format(&actions, "eth.dst = %s; next;", ++ op->lrp_networks.ea_s); ++ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ARP_RESOLVE, ++ 50, ds_cstr(&match), ds_cstr(&actions)); ++ } ++ } + } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router") + && strcmp(op->nbsp->type, "virtual")) { + /* This is a logical switch port that backs a VM or a container. +-- +2.23.0 + diff --git a/SOURCES/0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch b/SOURCES/0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch new file mode 100644 index 0000000..494afd9 --- /dev/null +++ b/SOURCES/0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch @@ -0,0 +1,88 @@ +From 3fb3e512bc90714907a520fb4a7ef84582bce6a7 Mon Sep 17 00:00:00 2001 +Message-Id: <3fb3e512bc90714907a520fb4a7ef84582bce6a7.1578588395.git.lorenzo.bianconi@redhat.com> +In-Reply-To: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com> +References: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 7 Jan 2020 17:50:06 +0100 +Subject: [PATCH 3/3] RA Route Info Option: copy route info string in order to + avoid truncated value + +ipv6_ra_send can run 2 times in a row before prepare_ipv6_ras updates +the route info. Copy route info string in packet_put_ra_route_info_opt +in order to avoid sending truncated route info on the wire. +Moreover move ip6_hdr definition just before accessing it because the +packet can be reallocated if the data area is not big enough for packet +content + +Fixes: 9f7f466af ("Add support for Route Info Option in RA - RFC 4191") +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/pinctrl.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2473,13 +2473,14 @@ out: + + static void + packet_put_ra_route_info_opt(struct dp_packet *b, ovs_be32 lifetime, +- char *route_list) ++ char *route_data) + { + size_t prev_l4_size = dp_packet_l4_size(b); +- struct ip6_hdr *nh = dp_packet_l3(b); +- char *t0, *r0 = NULL; ++ char *route_list, *t0, *r0 = NULL; + size_t size = 0; + ++ route_list = xstrdup(route_data); ++ + for (t0 = strtok_r(route_list, ",", &r0); t0; + t0 = strtok_r(NULL, ",", &r0)) { + struct ovs_nd_route_info nd_rinfo; +@@ -2507,11 +2508,11 @@ packet_put_ra_route_info_opt(struct dp_p + uint8_t plen; + + if (!extract_ip_addresses(t1, &route)) { +- return; ++ goto out; + } + if (!route.n_ipv6_addrs) { + destroy_lport_addresses(&route); +- return; ++ goto out; + } + + nd_rinfo.prefix_len = route.ipv6_addrs->plen; +@@ -2526,17 +2527,20 @@ packet_put_ra_route_info_opt(struct dp_p + break; + } + default: +- return; ++ goto out; + } + } + } + ++ struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + size); + struct ovs_ra_msg *ra = dp_packet_l4(b); + ra->icmph.icmp6_cksum = 0; + uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra, + prev_l4_size + size)); ++out: ++ free(route_list); + } + + /* Called with in the pinctrl_handler thread context. */ +@@ -2580,7 +2584,7 @@ ipv6_ra_send(struct rconn *swconn, struc + } + if (ra->config->route_info.length) { + packet_put_ra_route_info_opt(&packet, htonl(0xffffffff), +- ra->config->route_info.string); ++ ds_cstr(&ra->config->route_info)); + } + + uint64_t ofpacts_stub[4096 / 8]; diff --git a/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch b/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch new file mode 100644 index 0000000..e94d6eb --- /dev/null +++ b/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch @@ -0,0 +1,61 @@ +From 754d5581fa9d5de97f7c2acf8c2900e105f588c9 Mon Sep 17 00:00:00 2001 +Message-Id: <754d5581fa9d5de97f7c2acf8c2900e105f588c9.1590585469.git.lorenzo.bianconi@redhat.com> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Wed, 20 May 2020 22:01:16 +0200 +Subject: [PATCH ovn 2/3] controller: fix ip buffering with static routes + +When the arp request is sent to a gw router and not to the final +destination of the packet buffered_packets_map needs to be updated using +next-hop ip address and not the destionation one. + +Fixes: 2e5cdb4b1392 ("OVN: add buffering support for ip packets") +Signed-off-by: Lorenzo Bianconi +--- + ovn/controller/pinctrl.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -693,8 +693,7 @@ pinctrl_find_buffered_packets(const stru + + /* Called with in the pinctrl_handler thread context. */ + static int +-pinctrl_handle_buffered_packets(const struct flow *ip_flow, +- struct dp_packet *pkt_in, ++pinctrl_handle_buffered_packets(struct dp_packet *pkt_in, + const struct match *md, bool is_arp) + OVS_REQUIRES(pinctrl_mutex) + { +@@ -703,9 +702,10 @@ pinctrl_handle_buffered_packets(const st + struct in6_addr addr; + + if (is_arp) { +- addr = in6_addr_mapped_ipv4(ip_flow->nw_dst); ++ addr = in6_addr_mapped_ipv4(htonl(md->flow.regs[0])); + } else { +- addr = ip_flow->ipv6_dst; ++ ovs_be128 ip6 = hton128(flow_get_xxreg(&md->flow, 0)); ++ memcpy(&addr, &ip6, sizeof addr); + } + + uint32_t hash = hash_bytes(&addr, sizeof addr, 0); +@@ -746,7 +746,7 @@ pinctrl_handle_arp(struct rconn *swconn, + } + + ovs_mutex_lock(&pinctrl_mutex); +- pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, true); ++ pinctrl_handle_buffered_packets(pkt_in, md, true); + ovs_mutex_unlock(&pinctrl_mutex); + + /* Compose an ARP packet. */ +@@ -4338,7 +4338,7 @@ pinctrl_handle_nd_ns(struct rconn *swcon + } + + ovs_mutex_lock(&pinctrl_mutex); +- pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, false); ++ pinctrl_handle_buffered_packets(pkt_in, md, false); + ovs_mutex_unlock(&pinctrl_mutex); + + uint64_t packet_stub[128 / 8]; diff --git a/SOURCES/0003-northd-Load-config-before-processing-nbdb-contents.patch b/SOURCES/0003-northd-Load-config-before-processing-nbdb-contents.patch new file mode 100644 index 0000000..f0bdad0 --- /dev/null +++ b/SOURCES/0003-northd-Load-config-before-processing-nbdb-contents.patch @@ -0,0 +1,133 @@ +From f482ce2cbc0e76f1048927077021945ba0e1c216 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Mon, 9 Dec 2019 10:56:03 -0500 +Subject: [PATCH ovn 3/4] northd: Load config before processing nbdb contents + +Reorder ovnnb_db_run() such that configuration parameters are loaded +or initialized before processing the nbdb contents. + +I found this bug because I noticed dynamic MAC addresses being +assigned at ovn-northd startup with an empty prefix. Later, it would +switch to allocating MAC addresses with the random prefix that was +generated. + +The impact of this bug is particularly bad if ovn-northd restarts in +an existing environment. ovn-northd will check previously assigned +dynamic addresses for validity. At startup, previously assigned MAC +addresses will all appear invalid because they have a non-empty +prefix, so it will reset them all. In the case of IPv6, this also +causes the IPv6 addresses change, since OVN assigned dynamic IPv6 +addresses are based on the MAC address. + +With ovn-kubernetes, whatever first set of addresses were assigned is +what ends up cached on the Node object and used by the Pod. This bug +can cause all of this to get out of sync, breaking network +connectivity for Pods on an OVN virtual network. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique + +(cherry picked from upstream commit b441bf3a7211461552242d50e6b0c41e68d4799d) +--- + ovn/northd/ovn-northd.c | 78 ++++++++++++++++++++++++------------------------- + 1 file changed, 39 insertions(+), 39 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 40835a0..af67f63 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -10145,45 +10145,6 @@ ovnnb_db_run(struct northd_context *ctx, + struct shash meter_groups = SHASH_INITIALIZER(&meter_groups); + struct hmap lbs; + +- build_datapaths(ctx, datapaths, lr_list); +- build_ports(ctx, sbrec_chassis_by_name, datapaths, ports); +- build_ovn_lbs(ctx, ports, &lbs); +- build_ipam(datapaths, ports); +- build_port_group_lswitches(ctx, &port_groups, ports); +- build_lrouter_groups(ports, lr_list); +- build_ip_mcast(ctx, datapaths); +- build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups); +- build_meter_groups(ctx, &meter_groups); +- build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups, +- &igmp_groups, &meter_groups, &lbs); +- +- sync_address_sets(ctx); +- sync_port_groups(ctx); +- sync_meters(ctx); +- sync_dns_entries(ctx, datapaths); +- destroy_ovn_lbs(&lbs); +- hmap_destroy(&lbs); +- +- struct ovn_igmp_group *igmp_group, *next_igmp_group; +- +- HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, &igmp_groups) { +- ovn_igmp_group_destroy(&igmp_groups, igmp_group); +- } +- +- struct ovn_port_group *pg, *next_pg; +- HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &port_groups) { +- ovn_port_group_destroy(&port_groups, pg); +- } +- hmap_destroy(&igmp_groups); +- hmap_destroy(&mcast_groups); +- hmap_destroy(&port_groups); +- +- struct shash_node *node, *next; +- SHASH_FOR_EACH_SAFE (node, next, &meter_groups) { +- shash_delete(&meter_groups, node); +- } +- shash_destroy(&meter_groups); +- + /* Sync ipsec configuration. + * Copy nb_cfg from northbound to southbound database. + * Also set up to update sb_cfg once our southbound transaction commits. */ +@@ -10257,6 +10218,45 @@ ovnnb_db_run(struct northd_context *ctx, + controller_event_en = smap_get_bool(&nb->options, + "controller_event", false); + ++ build_datapaths(ctx, datapaths, lr_list); ++ build_ports(ctx, sbrec_chassis_by_name, datapaths, ports); ++ build_ovn_lbs(ctx, ports, &lbs); ++ build_ipam(datapaths, ports); ++ build_port_group_lswitches(ctx, &port_groups, ports); ++ build_lrouter_groups(ports, lr_list); ++ build_ip_mcast(ctx, datapaths); ++ build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups); ++ build_meter_groups(ctx, &meter_groups); ++ build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups, ++ &igmp_groups, &meter_groups, &lbs); ++ ++ sync_address_sets(ctx); ++ sync_port_groups(ctx); ++ sync_meters(ctx); ++ sync_dns_entries(ctx, datapaths); ++ destroy_ovn_lbs(&lbs); ++ hmap_destroy(&lbs); ++ ++ struct ovn_igmp_group *igmp_group, *next_igmp_group; ++ ++ HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, &igmp_groups) { ++ ovn_igmp_group_destroy(&igmp_groups, igmp_group); ++ } ++ ++ struct ovn_port_group *pg, *next_pg; ++ HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &port_groups) { ++ ovn_port_group_destroy(&port_groups, pg); ++ } ++ hmap_destroy(&igmp_groups); ++ hmap_destroy(&mcast_groups); ++ hmap_destroy(&port_groups); ++ ++ struct shash_node *node, *next; ++ SHASH_FOR_EACH_SAFE (node, next, &meter_groups) { ++ shash_delete(&meter_groups, node); ++ } ++ shash_destroy(&meter_groups); ++ + cleanup_macam(&macam); + } + +-- +1.8.3.1 + diff --git a/SOURCES/0003-northd-add-rate-limiting-support-for-SB-controller-e.patch b/SOURCES/0003-northd-add-rate-limiting-support-for-SB-controller-e.patch new file mode 100644 index 0000000..bd9352e --- /dev/null +++ b/SOURCES/0003-northd-add-rate-limiting-support-for-SB-controller-e.patch @@ -0,0 +1,209 @@ +From 0cb57d3e670d841510f911fbbf8a3db57239bf61 Mon Sep 17 00:00:00 2001 +Message-Id: <0cb57d3e670d841510f911fbbf8a3db57239bf61.1568637354.git.lorenzo.bianconi@redhat.com> +In-Reply-To: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com> +References: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 3 Sep 2019 16:22:53 +0200 +Subject: [PATCH ovn 3/3] northd: add rate limiting support for SB controller + events + +Introduce the capability to associate a meter to each controller event +type in order to not overload the pinctrl thread under heavy load. +Each event type relies on a meter with a defined name: +- empty_lb_backends: event-elb + +Acked-by: Mark Michelson +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Mark Michelson +--- + ovn/northd/ovn-northd.8.xml | 5 ++++ + ovn/northd/ovn-northd.c | 55 ++++++++++++++++++++++++++++++----------- + ovn/ovn-nb.xml | 8 ++++++ + tests/ovn.at | 1 + + 4 files changed, 55 insertions(+), 14 deletions(-) + +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -301,6 +301,11 @@ + Pre-stateful to send IP packets to the connection tracker + for packet de-fragmentation before eventually advancing to ingress table + LB. ++ If controller_event has been enabled and load balancing rules with ++ empty backends have been added in OVN_Northbound, a 130 flow ++ is added to trigger ovn-controller events whenever the chassis receives a ++ packet for that particular VIP. If event-elb meter has been ++ previously created, it will be associated to the empty_lb logical flow +

    + +

    Ingress Table 5: Pre-stateful

    +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -3885,14 +3885,18 @@ static void + build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows, + struct smap_node *node, char *ip_address, + struct nbrec_load_balancer *lb, uint16_t port, +- int addr_family, int pl) ++ int addr_family, int pl, struct shash *meter_groups) + { + if (!controller_event_en || node->value[0]) { + return; + } + + struct ds match = DS_EMPTY_INITIALIZER; +- char *action; ++ char *meter = "", *action; ++ ++ if (meter_groups && shash_find(meter_groups, "event-elb")) { ++ meter = "event-elb"; ++ } + + if (addr_family == AF_INET) { + ds_put_format(&match, "ip4.dst == %s && %s", +@@ -3906,18 +3910,20 @@ build_empty_lb_event_flow(struct ovn_dat + port); + } + action = xasprintf("trigger_event(event = \"%s\", " +- "vip = \"%s\", protocol = \"%s\", " +- "load_balancer = \"" UUID_FMT "\");", +- event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS), +- node->key, lb->protocol, +- UUID_ARGS(&lb->header_.uuid)); ++ "meter = \"%s\", vip = \"%s\", " ++ "protocol = \"%s\", " ++ "load_balancer = \"" UUID_FMT "\");", ++ event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS), ++ meter, node->key, lb->protocol, ++ UUID_ARGS(&lb->header_.uuid)); + ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action); + ds_destroy(&match); + free(action); + } + + static void +-build_pre_lb(struct ovn_datapath *od, struct hmap *lflows) ++build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, ++ struct shash *meter_groups) + { + /* Do not send ND packets to conntrack */ + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110, +@@ -3954,7 +3960,8 @@ build_pre_lb(struct ovn_datapath *od, st + } + + build_empty_lb_event_flow(od, lflows, node, ip_address, lb, +- port, addr_family, S_SWITCH_IN_PRE_LB); ++ port, addr_family, S_SWITCH_IN_PRE_LB, ++ meter_groups); + + free(ip_address); + +@@ -4774,7 +4781,8 @@ build_lrouter_groups(struct hmap *ports, + static void + build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + struct hmap *port_groups, struct hmap *lflows, +- struct hmap *mcgroups, struct hmap *igmp_groups) ++ struct hmap *mcgroups, struct hmap *igmp_groups, ++ struct shash *meter_groups) + { + /* This flow table structure is documented in ovn-northd(8), so please + * update ovn-northd.8.xml if you change anything. */ +@@ -4791,7 +4799,7 @@ build_lswitch_flows(struct hmap *datapat + } + + build_pre_acls(od, lflows); +- build_pre_lb(od, lflows); ++ build_pre_lb(od, lflows, meter_groups); + build_pre_stateful(od, lflows); + build_acls(od, lflows, port_groups); + build_qos(od, lflows); +@@ -8084,12 +8092,13 @@ build_lrouter_flows(struct hmap *datapat + static void + build_lflows(struct northd_context *ctx, struct hmap *datapaths, + struct hmap *ports, struct hmap *port_groups, +- struct hmap *mcgroups, struct hmap *igmp_groups) ++ struct hmap *mcgroups, struct hmap *igmp_groups, ++ struct shash *meter_groups) + { + struct hmap lflows = HMAP_INITIALIZER(&lflows); + + build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups, +- igmp_groups); ++ igmp_groups, meter_groups); + build_lrouter_flows(datapaths, ports, &lflows); + + /* Push changes to the Logical_Flow table to database. */ +@@ -8703,6 +8712,16 @@ build_mcast_groups(struct northd_context + } + + static void ++build_meter_groups(struct northd_context *ctx, ++ struct shash *meter_groups) ++{ ++ const struct nbrec_meter *nb_meter; ++ NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) { ++ shash_add(meter_groups, nb_meter->name, nb_meter); ++ } ++} ++ ++static void + ovnnb_db_run(struct northd_context *ctx, + struct ovsdb_idl_index *sbrec_chassis_by_name, + struct ovsdb_idl_loop *sb_loop, +@@ -8715,6 +8734,7 @@ ovnnb_db_run(struct northd_context *ctx, + struct hmap port_groups; + struct hmap mcast_groups; + struct hmap igmp_groups; ++ struct shash meter_groups = SHASH_INITIALIZER(&meter_groups); + + build_datapaths(ctx, datapaths, lr_list); + build_ports(ctx, sbrec_chassis_by_name, datapaths, ports); +@@ -8723,8 +8743,9 @@ ovnnb_db_run(struct northd_context *ctx, + build_lrouter_groups(ports, lr_list); + build_ip_mcast(ctx, datapaths); + build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups); ++ build_meter_groups(ctx, &meter_groups); + build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups, +- &igmp_groups); ++ &igmp_groups, &meter_groups); + + sync_address_sets(ctx); + sync_port_groups(ctx); +@@ -8745,6 +8766,12 @@ ovnnb_db_run(struct northd_context *ctx, + hmap_destroy(&mcast_groups); + hmap_destroy(&port_groups); + ++ struct shash_node *node, *next; ++ SHASH_FOR_EACH_SAFE (node, next, &meter_groups) { ++ shash_delete(&meter_groups, node); ++ } ++ shash_destroy(&meter_groups); ++ + /* Sync ipsec configuration. + * Copy nb_cfg from northbound to southbound database. + * Also set up to update sb_cfg once our southbound transaction commits. */ +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -117,6 +117,14 @@ + table. + The intention is for a CMS to see the events and take some sort of + action. Please see the table in SBDB. ++ It is possible to associate a meter to each controller event type ++ in order to not overload the pinctrl thread under heavy load. ++ Each event type relies on a meter with a defined name: ++ ++
      ++
    • empty_lb_backends: event-elb
    • ++
    ++ +
    +
    + +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -14719,6 +14719,7 @@ ovn-nbctl --wait=hv set NB_Global . opti + ovn-nbctl lb-add lb0 192.168.1.100:80 "" + ovn-nbctl ls-lb-add sw0 lb0 + uuid_lb=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0) ++ovn-nbctl --wait=hv meter-add event-elb drop 100 pktps 10 + + OVN_POPULATE_ARP + ovn-nbctl --timeout=3 --wait=hv sync diff --git a/SOURCES/0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch b/SOURCES/0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch new file mode 100644 index 0000000..f8e6413 --- /dev/null +++ b/SOURCES/0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch @@ -0,0 +1,299 @@ +From 84d337ec6c4a15154de2e8dd7b53d3626814d7a9 Mon Sep 17 00:00:00 2001 +Message-Id: <84d337ec6c4a15154de2e8dd7b53d3626814d7a9.1569932887.git.lorenzo.bianconi@redhat.com> +In-Reply-To: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com> +References: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com> +From: Lorenzo Bianconi +Date: Tue, 24 Sep 2019 18:39:56 +0200 +Subject: [PATCH ovn 3/3] northd: introduce logical flow for localnet egress + shaping + +Add set_queue() action for qos capable localnet port in +S_SWITCH_OUT_PORT_SEC_L2 stage of logical swith pipeline +Introduce build_lswitch_{input,outpur}_port_sec and refactor +lswitch_port_security code in order to remove duplicated code + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Mark Michelson +Acked-by: Mark Michelson +--- + ovn/northd/ovn-northd.8.xml | 7 +- + ovn/northd/ovn-northd.c | 231 ++++++++++++++++++++++++---------------- + 2 files changed, 143 insertions(+), 95 deletions(-) + +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1120,10 +1120,15 @@ output; + eth.dst are always accepted instead of being subject to the + port security rules; this is implemented through a priority-100 flow that + matches on eth.mcast with action output;. +- Finally, to ensure that even broadcast and multicast packets are not ++ Moreover, to ensure that even broadcast and multicast packets are not + delivered to disabled logical ports, a priority-150 flow for each + disabled logical outport overrides the priority-100 flow + with a drop; action. ++ Finally if egress qos has been enabled on a localnet port, the outgoing ++ queue id is set through set_queue action. Please remember to ++ mark the corresponding physical interface with ++ ovn-egress-iface set to true in +

    + +

    Logical Router Datapaths

    +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -3771,6 +3771,140 @@ has_stateful_acl(struct ovn_datapath *od + } + + static void ++build_lswitch_input_port_sec(struct hmap *ports, struct hmap *datapaths, ++ struct hmap *lflows) ++{ ++ /* Logical switch ingress table 0: Ingress port security - L2 ++ * (priority 50). ++ * Ingress table 1: Ingress port security - IP (priority 90 and 80) ++ * Ingress table 2: Ingress port security - ND (priority 90 and 80) ++ */ ++ struct ds actions = DS_EMPTY_INITIALIZER; ++ struct ds match = DS_EMPTY_INITIALIZER; ++ struct ovn_port *op; ++ ++ HMAP_FOR_EACH (op, key_node, ports) { ++ if (!op->nbsp) { ++ continue; ++ } ++ ++ if (!lsp_is_enabled(op->nbsp)) { ++ /* Drop packets from disabled logical ports (since logical flow ++ * tables are default-drop). */ ++ continue; ++ } ++ ++ if (lsp_is_external(op->nbsp)) { ++ continue; ++ } ++ ++ ds_clear(&match); ++ ds_clear(&actions); ++ ds_put_format(&match, "inport == %s", op->json_key); ++ build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs, ++ &match); ++ ++ const char *queue_id = smap_get(&op->sb->options, "qdisc_queue_id"); ++ if (queue_id) { ++ ds_put_format(&actions, "set_queue(%s); ", queue_id); ++ } ++ ds_put_cstr(&actions, "next;"); ++ ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50, ++ ds_cstr(&match), ds_cstr(&actions)); ++ ++ if (op->nbsp->n_port_security) { ++ build_port_security_ip(P_IN, op, lflows); ++ build_port_security_nd(op, lflows); ++ } ++ } ++ ++ /* Ingress table 1 and 2: Port security - IP and ND, by default ++ * goto next. (priority 0) ++ */ ++ struct ovn_datapath *od; ++ HMAP_FOR_EACH (od, key_node, datapaths) { ++ if (!od->nbs) { ++ continue; ++ } ++ ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 0, "1", "next;"); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1", "next;"); ++ } ++ ++ ds_destroy(&match); ++ ds_destroy(&actions); ++} ++ ++static void ++build_lswitch_output_port_sec(struct hmap *ports, struct hmap *datapaths, ++ struct hmap *lflows) ++{ ++ struct ds actions = DS_EMPTY_INITIALIZER; ++ struct ds match = DS_EMPTY_INITIALIZER; ++ struct ovn_port *op; ++ ++ /* Egress table 8: Egress port security - IP (priorities 90 and 80) ++ * if port security enabled. ++ * ++ * Egress table 9: Egress port security - L2 (priorities 50 and 150). ++ * ++ * Priority 50 rules implement port security for enabled logical port. ++ * ++ * Priority 150 rules drop packets to disabled logical ports, so that ++ * they don't even receive multicast or broadcast packets. ++ */ ++ HMAP_FOR_EACH (op, key_node, ports) { ++ if (!op->nbsp || lsp_is_external(op->nbsp)) { ++ continue; ++ } ++ ++ ds_clear(&actions); ++ ds_clear(&match); ++ ++ ds_put_format(&match, "outport == %s", op->json_key); ++ if (lsp_is_enabled(op->nbsp)) { ++ build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs, ++ &match); ++ ++ if (!strcmp(op->nbsp->type, "localnet")) { ++ const char *queue_id = smap_get(&op->sb->options, ++ "qdisc_queue_id"); ++ if (queue_id) { ++ ds_put_format(&actions, "set_queue(%s); ", queue_id); ++ } ++ } ++ ds_put_cstr(&actions, "output;"); ++ ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50, ++ ds_cstr(&match), ds_cstr(&actions)); ++ } else { ++ ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150, ++ ds_cstr(&match), "drop;"); ++ } ++ ++ if (op->nbsp->n_port_security) { ++ build_port_security_ip(P_OUT, op, lflows); ++ } ++ } ++ ++ /* Egress tables 8: Egress port security - IP (priority 0) ++ * Egress table 9: Egress port security L2 - multicast/broadcast ++ * (priority 100). */ ++ struct ovn_datapath *od; ++ HMAP_FOR_EACH (od, key_node, datapaths) { ++ if (!od->nbs) { ++ continue; ++ } ++ ++ ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 0, "1", "next;"); ++ ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 100, "eth.mcast", ++ "output;"); ++ } ++ ++ ds_destroy(&match); ++ ds_destroy(&actions); ++} ++ ++static void + build_pre_acls(struct ovn_datapath *od, struct hmap *lflows) + { + bool has_stateful = has_stateful_acl(od); +@@ -4841,61 +4975,12 @@ build_lswitch_flows(struct hmap *datapat + * to the next table if packet source is acceptable. */ + } + +- /* Logical switch ingress table 0: Ingress port security - L2 +- * (priority 50). +- * Ingress table 1: Ingress port security - IP (priority 90 and 80) +- * Ingress table 2: Ingress port security - ND (priority 90 and 80) +- */ +- struct ovn_port *op; +- HMAP_FOR_EACH (op, key_node, ports) { +- if (!op->nbsp) { +- continue; +- } +- +- if (!lsp_is_enabled(op->nbsp)) { +- /* Drop packets from disabled logical ports (since logical flow +- * tables are default-drop). */ +- continue; +- } +- +- if (lsp_is_external(op->nbsp)) { +- continue; +- } +- +- ds_clear(&match); +- ds_clear(&actions); +- ds_put_format(&match, "inport == %s", op->json_key); +- build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs, +- &match); +- +- const char *queue_id = smap_get(&op->sb->options, "qdisc_queue_id"); +- if (queue_id) { +- ds_put_format(&actions, "set_queue(%s); ", queue_id); +- } +- ds_put_cstr(&actions, "next;"); +- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50, +- ds_cstr(&match), ds_cstr(&actions)); +- +- if (op->nbsp->n_port_security) { +- build_port_security_ip(P_IN, op, lflows); +- build_port_security_nd(op, lflows); +- } +- } +- +- /* Ingress table 1 and 2: Port security - IP and ND, by default goto next. +- * (priority 0)*/ +- HMAP_FOR_EACH (od, key_node, datapaths) { +- if (!od->nbs) { +- continue; +- } +- +- ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 0, "1", "next;"); +- ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1", "next;"); +- } ++ build_lswitch_input_port_sec(ports, datapaths, lflows); + + /* Ingress table 11: ARP/ND responder, skip requests coming from localnet + * and vtep ports. (priority 100); see ovn-northd.8.xml for the + * rationale. */ ++ struct ovn_port *op; + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbsp) { + continue; +@@ -5501,49 +5586,7 @@ build_lswitch_flows(struct hmap *datapat + } + } + +- /* Egress tables 8: Egress port security - IP (priority 0) +- * Egress table 9: Egress port security L2 - multicast/broadcast +- * (priority 100). */ +- HMAP_FOR_EACH (od, key_node, datapaths) { +- if (!od->nbs) { +- continue; +- } +- +- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 0, "1", "next;"); +- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 100, "eth.mcast", +- "output;"); +- } +- +- /* Egress table 8: Egress port security - IP (priorities 90 and 80) +- * if port security enabled. +- * +- * Egress table 9: Egress port security - L2 (priorities 50 and 150). +- * +- * Priority 50 rules implement port security for enabled logical port. +- * +- * Priority 150 rules drop packets to disabled logical ports, so that they +- * don't even receive multicast or broadcast packets. */ +- HMAP_FOR_EACH (op, key_node, ports) { +- if (!op->nbsp || lsp_is_external(op->nbsp)) { +- continue; +- } +- +- ds_clear(&match); +- ds_put_format(&match, "outport == %s", op->json_key); +- if (lsp_is_enabled(op->nbsp)) { +- build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs, +- &match); +- ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50, +- ds_cstr(&match), "output;"); +- } else { +- ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150, +- ds_cstr(&match), "drop;"); +- } +- +- if (op->nbsp->n_port_security) { +- build_port_security_ip(P_OUT, op, lflows); +- } +- } ++ build_lswitch_output_port_sec(ports, datapaths, lflows); + + ds_destroy(&match); + ds_destroy(&actions); diff --git a/SOURCES/0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch b/SOURCES/0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch new file mode 100644 index 0000000..b9f13b1 --- /dev/null +++ b/SOURCES/0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch @@ -0,0 +1,272 @@ +From f4984801e417fc2c9c92d5d34f9621a4a338aae3 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Wed, 27 Nov 2019 14:15:38 +0100 +Subject: [PATCH ovn 3/4] ovn-controller: Add separate I-P engine node for + processing ct-zones. + +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit 2117ba0a91f36206d0f3665e8680c15f1f6fa0a0) + +Change-Id: I4980ad2641e02356c85c259e71a905bc94dcb833 +--- + ovn/controller/ovn-controller.c | 117 ++++++++++++++++++++++++++-------------- + 1 file changed, 78 insertions(+), 39 deletions(-) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index 8c474e9..20e5ce4 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -897,11 +897,6 @@ struct ed_type_runtime_data { + * _ */ + struct sset local_lport_ids; + struct sset active_tunnels; +- +- /* connection tracking zones. */ +- unsigned long ct_zone_bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)]; +- struct shash pending_ct_zones; +- struct simap ct_zones; + }; + + static void +@@ -909,24 +904,11 @@ en_runtime_data_init(struct engine_node *node) + { + struct ed_type_runtime_data *data = + (struct ed_type_runtime_data *)node->data; +- struct ovsrec_open_vswitch_table *ovs_table = +- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( +- engine_get_input("OVS_open_vswitch", node)); +- struct ovsrec_bridge_table *bridge_table = +- (struct ovsrec_bridge_table *)EN_OVSDB_GET( +- engine_get_input("OVS_bridge", node)); ++ + hmap_init(&data->local_datapaths); + sset_init(&data->local_lports); + sset_init(&data->local_lport_ids); + sset_init(&data->active_tunnels); +- shash_init(&data->pending_ct_zones); +- simap_init(&data->ct_zones); +- +- /* Initialize connection tracking zones. */ +- memset(data->ct_zone_bitmap, 0, sizeof data->ct_zone_bitmap); +- bitmap_set1(data->ct_zone_bitmap, 0); /* Zone 0 is reserved. */ +- restore_ct_zones(bridge_table, ovs_table, +- &data->ct_zones, data->ct_zone_bitmap); + } + + static void +@@ -946,9 +928,6 @@ en_runtime_data_cleanup(struct engine_node *node) + free(cur_node); + } + hmap_destroy(&data->local_datapaths); +- +- simap_destroy(&data->ct_zones); +- shash_destroy(&data->pending_ct_zones); + } + + static void +@@ -960,9 +939,6 @@ en_runtime_data_run(struct engine_node *node) + struct sset *local_lports = &data->local_lports; + struct sset *local_lport_ids = &data->local_lport_ids; + struct sset *active_tunnels = &data->active_tunnels; +- unsigned long *ct_zone_bitmap = data->ct_zone_bitmap; +- struct shash *pending_ct_zones = &data->pending_ct_zones; +- struct simap *ct_zones = &data->ct_zones; + + static bool first_run = true; + if (first_run) { +@@ -1056,9 +1032,6 @@ en_runtime_data_run(struct engine_node *node) + ovs_table, local_datapaths, + local_lports, local_lport_ids); + +- update_ct_zones(local_lports, local_datapaths, ct_zones, +- ct_zone_bitmap, pending_ct_zones); +- + engine_set_node_state(node, EN_UPDATED); + } + +@@ -1100,6 +1073,55 @@ runtime_data_sb_port_binding_handler(struct engine_node *node) + return !changed; + } + ++/* Connection tracking zones. */ ++struct ed_type_ct_zones { ++ unsigned long bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)]; ++ struct shash pending; ++ struct simap current; ++}; ++ ++static void ++en_ct_zones_init(struct engine_node *node) ++{ ++ struct ed_type_ct_zones *data = node->data; ++ struct ovsrec_open_vswitch_table *ovs_table = ++ (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( ++ engine_get_input("OVS_open_vswitch", node)); ++ struct ovsrec_bridge_table *bridge_table = ++ (struct ovsrec_bridge_table *)EN_OVSDB_GET( ++ engine_get_input("OVS_bridge", node)); ++ ++ shash_init(&data->pending); ++ simap_init(&data->current); ++ ++ memset(data->bitmap, 0, sizeof data->bitmap); ++ bitmap_set1(data->bitmap, 0); /* Zone 0 is reserved. */ ++ restore_ct_zones(bridge_table, ovs_table, &data->current, data->bitmap); ++} ++ ++static void ++en_ct_zones_cleanup(struct engine_node *node) ++{ ++ struct ed_type_ct_zones *data = node->data; ++ ++ simap_destroy(&data->current); ++ shash_destroy(&data->pending); ++} ++ ++static void ++en_ct_zones_run(struct engine_node *node) ++{ ++ struct ed_type_ct_zones *data = node->data; ++ struct ed_type_runtime_data *rt_data = ++ (struct ed_type_runtime_data *)engine_get_input( ++ "runtime_data", node)->data; ++ ++ update_ct_zones(&rt_data->local_lports, &rt_data->local_datapaths, ++ &data->current, data->bitmap, &data->pending); ++ ++ engine_set_node_state(node, EN_UPDATED); ++} ++ + struct ed_type_mff_ovn_geneve { + enum mf_field_id mff_ovn_geneve; + }; +@@ -1177,7 +1199,11 @@ en_flow_output_run(struct engine_node *node) + struct sset *local_lports = &rt_data->local_lports; + struct sset *local_lport_ids = &rt_data->local_lport_ids; + struct sset *active_tunnels = &rt_data->active_tunnels; +- struct simap *ct_zones = &rt_data->ct_zones; ++ ++ struct ed_type_ct_zones *ct_zones_data = ++ (struct ed_type_ct_zones *)engine_get_input( ++ "ct_zones", node)->data; ++ struct simap *ct_zones = &ct_zones_data->current; + + struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = + (struct ed_type_mff_ovn_geneve *)engine_get_input( +@@ -1407,7 +1433,11 @@ flow_output_sb_port_binding_handler(struct engine_node *node) + "runtime_data", node)->data; + struct hmap *local_datapaths = &data->local_datapaths; + struct sset *active_tunnels = &data->active_tunnels; +- struct simap *ct_zones = &data->ct_zones; ++ ++ struct ed_type_ct_zones *ct_zones_data = ++ (struct ed_type_ct_zones *)engine_get_input( ++ "ct_zones", node)->data; ++ struct simap *ct_zones = &ct_zones_data->current; + + struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = + (struct ed_type_mff_ovn_geneve *)engine_get_input( +@@ -1510,7 +1540,11 @@ flow_output_sb_multicast_group_handler(struct engine_node *node) + (struct ed_type_runtime_data *)engine_get_input( + "runtime_data", node)->data; + struct hmap *local_datapaths = &data->local_datapaths; +- struct simap *ct_zones = &data->ct_zones; ++ ++ struct ed_type_ct_zones *ct_zones_data = ++ (struct ed_type_ct_zones *)engine_get_input( ++ "ct_zones", node)->data; ++ struct simap *ct_zones = &ct_zones_data->current; + + struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = + (struct ed_type_mff_ovn_geneve *)engine_get_input( +@@ -1817,6 +1851,7 @@ main(int argc, char *argv[]) + stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS); + + /* Define inc-proc-engine nodes. */ ++ struct ed_type_ct_zones ed_ct_zones; + struct ed_type_runtime_data ed_runtime_data; + struct ed_type_mff_ovn_geneve ed_mff_ovn_geneve; + struct ed_type_ofctrl_is_connected ed_ofctrl_is_connected; +@@ -1824,6 +1859,7 @@ main(int argc, char *argv[]) + struct ed_type_addr_sets ed_addr_sets; + struct ed_type_port_groups ed_port_groups; + ++ ENGINE_NODE(ct_zones, "ct_zones"); + ENGINE_NODE(runtime_data, "runtime_data"); + ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve"); + ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected"); +@@ -1863,6 +1899,7 @@ main(int argc, char *argv[]) + engine_add_input(&en_flow_output, &en_port_groups, + flow_output_port_groups_handler); + engine_add_input(&en_flow_output, &en_runtime_data, NULL); ++ engine_add_input(&en_flow_output, &en_ct_zones, NULL); + engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL); + + engine_add_input(&en_flow_output, &en_ovs_open_vswitch, NULL); +@@ -1882,6 +1919,10 @@ main(int argc, char *argv[]) + engine_add_input(&en_flow_output, &en_sb_dhcpv6_options, NULL); + engine_add_input(&en_flow_output, &en_sb_dns, NULL); + ++ engine_add_input(&en_ct_zones, &en_ovs_open_vswitch, NULL); ++ engine_add_input(&en_ct_zones, &en_ovs_bridge, NULL); ++ engine_add_input(&en_ct_zones, &en_runtime_data, NULL); ++ + engine_add_input(&en_runtime_data, &en_ofctrl_is_connected, NULL); + + engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL); +@@ -1907,7 +1948,7 @@ main(int argc, char *argv[]) + meter_table_list, &ed_flow_output.meter_table); + + unixctl_command_register("ct-zone-list", "", 0, 0, +- ct_zone_list, &ed_runtime_data.ct_zones); ++ ct_zone_list, &ed_ct_zones.current); + + struct pending_pkt pending_pkt = { .conn = NULL }; + unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt, +@@ -1983,7 +2024,7 @@ main(int argc, char *argv[]) + } + + if (br_int) { +- ofctrl_run(br_int, &ed_runtime_data.pending_ct_zones); ++ ofctrl_run(br_int, &ed_ct_zones.pending); + + if (chassis) { + patch_run(ovs_idl_txn, +@@ -2024,8 +2065,7 @@ main(int argc, char *argv[]) + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, + time_msec()); + if (ovs_idl_txn) { +- commit_ct_zones(br_int, +- &ed_runtime_data.pending_ct_zones); ++ commit_ct_zones(br_int, &ed_ct_zones.pending); + bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl), + br_int, chassis, + sbrec_ha_chassis_group_table_get( +@@ -2033,7 +2073,7 @@ main(int argc, char *argv[]) + sbrec_sb_global_table_get(ovnsb_idl_loop.idl)); + } + ofctrl_put(&ed_flow_output.flow_table, +- &ed_runtime_data.pending_ct_zones, ++ &ed_ct_zones.pending, + sbrec_meter_table_get(ovnsb_idl_loop.idl), + get_nb_cfg(sbrec_sb_global_table_get( + ovnsb_idl_loop.idl)), +@@ -2134,11 +2174,10 @@ main(int argc, char *argv[]) + + if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) { + struct shash_node *iter, *iter_next; +- SHASH_FOR_EACH_SAFE (iter, iter_next, +- &ed_runtime_data.pending_ct_zones) { ++ SHASH_FOR_EACH_SAFE (iter, iter_next, &ed_ct_zones.pending) { + struct ct_zone_pending_entry *ctzpe = iter->data; + if (ctzpe->state == CT_ZONE_DB_SENT) { +- shash_delete(&ed_runtime_data.pending_ct_zones, iter); ++ shash_delete(&ed_ct_zones.pending, iter); + free(ctzpe); + } + } +-- +1.8.3.1 + diff --git a/SOURCES/0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch b/SOURCES/0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch new file mode 100644 index 0000000..ea3e611 --- /dev/null +++ b/SOURCES/0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch @@ -0,0 +1,42 @@ +From bfed073bd6b2d4923286fe77b4813c330895bbfa Mon Sep 17 00:00:00 2001 +From: Han Zhou +Date: Thu, 14 May 2020 22:27:39 +0530 +Subject: [PATCH 3/4] ovn-controller.c: Fix memory leak of + local_datapath->ports. + +Fixes: 89f5048f960c ("ovn-controller: Minimize SB DB port_binding lookups.") +Acked-by: Numan Siddique +Acked-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit dc0e10c068c20c4e59c9c86ecee26baf8ed50e90) + +Change-Id: I317fabe5ed0017893267524dc25881c20e764975 +Signed-off-by: Numan Siddique +--- + ovn/controller/ovn-controller.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index b80d6c0dc..8d7ea89c4 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -929,6 +929,7 @@ en_runtime_data_cleanup(void *data) + HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, + &rt_data->local_datapaths) { + free(cur_node->peer_ports); ++ free(cur_node->ports); + hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node); + free(cur_node); + } +@@ -952,6 +953,7 @@ en_runtime_data_run(struct engine_node *node, void *data) + struct local_datapath *cur_node, *next_node; + HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, local_datapaths) { + free(cur_node->peer_ports); ++ free(cur_node->ports); + hmap_remove(local_datapaths, &cur_node->hmap_node); + free(cur_node); + } +-- +2.26.2 + diff --git a/SOURCES/0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch b/SOURCES/0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch new file mode 100644 index 0000000..c655f80 --- /dev/null +++ b/SOURCES/0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch @@ -0,0 +1,209 @@ +From b76dba2ee078b2b2d168b9ada5ddc01b05a9a9d1 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Fri, 25 Oct 2019 12:22:04 -0400 +Subject: [PATCH 3/5] ovn-nbctl: Allow IPv6 NAT rules to be added + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique +--- + ovn/utilities/ovn-nbctl.c | 49 +++++++++++++++++++++++++++++---------- + tests/ovn-nbctl.at | 41 +++++++++++++++++++++++++------- + 2 files changed, 69 insertions(+), 21 deletions(-) + +diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c +index 7a38b2bf7..497d6f231 100644 +--- a/ovn/utilities/ovn-nbctl.c ++++ b/ovn/utilities/ovn-nbctl.c +@@ -3853,26 +3853,51 @@ nbctl_lr_nat_add(struct ctl_context *ctx) + + ovs_be32 ipv4 = 0; + unsigned int plen; ++ struct in6_addr ipv6; ++ bool is_v6 = false; + if (!ip_parse(external_ip, &ipv4)) { +- ctl_error(ctx, "%s: should be an IPv4 address.", external_ip); +- return; ++ if (ipv6_parse(external_ip, &ipv6)) { ++ is_v6 = true; ++ } else { ++ ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.", ++ external_ip); ++ return; ++ } + } + + if (strcmp("snat", nat_type)) { +- if (!ip_parse(logical_ip, &ipv4)) { +- ctl_error(ctx, "%s: should be an IPv4 address.", logical_ip); +- return; ++ if (is_v6) { ++ if (!ipv6_parse(logical_ip, &ipv6)) { ++ ctl_error(ctx, "%s: Not a valid IPv6 address.", logical_ip); ++ return; ++ } ++ } else { ++ if (!ip_parse(logical_ip, &ipv4)) { ++ ctl_error(ctx, "%s: Not a valid IPv4 address.", logical_ip); ++ return; ++ } + } + new_logical_ip = xstrdup(logical_ip); + } else { +- error = ip_parse_cidr(logical_ip, &ipv4, &plen); +- if (error) { +- free(error); +- ctl_error(ctx, "%s: should be an IPv4 address or network.", +- logical_ip); +- return; ++ if (is_v6) { ++ error = ipv6_parse_cidr(logical_ip, &ipv6, &plen); ++ if (error) { ++ free(error); ++ ctl_error(ctx, "%s: should be an IPv6 address or network.", ++ logical_ip); ++ return; ++ } ++ new_logical_ip = normalize_ipv6_prefix(ipv6, plen); ++ } else { ++ error = ip_parse_cidr(logical_ip, &ipv4, &plen); ++ if (error) { ++ free(error); ++ ctl_error(ctx, "%s: should be an IPv4 address or network.", ++ logical_ip); ++ return; ++ } ++ new_logical_ip = normalize_ipv4_prefix(ipv4, plen); + } +- new_logical_ip = normalize_ipv4_prefix(ipv4, plen); + } + + const char *logical_port; +diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at +index e87242c03..30849682c 100644 +--- a/tests/ovn-nbctl.at ++++ b/tests/ovn-nbctl.at +@@ -407,16 +407,16 @@ AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [], + [ovn-nbctl: snatt: type must be one of "dnat", "snat" and "dnat_and_snat". + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2a 192.168.1.2], [1], [], +-[ovn-nbctl: 30.0.0.2a: should be an IPv4 address. ++[ovn-nbctl: 30.0.0.2a: Not a valid IPv4 or IPv6 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0 192.168.1.2], [1], [], +-[ovn-nbctl: 30.0.0: should be an IPv4 address. ++[ovn-nbctl: 30.0.0: Not a valid IPv4 or IPv6 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2/24 192.168.1.2], [1], [], +-[ovn-nbctl: 30.0.0.2/24: should be an IPv4 address. ++[ovn-nbctl: 30.0.0.2/24: Not a valid IPv4 or IPv6 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2:80 192.168.1.2], [1], [], +-[ovn-nbctl: 30.0.0.2:80: should be an IPv4 address. ++[ovn-nbctl: 30.0.0.2:80: Not a valid IPv4 or IPv6 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2a], [1], [], + [ovn-nbctl: 192.168.1.2a: should be an IPv4 address or network. +@@ -431,19 +431,19 @@ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2/a], [1], [], + [ovn-nbctl: 192.168.1.2/a: should be an IPv4 address or network. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2a], [1], [], +-[ovn-nbctl: 192.168.1.2a: should be an IPv4 address. ++[ovn-nbctl: 192.168.1.2a: Not a valid IPv4 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1], [1], [], +-[ovn-nbctl: 192.168.1: should be an IPv4 address. ++[ovn-nbctl: 192.168.1: Not a valid IPv4 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2:80], [1], [], +-[ovn-nbctl: 192.168.1.2:80: should be an IPv4 address. ++[ovn-nbctl: 192.168.1.2:80: Not a valid IPv4 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2/24], [1], [], +-[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address. ++[ovn-nbctl: 192.168.1.2/24: Not a valid IPv4 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2/24], [1], [], +-[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address. ++[ovn-nbctl: 192.168.1.2/24: Not a valid IPv4 address. + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0], [1], [], + [ovn-nbctl: lr-nat-add with logical_port must also specify external_mac. +@@ -465,15 +465,23 @@ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0 00:00: + + dnl Add snat and dnat + AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24]) ++AT_CHECK([ovn-nbctl lr-nat-add lr0 snat fd01::1 fd11::/64]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2]) ++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat fd01::1 fd11::2]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2]) ++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat fd01::1 fd11::2]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3 lp0 00:00:00:01:02:03]) ++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat fd01::2 fd11::3 lp0 00:00:00:01:02:03]) + AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl + TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT + dnat 30.0.0.1 192.168.1.2 ++dnat fd01::1 fd11::2 + dnat_and_snat 30.0.0.1 192.168.1.2 + dnat_and_snat 30.0.0.2 192.168.1.3 00:00:00:01:02:03 lp0 ++dnat_and_snat fd01::1 fd11::2 ++dnat_and_snat fd01::2 fd11::3 00:00:00:01:02:03 lp0 + snat 30.0.0.1 192.168.1.0/24 ++snat fd01::1 fd11::/64 + ]) + AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24], [1], [], + [ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and logical_ip already exists +@@ -503,17 +511,26 @@ AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1. + AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl + TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT + dnat 30.0.0.1 192.168.1.2 ++dnat fd01::1 fd11::2 + dnat_and_snat 30.0.0.1 192.168.1.2 + dnat_and_snat 30.0.0.2 192.168.1.3 00:00:00:04:05:06 lp0 ++dnat_and_snat fd01::1 fd11::2 ++dnat_and_snat fd01::2 fd11::3 00:00:00:01:02:03 lp0 + snat 30.0.0.1 192.168.1.0/24 ++snat fd01::1 fd11::/64 + ]) + AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3]) ++AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat fd01::2 fd11::3]) + AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl + TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT + dnat 30.0.0.1 192.168.1.2 ++dnat fd01::1 fd11::2 + dnat_and_snat 30.0.0.1 192.168.1.2 + dnat_and_snat 30.0.0.2 192.168.1.3 ++dnat_and_snat fd01::1 fd11::2 ++dnat_and_snat fd01::2 fd11::3 + snat 30.0.0.1 192.168.1.0/24 ++snat fd01::1 fd11::/64 + ]) + + dnl Deletes the NATs +@@ -529,18 +546,24 @@ AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 192.168.10.0/24], [1], [], + AT_CHECK([ovn-nbctl --if-exists lr-nat-del lr0 snat 192.168.10.0/24]) + + AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.1]) ++AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat fd01::1]) + AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl + TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT + dnat 30.0.0.1 192.168.1.2 ++dnat fd01::1 fd11::2 + dnat_and_snat 30.0.0.2 192.168.1.3 ++dnat_and_snat fd01::2 fd11::3 + snat 30.0.0.1 192.168.1.0/24 ++snat fd01::1 fd11::/64 + ]) + + AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat]) + AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl + TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT + dnat_and_snat 30.0.0.2 192.168.1.3 ++dnat_and_snat fd01::2 fd11::3 + snat 30.0.0.1 192.168.1.0/24 ++snat fd01::1 fd11::/64 + ]) + + AT_CHECK([ovn-nbctl lr-nat-del lr0]) +-- +2.23.0 + diff --git a/SOURCES/0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch b/SOURCES/0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch new file mode 100644 index 0000000..efe7a14 --- /dev/null +++ b/SOURCES/0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch @@ -0,0 +1,42 @@ +From a152a434074ee38e38be6593502ff0058e67c2f4 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Thu, 14 Nov 2019 13:43:49 +0100 +Subject: [PATCH ovn 3/3] ovn-northd: Avoid empty address list when limiting + ARP/ND broadcast. + +Reported-by: Numan Siddique +Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.") +Signed-off-by: Dumitru Ceara +Signed-off-by: Numan Siddique + +(cherry-picked from upstream commit 998617cd85bfa1f03f2e734e479fd87f3e833797) +--- + ovn/northd/ovn-northd.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 03eb7a4..0e6f8f6 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -5331,10 +5331,14 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op, + } + } + +- build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op, +- sw_od, 75, lflows); +- build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op, +- sw_od, 75, lflows); ++ if (!sset_is_empty(&all_ips_v4)) { ++ build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op, ++ sw_od, 75, lflows); ++ } ++ if (!sset_is_empty(&all_ips_v6)) { ++ build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op, ++ sw_od, 75, lflows); ++ } + + sset_destroy(&all_ips_v4); + sset_destroy(&all_ips_v6); +-- +1.8.3.1 + diff --git a/SOURCES/0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch b/SOURCES/0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch new file mode 100644 index 0000000..cf61e84 --- /dev/null +++ b/SOURCES/0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch @@ -0,0 +1,848 @@ +From e392ac9d89e4a5f2d22a6a17a375cff41200d78c Mon Sep 17 00:00:00 2001 +From: Ankur Sharma +Date: Wed, 28 Aug 2019 01:55:30 +0000 +Subject: [PATCH 04/12] OVN: Vlan backed DVR N-S, redirect packet via localnet + port + +Background: +With 795d7f24ce0e ("OVN: Enable E-W Traffic, Vlan backed DVR"), we have added +support for E-W workflow for vlan backed DVRs. + +This series enables N-S workflow for vlan backed DVRs. + +Key difference between E-W and N-S traffic flow is that +N-S flow requires a gateway chassis. A gateway chassis +will be respondible for following: +a. Doing Network Address Translation (NAT). +b. Becoming entry and exit point for North->South + and South->North traffic respectively. + +OVN by default always uses overlay encapsulation to redirect +the packet to gateway chassis. This series will enable +the redirection to gateway chassis in the absence of encapsulation. + +This patch: +Achieves the bridged backed redirection by doing following: + +Sender Side: +------------ +a. For a remote port of type "chassisredirect" and if it + has redirect type as "bridged", then do not add tunnel + based redirection flow in table=32. + +b. In table=33, add a flow with priority=100, that would do following: + i. Change the metadata to that of gateway logical switch + (i.e logical switch attached to gateway logical router port). + ii. Change REG15 to point to localnet port of gateway logical switch. + iii. send to packet to table=65. + +c. In Table=65, packet will hit the existing priority=150 flow to send + the packet to physical bridge, while attaching vlan header (if configured) + and changing source mac to chassis mac. + +Receiver Side: +-------------- +a. No changes needed + +OVERALL PACKET FLOW: +Sender Side: +----------- +a. logical flow in lr_in_gw_redirect stage will ensure that + outport of the packet is chassisredirect port. + For example: + table=12(lr_in_gw_redirect ), priority=50 , match=(outport == "router-to-underlay"), action=(outport = "cr-router-to-underlay"; next;) + +b. After ingress pipeline, packet will enter the table=32, followed by table=33 + +c. Table=33, will send the packet to table=65. + +d. Table=65, will send the packet to uplink bridge + with destination mac of chassisredirect port and vlan + id of peer logical switch. + +Receiver Side: +------------- +a. Packet is received by the pipeline of peer logical switch. +b. Since destination mac is that of router port, hence packet will + enter the logical router pipeline. +c. Now, packet will go through regular logical router pipeline + (both ingress and egress). + +One caveat with the approach is that ttl will be decremented twice, +since the packets are going through logical router ingress pipeline +twice (once on sender chassis and again on gateway chassis). + +No changes needed for the reverse path. + +Change-Id: I3c36cbd02ff53ed3eaa8ee474a31c7d9f284362d +Signed-off-by: Ankur Sharma +Signed-off-by: Numan Siddique +--- + ovn/controller/physical.c | 255 +++++++++++++++++++++---------- + ovn/lib/ovn-util.c | 33 ++++ + ovn/lib/ovn-util.h | 5 + + ovn/ovn-architecture.7.xml | 72 +++++++++ + tests/ovn.at | 304 +++++++++++++++++++++++++++++++++++++ + 5 files changed, 586 insertions(+), 83 deletions(-) + +diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c +index 425110d0b..bf1d0d716 100644 +--- a/ovn/controller/physical.c ++++ b/ovn/controller/physical.c +@@ -227,6 +227,164 @@ get_zone_ids(const struct sbrec_port_binding *binding, + return zone_ids; + } + ++static void ++put_remote_port_redirect_bridged(const struct ++ sbrec_port_binding *binding, ++ const struct hmap *local_datapaths, ++ struct local_datapath *ld, ++ struct match *match, ++ struct ofpbuf *ofpacts_p, ++ struct ovn_desired_flow_table *flow_table) ++{ ++ if (strcmp(binding->type, "chassisredirect")) { ++ /* bridged based redirect is only supported for chassisredirect ++ * type remote ports. */ ++ return; ++ } ++ ++ struct eth_addr binding_mac; ++ bool is_valid_mac = extract_sbrec_binding_first_mac(binding, ++ &binding_mac); ++ if (!is_valid_mac) { ++ return; ++ } ++ ++ uint32_t ls_dp_key = 0; ++ for (int i = 0; i < ld->n_peer_ports; i++) { ++ const struct sbrec_port_binding *sport_binding = ld->peer_ports[i]; ++ const char *sport_peer_name = smap_get(&sport_binding->options, ++ "peer"); ++ const char *distributed_port = smap_get(&binding->options, ++ "distributed-port"); ++ ++ if (!strcmp(sport_peer_name, distributed_port)) { ++ ls_dp_key = sport_binding->datapath->tunnel_key; ++ break; ++ } ++ } ++ ++ if (!ls_dp_key) { ++ return; ++ } ++ ++ union mf_value value; ++ struct ofpact_mac *src_mac; ++ const struct sbrec_port_binding *ls_localnet_port; ++ ++ ls_localnet_port = get_localnet_port(local_datapaths, ls_dp_key); ++ ++ src_mac = ofpact_put_SET_ETH_SRC(ofpacts_p); ++ src_mac->mac = binding_mac; ++ ++ value.be64 = htonll(ls_dp_key); ++ ++ ofpact_put_set_field(ofpacts_p, mf_from_id(MFF_METADATA), ++ &value, NULL); ++ ++ value.be32 = htonl(ls_localnet_port->tunnel_key); ++ ofpact_put_set_field(ofpacts_p, mf_from_id(MFF_REG15), ++ &value, NULL); ++ ++ put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p); ++ ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0, ++ match, ofpacts_p, &binding->header_.uuid); ++ ++} ++ ++static void ++put_remote_port_redirect_overlay(const struct ++ sbrec_port_binding *binding, ++ bool is_ha_remote, ++ struct ha_chassis_ordered *ha_ch_ordered, ++ enum mf_field_id mff_ovn_geneve, ++ const struct chassis_tunnel *tun, ++ uint32_t port_key, ++ struct match *match, ++ struct ofpbuf *ofpacts_p, ++ struct ovn_desired_flow_table *flow_table) ++{ ++ if (!is_ha_remote) { ++ /* Setup encapsulation */ ++ const struct chassis_tunnel *rem_tun = ++ get_port_binding_tun(binding); ++ if (!rem_tun) { ++ return; ++ } ++ put_encapsulation(mff_ovn_geneve, tun, binding->datapath, ++ port_key, ofpacts_p); ++ /* Output to tunnel. */ ++ ofpact_put_OUTPUT(ofpacts_p)->port = rem_tun->ofport; ++ } else { ++ /* Make sure all tunnel endpoints use the same encapsulation, ++ * and set it up */ ++ for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) { ++ const struct sbrec_chassis *ch = ha_ch_ordered->ha_ch[i].chassis; ++ if (!ch) { ++ continue; ++ } ++ if (!tun) { ++ tun = chassis_tunnel_find(ch->name, NULL); ++ } else { ++ struct chassis_tunnel *chassis_tunnel = ++ chassis_tunnel_find(ch->name, NULL); ++ if (chassis_tunnel && ++ tun->type != chassis_tunnel->type) { ++ static struct vlog_rate_limit rl = ++ VLOG_RATE_LIMIT_INIT(1, 1); ++ VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis " ++ "with mixed encapsulations, only " ++ "uniform encapsulations are " ++ "supported.", binding->logical_port); ++ return; ++ } ++ } ++ } ++ if (!tun) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); ++ VLOG_ERR_RL(&rl, "No tunnel endpoint found for HA chassis in " ++ "HA chassis group of port %s", ++ binding->logical_port); ++ return; ++ } ++ ++ put_encapsulation(mff_ovn_geneve, tun, binding->datapath, ++ port_key, ofpacts_p); ++ ++ /* Output to tunnels with active/backup */ ++ struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p); ++ ++ for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) { ++ const struct sbrec_chassis *ch = ++ ha_ch_ordered->ha_ch[i].chassis; ++ if (!ch) { ++ continue; ++ } ++ tun = chassis_tunnel_find(ch->name, NULL); ++ if (!tun) { ++ continue; ++ } ++ if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); ++ VLOG_WARN_RL(&rl, "Remote endpoints for port beyond " ++ "BUNDLE_MAX_SLAVES"); ++ break; ++ } ++ ofpbuf_put(ofpacts_p, &tun->ofport, sizeof tun->ofport); ++ bundle = ofpacts_p->header; ++ bundle->n_slaves++; ++ } ++ ++ bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; ++ /* Although ACTIVE_BACKUP bundle algorithm seems to ignore ++ * the next two fields, those are always set */ ++ bundle->basis = 0; ++ bundle->fields = NX_HASH_FIELDS_ETH_SRC; ++ ofpact_finish_BUNDLE(ofpacts_p, &bundle); ++ } ++ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0, ++ match, ofpacts_p, &binding->header_.uuid); ++} ++ + static void + put_replace_router_port_mac_flows(struct ovsdb_idl_index + *sbrec_port_binding_by_name, +@@ -484,7 +642,8 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, + { + uint32_t dp_key = binding->datapath->tunnel_key; + uint32_t port_key = binding->tunnel_key; +- if (!get_local_datapath(local_datapaths, dp_key)) { ++ struct local_datapath *ld; ++ if (!(ld = get_local_datapath(local_datapaths, dp_key))) { + return; + } + +@@ -830,6 +989,10 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, + ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0, + &match, ofpacts_p, &binding->header_.uuid); + } else { ++ ++ const char *redirect_type = smap_get(&binding->options, ++ "redirect-type"); ++ + /* Remote port connected by tunnel */ + + /* Table 32, priority 100. +@@ -846,90 +1009,16 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + +- if (!is_ha_remote) { +- /* Setup encapsulation */ +- const struct chassis_tunnel *rem_tun = +- get_port_binding_tun(binding); +- if (!rem_tun) { +- goto out; +- } +- put_encapsulation(mff_ovn_geneve, tun, binding->datapath, +- port_key, ofpacts_p); +- /* Output to tunnel. */ +- ofpact_put_OUTPUT(ofpacts_p)->port = rem_tun->ofport; ++ if (redirect_type && !strcasecmp(redirect_type, "bridged")) { ++ put_remote_port_redirect_bridged(binding, local_datapaths, ++ ld, &match, ofpacts_p, ++ flow_table); + } else { +- /* Make sure all tunnel endpoints use the same encapsulation, +- * and set it up */ +- for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) { +- const struct sbrec_chassis *ch = +- ha_ch_ordered->ha_ch[i].chassis; +- if (!ch) { +- continue; +- } +- if (!tun) { +- tun = chassis_tunnel_find(ch->name, NULL); +- } else { +- struct chassis_tunnel *chassis_tunnel = +- chassis_tunnel_find(ch->name, NULL); +- if (chassis_tunnel && +- tun->type != chassis_tunnel->type) { +- static struct vlog_rate_limit rl = +- VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis " +- "with mixed encapsulations, only " +- "uniform encapsulations are " +- "supported.", +- binding->logical_port); +- goto out; +- } +- } +- } +- if (!tun) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_ERR_RL(&rl, "No tunnel endpoint found for HA chassis in " +- "HA chassis group of port %s", +- binding->logical_port); +- goto out; +- } +- +- put_encapsulation(mff_ovn_geneve, tun, binding->datapath, +- port_key, ofpacts_p); +- +- /* Output to tunnels with active/backup */ +- struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p); +- +- for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) { +- const struct sbrec_chassis *ch = +- ha_ch_ordered->ha_ch[i].chassis; +- if (!ch) { +- continue; +- } +- tun = chassis_tunnel_find(ch->name, NULL); +- if (!tun) { +- continue; +- } +- if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) { +- static struct vlog_rate_limit rl = +- VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_WARN_RL(&rl, "Remote endpoints for port beyond " +- "BUNDLE_MAX_SLAVES"); +- break; +- } +- ofpbuf_put(ofpacts_p, &tun->ofport, +- sizeof tun->ofport); +- bundle = ofpacts_p->header; +- bundle->n_slaves++; +- } +- +- bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; +- /* Although ACTIVE_BACKUP bundle algorithm seems to ignore +- * the next two fields, those are always set */ +- bundle->basis = 0; +- bundle->fields = NX_HASH_FIELDS_ETH_SRC; +- ofpact_finish_BUNDLE(ofpacts_p, &bundle); ++ put_remote_port_redirect_overlay(binding, is_ha_remote, ++ ha_ch_ordered, mff_ovn_geneve, ++ tun, port_key, &match, ofpacts_p, ++ flow_table); + } +- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0, +- &match, ofpacts_p, &binding->header_.uuid); + } + out: + if (ha_ch_ordered) { +diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c +index de745d73f..91ec3d62e 100644 +--- a/ovn/lib/ovn-util.c ++++ b/ovn/lib/ovn-util.c +@@ -16,6 +16,7 @@ + #include "ovn-util.h" + #include "dirs.h" + #include "openvswitch/vlog.h" ++#include "openvswitch/ofp-parse.h" + #include "ovn/lib/ovn-nb-idl.h" + #include "ovn/lib/ovn-sb-idl.h" + +@@ -272,6 +273,38 @@ extract_lrp_networks(const struct nbrec_logical_router_port *lrp, + return true; + } + ++bool ++extract_sbrec_binding_first_mac(const struct sbrec_port_binding *binding, ++ struct eth_addr *ea) ++{ ++ char *save_ptr = NULL; ++ bool ret = false; ++ ++ if (!binding->n_mac) { ++ return ret; ++ } ++ ++ char *tokstr = xstrdup(binding->mac[0]); ++ ++ for (char *token = strtok_r(tokstr, " ", &save_ptr); ++ token != NULL; ++ token = strtok_r(NULL, " ", &save_ptr)) { ++ ++ /* Return the first chassis mac. */ ++ char *err_str = str_to_mac(token, ea); ++ if (err_str) { ++ free(err_str); ++ continue; ++ } ++ ++ ret = true; ++ break; ++ } ++ ++ free(tokstr); ++ return ret; ++} ++ + void + destroy_lport_addresses(struct lport_addresses *laddrs) + { +diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h +index 6d5e1dfb5..8461db505 100644 +--- a/ovn/lib/ovn-util.h ++++ b/ovn/lib/ovn-util.h +@@ -21,6 +21,8 @@ + struct nbrec_logical_router_port; + struct sbrec_logical_flow; + struct uuid; ++struct eth_addr; ++struct sbrec_port_binding; + + struct ipv4_netaddr { + ovs_be32 addr; /* 192.168.10.123 */ +@@ -61,6 +63,9 @@ bool extract_lsp_addresses(const char *address, struct lport_addresses *); + bool extract_ip_addresses(const char *address, struct lport_addresses *); + bool extract_lrp_networks(const struct nbrec_logical_router_port *, + struct lport_addresses *); ++bool extract_sbrec_binding_first_mac(const struct sbrec_port_binding *binding, ++ struct eth_addr *ea); ++ + void destroy_lport_addresses(struct lport_addresses *); + + char *alloc_nat_zone_key(const struct uuid *key, const char *type); +diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml +index 771edb8d8..fa163c023 100644 +--- a/ovn/ovn-architecture.7.xml ++++ b/ovn/ovn-architecture.7.xml +@@ -1651,6 +1651,78 @@ + + + ++

    ++ VLAN based redirection ++ ++ As an enhancement to reside-on-redirect-chassis we support ++ VLAN based redirection as well. By setting ++ options:redirect-type to vlan to a gateway ++ chassis attached router port, user can enforce that redirected packet ++ should not use tunnel port but rather use localnet port of peer logical ++ switch to go out as vlan packet. ++

    ++ ++

    ++ Following happens for a VLAN based redirection: ++

    ++
      ++
    1. ++ On compute chassis, packet passes though logical router's ++ ingress pipeline. ++
    2. ++ ++
    3. ++ If logical outport is gateway chassis attached router port ++ then packet is "redirected" to gateway chassis using peer logical ++ switch's localnet port. ++
    4. ++ ++
    5. ++ This VLAN backed redirected packet has destination mac ++ as router port mac (the one to which gateway chassis is attached) and ++ vlan id is that of localnet port (peer logical switch of ++ the logical router port). ++
    6. ++ ++
    7. ++ On the gateway chassis packet will enter the logical router pipeline ++ again and this time it will passthrough egress pipeline as well. ++
    8. ++ ++
    9. ++ Reverse traffic packet flows stays the same. ++
    10. ++
    ++ ++

    ++ Some guidelines and expections with VLAN based redirection: ++

    ++ ++
      ++
    1. ++ Since router port mac is destination mac, hence it has to be ensured ++ that physical network learns it on ONLY from the gateway chassis. ++ Which means that ovn-chassis-mac-mappings should be ++ configure on all the compute nodes, so that physical network ++ never learn router port mac from compute nodes. ++
    2. ++ ++
    3. ++ Since packet enters logical router ingress pipeline twice ++ (once on compute chassis and again on gateway chassis), ++ hence ttl will be decremented twice. ++
    4. ++ ++
    5. ++ Default redirection type continues to be overlay. ++ User can switch the redirect-type between vlan ++ and overlay by changing the value of ++ options:redirect-type ++
    6. ++ ++
    ++ ++ +

    Life Cycle of a VTEP gateway

    + +

    +diff --git a/tests/ovn.at b/tests/ovn.at +index b7ee7beed..fe5d7f130 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -16296,3 +16296,307 @@ as hv4 ovs-appctl fdb/show br-phys + OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) + + AT_CLEANUP ++ ++AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S Ping]) ++ovn_start ++ ++# In this test cases we create 3 switches, all connected to same ++# physical network (through br-phys on each HV). LS1 and LS2 have ++# 1 VIF each. Each HV has 1 VIF port. The first digit ++# of VIF port name indicates the hypervisor it is bound to, e.g. ++# lp23 means VIF 3 on hv2. ++# ++# All the switches are connected to a logical router "router". ++# ++# Each switch's VLAN tag and their logical switch ports are: ++# - ls1: ++# - tagged with VLAN 101 ++# - ports: lp11 ++# - ls2: ++# - tagged with VLAN 201 ++# - ports: lp22 ++# - ls-underlay: ++# - tagged with VLAN 1000 ++# Note: a localnet port is created for each switch to connect to ++# physical network. ++ ++for i in 1 2; do ++ ls_name=ls$i ++ ovn-nbctl ls-add $ls_name ++ ln_port_name=ln$i ++ if test $i -eq 1; then ++ ovn-nbctl lsp-add $ls_name $ln_port_name "" 101 ++ elif test $i -eq 2; then ++ ovn-nbctl lsp-add $ls_name $ln_port_name "" 201 ++ fi ++ ovn-nbctl lsp-set-addresses $ln_port_name unknown ++ ovn-nbctl lsp-set-type $ln_port_name localnet ++ ovn-nbctl lsp-set-options $ln_port_name network_name=phys ++done ++ ++# lsp_to_ls LSP ++# ++# Prints the name of the logical switch that contains LSP. ++lsp_to_ls () { ++ case $1 in dnl ( ++ lp?[[11]]) echo ls1 ;; dnl ( ++ lp?[[12]]) echo ls2 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_hv () { ++ case $1 in dnl ( ++ vif[[1]]?) echo hv1 ;; dnl ( ++ vif[[2]]?) echo hv2 ;; dnl ( ++ vif?[[north]]?) echo hv4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++net_add n1 ++for i in 1 2; do ++ sim_add hv$i ++ as hv$i ++ ovs-vsctl add-br br-phys ++ ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++ ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i" ++ ovn_attach n1 br-phys 192.168.0.$i ++ ++ ovs-vsctl add-port br-int vif$i$i -- \ ++ set Interface vif$i$i external-ids:iface-id=lp$i$i \ ++ options:tx_pcap=hv$i/vif$i$i-tx.pcap \ ++ options:rxq_pcap=hv$i/vif$i$i-rx.pcap \ ++ ofport-request=$i$i ++ ++ lsp_name=lp$i$i ++ ls_name=$(lsp_to_ls $lsp_name) ++ ++ ovn-nbctl lsp-add $ls_name $lsp_name ++ ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i" ++ ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i ++ ++ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup]) ++ ++done ++ ++ovn-nbctl ls-add ls-underlay ++ovn-nbctl lsp-add ls-underlay ln3 "" 1000 ++ovn-nbctl lsp-set-addresses ln3 unknown ++ovn-nbctl lsp-set-type ln3 localnet ++ovn-nbctl lsp-set-options ln3 network_name=phys ++ ++ovn-nbctl ls-add ls-north ++ovn-nbctl lsp-add ls-north ln4 "" 1000 ++ovn-nbctl lsp-set-addresses ln4 unknown ++ovn-nbctl lsp-set-type ln4 localnet ++ovn-nbctl lsp-set-options ln4 network_name=phys ++ ++# Add a VM on ls-north ++ovn-nbctl lsp-add ls-north lp-north ++ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10" ++ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11 ++ ++# Add 3rd hypervisor ++sim_add hv3 ++as hv3 ovs-vsctl add-br br-phys ++as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++as hv3 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33" ++as hv3 ovn_attach n1 br-phys 192.168.0.3 ++ ++# Add 4th hypervisor ++sim_add hv4 ++as hv4 ovs-vsctl add-br br-phys ++as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++as hv4 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44" ++as hv4 ovn_attach n1 br-phys 192.168.0.4 ++ ++as hv4 ovs-vsctl add-port br-int vif-north -- \ ++ set Interface vif-north external-ids:iface-id=lp-north \ ++ options:tx_pcap=hv4/vif-north-tx.pcap \ ++ options:rxq_pcap=hv4/vif-north-rx.pcap \ ++ ofport-request=44 ++ ++ovn-nbctl lr-add router ++ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24 ++ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24 ++ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24 ++ ++ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router \ ++ options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router ++ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router \ ++ options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router ++ovn-nbctl lsp-add ls-underlay underlay-to-router -- set Logical_Switch_Port \ ++ underlay-to-router type=router \ ++ options:router-port=router-to-underlay \ ++ -- lsp-set-addresses underlay-to-router router ++ ++ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3 ++ovn-nbctl lrp-set-redirect-type router-to-underlay bridged ++ ++ovn-nbctl --wait=sb sync ++ ++ ++OVN_POPULATE_ARP ++ ++# lsp_to_ls LSP ++# ++# Prints the name of the logical switch that contains LSP. ++lsp_to_ls () { ++ case $1 in dnl ( ++ lp?[[11]]) echo ls1 ;; dnl ( ++ lp?[[12]]) echo ls2 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_ls () { ++ case $1 in dnl ( ++ vif?[[11]]) echo ls1 ;; dnl ( ++ vif?[[12]]) echo ls2 ;; dnl ( ++ vif-north) echo ls-north ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++hv_to_num () { ++ case $1 in dnl ( ++ hv1) echo 1 ;; dnl ( ++ hv2) echo 2 ;; dnl ( ++ hv3) echo 3 ;; dnl ( ++ hv4) echo 4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_num () { ++ case $1 in dnl ( ++ vif22) echo 22 ;; dnl ( ++ vif21) echo 21 ;; dnl ( ++ vif11) echo 11 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_hv () { ++ case $1 in dnl ( ++ vif[[1]]?) echo hv1 ;; dnl ( ++ vif[[2]]?) echo hv2 ;; dnl ( ++ vif-north) echo hv4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_lrp () { ++ echo router-to-`vif_to_ls $1` ++} ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++ ++test_ip() { ++ # This packet has bad checksums but logical L3 routing doesn't check. ++ local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 outport=$6 ++ local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 ++ shift; shift; shift; shift; shift ++ hv=`vif_to_hv $inport` ++ as $hv ovs-appctl netdev-dummy/receive $inport $packet ++ in_ls=`vif_to_ls $inport` ++ for outport; do ++ out_ls=`vif_to_ls $outport` ++ if test $in_ls = $out_ls; then ++ # Ports on the same logical switch receive exactly the same packet. ++ echo $packet ++ else ++ # Routing decrements TTL and updates source and dest MAC ++ # (and checksum). ++ out_lrp=`vif_to_lrp $outport` ++ # For North-South, packet will come via gateway chassis, i.e hv3 ++ if test $inport = vif-north; then ++ echo f00000000011aabbccddee3308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected ++ fi ++ if test $outport = vif-north; then ++ echo f0f00000001100000101020708004500001c000000003e110200${src_ip}${dst_ip}0035111100080000 >> $outport.expected ++ fi ++ fi >> $outport.expected ++ done ++} ++ ++# Dump a bunch of info helpful for debugging if there's a failure. ++ ++echo "------ OVN dump ------" ++ovn-nbctl show ++ovn-sbctl show ++ovn-sbctl list port_binding ++ovn-sbctl list mac_binding ++ ++echo "------ hv1 dump ------" ++as hv1 ovs-vsctl show ++as hv1 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv2 dump ------" ++as hv2 ovs-vsctl show ++as hv2 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv3 dump ------" ++as hv3 ovs-vsctl show ++as hv3 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv4 dump ------" ++as hv4 ovs-vsctl show ++as hv4 ovs-vsctl list Open_Vswitch ++ ++echo "Send traffic North to South" ++ ++sip=`ip_to_hex 172 31 0 10` ++dip=`ip_to_hex 192 168 1 1` ++test_ip vif-north f0f000000011 000001010207 $sip $dip vif11 ++ ++# Confirm that North to south traffic works fine. ++OVN_CHECK_PACKETS([hv1/vif11-tx.pcap], [vif11.expected]) ++ ++echo "Send traffic South to Nouth" ++sip=`ip_to_hex 192 168 1 1` ++dip=`ip_to_hex 172 31 0 10` ++test_ip vif11 f00000000011 000001010203 $sip $dip vif-north ++ ++# Confirm that South to North traffic works fine. ++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected]) ++ ++# Confirm that packets did not go out via tunnel port. ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=32 | grep NXM_NX_TUN_METADATA0 | grep n_packets=0 | wc -l], [0], [[0 ++]]) ++ ++# Confirm that packet went out via localnet port ++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=65 | grep priority=150 | grep src=00:00:01:01:02:07 | grep n_packets=1 | wc -l], [0], [[1 ++]]) ++ ++echo "----------- Post Traffic hv1 dump -----------" ++as hv1 ovs-ofctl dump-flows br-int ++as hv1 ovs-ofctl show br-phys ++as hv1 ovs-appctl fdb/show br-phys ++ ++echo "----------- Post Traffic hv2 dump -----------" ++as hv2 ovs-ofctl dump-flows br-int ++as hv2 ovs-ofctl show br-phys ++as hv2 ovs-appctl fdb/show br-phys ++ ++echo "----------- Post Traffic hv3 dump -----------" ++as hv3 ovs-ofctl dump-flows br-int ++as hv3 ovs-ofctl show br-phys ++as hv3 ovs-appctl fdb/show br-phys ++ ++echo "----------- Post Traffic hv4 dump -----------" ++as hv4 ovs-ofctl dump-flows br-int ++as hv4 ovs-ofctl show br-phys ++as hv4 ovs-appctl fdb/show br-phys ++ ++OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) ++ ++AT_CLEANUP +-- +2.23.0 + diff --git a/SOURCES/0004-northd-Add-lflows-for-IPv6-NAT.patch b/SOURCES/0004-northd-Add-lflows-for-IPv6-NAT.patch new file mode 100644 index 0000000..48c80ff --- /dev/null +++ b/SOURCES/0004-northd-Add-lflows-for-IPv6-NAT.patch @@ -0,0 +1,1064 @@ +From 0ed8c91b86b0d4bc01b4795155b25e85df1c6a9d Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Fri, 25 Oct 2019 16:47:21 -0400 +Subject: [PATCH 4/5] northd: Add lflows for IPv6 NAT. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 233 ++++++++++++---------- + ovn/northd/ovn-northd.c | 384 +++++++++++++++++++++++++++--------- + 2 files changed, 418 insertions(+), 199 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index b9ae28e14..38e6ecec5 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1498,11 +1498,55 @@ output; + +

  • +

    +- These flows reply to ARP requests for the virtual IP addresses +- configured in the router for DNAT or load balancing. For a +- configured DNAT IP address or a load balancer IPv4 VIP A, +- for each router port P with Ethernet +- address E, a priority-90 flow matches ++ Reply to IPv6 Neighbor Solicitations. These flows reply to ++ Neighbor Solicitation requests for the router's own IPv6 ++ address and populate the logical router's mac binding table. ++

    ++ ++

    ++ For each router port P that ++ owns IPv6 address A, solicited node address S, ++ and Ethernet address E, a priority-90 flow matches ++ inport == P && ++ nd_ns && ip6.dst == {A, E} && ++ nd.target == A with the following actions: ++

    ++ ++
    ++nd_na_router {
    ++    eth.src = E;
    ++    ip6.src = A;
    ++    nd.target = A;
    ++    nd.tll = E;
    ++    outport = inport;
    ++    flags.loopback = 1;
    ++    output;
    ++};
    ++        
    ++ ++

    ++ For the gateway port on a distributed logical router (where ++ one of the logical router ports specifies a ++ redirect-chassis), the above flows replying to ++ IPv6 Neighbor Solicitations are only programmed on the ++ gateway port instance on the redirect-chassis. ++ This behavior avoids generation of multiple replies from ++ different chassis, and allows upstream MAC learning to point ++ to the redirect-chassis. ++

    ++
  • ++ ++
  • ++

    ++ These flows reply to ARP requests or IPv6 neighbor solicitation ++ for the virtual IP addresses configured in the router for DNAT ++ or load balancing. ++

    ++ ++

    ++ IPv4: For a configured DNAT IP address or a load balancer ++ IPv4 VIP A, for each router port P with ++ Ethernet address E, a priority-90 flow matches + inport == P && arp.op == 1 && + arp.tpa == A (ARP request) + with the following actions: +@@ -1521,6 +1565,30 @@ flags.loopback = 1; + output; + + ++

    ++ IPv6: For a configured DNAT IP address or a load balancer ++ IPv6 VIP A, solicited node address S, ++ for each router port P with ++ Ethernet address E, a priority-90 flow matches ++ inport == P && nd_ns && ++ ip6.dst == {A, S} && ++ nd.target == A ++ with the following actions: ++

    ++ ++
    ++eth.dst = eth.src;
    ++nd_na {
    ++    eth.src = E;
    ++    nd.tll = E;
    ++    ip6.src = A;
    ++    nd.target = A;
    ++    outport = P;
    ++    flags.loopback = 1;
    ++    output;
    ++}
    ++        
    ++ +

    + For the gateway port on a distributed logical router with NAT + (where one of the logical router ports specifies a +@@ -1557,6 +1625,15 @@ eth.src = external_mac; + arp.sha = external_mac; + + ++

    ++ or in the case of IPv6 neighbor solicition: ++

    ++ ++
    ++eth.src = external_mac;
    ++nd.tll = external_mac;
    ++            
    ++ +

    + This behavior avoids generation of multiple ARP responses + from different chassis, and allows upstream MAC learning to +@@ -1566,68 +1643,6 @@ arp.sha = external_mac; + +

  • + +-
  • +-

    +- Reply to IPv6 Neighbor Solicitations. These flows reply to +- Neighbor Solicitation requests for the router's own IPv6 +- address and load balancing IPv6 VIPs and populate the logical +- router's mac binding table. +-

    +- +-

    +- For each router port P that +- owns IPv6 address A, solicited node address S, +- and Ethernet address E, a priority-90 flow matches +- inport == P && +- nd_ns && ip6.dst == {A, E} && +- nd.target == A with the following actions: +-

    +- +-
    +-nd_na_router {
    +-    eth.src = E;
    +-    ip6.src = A;
    +-    nd.target = A;
    +-    nd.tll = E;
    +-    outport = inport;
    +-    flags.loopback = 1;
    +-    output;
    +-};
    +-        
    +- +-

    +- For each router port P that has load balancing VIP +- A, solicited node address S, and Ethernet +- address E, a priority-90 flow matches +- inport == P && +- nd_ns && ip6.dst == {A, E} && +- nd.target == A with the following actions: +-

    +- +-
    +-nd_na {
    +-    eth.src = E;
    +-    ip6.src = A;
    +-    nd.target = A;
    +-    nd.tll = E;
    +-    outport = inport;
    +-    flags.loopback = 1;
    +-    output;
    +-};
    +-        
    +- +-

    +- For the gateway port on a distributed logical router (where +- one of the logical router ports specifies a +- redirect-chassis), the above flows replying to +- IPv6 Neighbor Solicitations are only programmed on the +- gateway port instance on the redirect-chassis. +- This behavior avoids generation of multiple replies from +- different chassis, and allows upstream MAC learning to point +- to the redirect-chassis. +-

    +-
  • +- +
  • + Priority-85 flows which drops the ARP and IPv6 Neighbor Discovery + packets. +@@ -1681,10 +1696,11 @@ nd_na { + handled by one of the flows above, which amounts to ICMP (other than + echo requests) and fragments with nonzero offsets. For each IP address + A owned by the router, a priority-60 flow matches +- ip4.dst == A and drops the traffic. An +- exception is made and the above flow is not added if the router +- port's own IP address is used to SNAT packets passing through that +- router. ++ ip4.dst == A or ++ ip6.dst == A ++ and drops the traffic. An exception is made and the above flow ++ is not added if the router port's own IP address is used to SNAT ++ packets passing through that router. +
  • + + +@@ -1778,23 +1794,26 @@ icmp6 { +

    + If the Gateway router has been configured to force SNAT any + previously DNATted packets to B, a priority-110 flow +- matches ip && ip4.dst == B with +- an action ct_snat; . ++ matches ip && ip4.dst == B or ++ ip && ip6.dst == B ++ with an action ct_snat; . +

    + +

    + If the Gateway router has been configured to force SNAT any + previously load-balanced packets to B, a priority-100 flow +- matches ip && ip4.dst == B with +- an action ct_snat; . ++ matches ip && ip4.dst == B or ++ ip && ip6.dst == B ++ with an action ct_snat; . +

    + +

    + For each NAT configuration in the OVN Northbound database, that asks + to change the source IP address of a packet from A to +- B, a priority-90 flow matches ip && +- ip4.dst == B with an action +- ct_snat; . ++ B, a priority-90 flow matches ++ ip && ip4.dst == B or ++ ip && ip6.dst == B ++ with an action ct_snat; . +

    + +

    +@@ -1812,7 +1831,9 @@ icmp6 { + For each configuration in the OVN Northbound database, that asks + to change the source IP address of a packet from A to + B, a priority-100 flow matches ip && +- ip4.dst == B && inport == GW, ++ ip4.dst == B && inport == GW or ++ ip && ++ ip6.dst == B && inport == GW + where GW is the logical router gateway port, with an + action ct_snat;. +

    +@@ -1826,10 +1847,12 @@ icmp6 { +

    + For each configuration in the OVN Northbound database, that asks + to change the source IP address of a packet from A to +- B, a priority-50 flow matches ip && +- ip4.dst == B with an action ++ B, a priority-50 flow matches ++ ip && ip4.dst == B or ++ ip && ip6.dst == B ++ with an action + REGBIT_NAT_REDIRECT = 1; next;. This flow is for +- east/west traffic to a NAT destination IPv4 address. By ++ east/west traffic to a NAT destination IPv4/IPv6 address. By + setting the REGBIT_NAT_REDIRECT flag, in the + ingress table Gateway Redirect this will trigger a + redirect to the instance of the gateway port on the +@@ -1875,25 +1898,27 @@ icmp6 { + For all the configured load balancing rules for a Gateway router or + Router with gateway port in OVN_Northbound database that + includes a L4 port PORT of protocol P and IPv4 +- address VIP, a priority-120 flow that matches on ++ or IPv6 address VIP, a priority-120 flow that matches on + ct.new && ip && ip4.dst == VIP + && P && P.dst == PORT +- with an action of ct_lb(args), +- where args contains comma separated IPv4 addresses (and +- optional port numbers) to load balance to. If the router is configured +- to force SNAT any load-balanced packets, the above action will be +- replaced by flags.force_snat_for_lb = 1; ++ (ip6.dst == VIP in the IPv6 case) ++ with an action of ct_lb(args), ++ where args contains comma separated IPv4 or IPv6 addresses ++ (and optional port numbers) to load balance to. If the router is ++ configured to force SNAT any load-balanced packets, the above action ++ will be replaced by flags.force_snat_for_lb = 1; + ct_lb(args);. + + +

  • + For all the configured load balancing rules for a router in + OVN_Northbound database that includes a L4 port +- PORT of protocol P and IPv4 address ++ PORT of protocol P and IPv4 or IPv6 address + VIP, a priority-120 flow that matches on + ct.est && ip && ip4.dst == VIP + && P && P.dst == PORT +- with an action of ct_dnat;. If the router is ++ (ip6.dst == VIP in the IPv6 case) ++ with an action of ct_dnat;. If the router is + configured to force SNAT any load-balanced packets, the above action + will be replaced by flags.force_snat_for_lb = 1; ct_dnat;. +
  • +@@ -1903,11 +1928,13 @@ icmp6 { + OVN_Northbound database that includes just an IP address + VIP to match on, a priority-110 flow that matches on + ct.new && ip && ip4.dst == +- VIP with an action of ++ VIP (ip6.dst == VIP in the ++ IPv6 case) with an action of + ct_lb(args), where args contains +- comma separated IPv4 addresses. If the router is configured to force +- SNAT any load-balanced packets, the above action will be replaced by +- flags.force_snat_for_lb = 1; ct_lb(args);. ++ comma separated IPv4 or IPv6 addresses. If the router is configured ++ to force SNAT any load-balanced packets, the above action will be ++ replaced by flags.force_snat_for_lb = 1; ++ ct_lb(args);. + + +
  • +@@ -1915,7 +1942,8 @@ icmp6 { + OVN_Northbound database that includes just an IP address + VIP to match on, a priority-110 flow that matches on + ct.est && ip && ip4.dst == +- VIP with an action of ct_dnat;. ++ VIP (or ip6.dst == VIP) ++ with an action of ct_dnat;. + If the router is configured to force SNAT any load-balanced + packets, the above action will be replaced by + flags.force_snat_for_lb = 1; ct_dnat;. +@@ -1929,7 +1957,8 @@ icmp6 { + For each configuration in the OVN Northbound database, that asks + to change the destination IP address of a packet from A to + B, a priority-100 flow matches ip && +- ip4.dst == A with an action ++ ip4.dst == A or ip && ++ ip6.dst == A with an action + flags.loopback = 1; ct_dnat(B);. If the + Gateway router is configured to force SNAT any DNATed packet, + the above action will be replaced by +@@ -1966,7 +1995,8 @@ icmp6 { + B, a priority-100 flow matches ip && + ip4.dst == B && inport == GW, + where GW is the logical router gateway port, with an +- action ct_dnat(B);. ++ action ct_dnat(B);. The match will ++ include ip6.dst == B in the IPv6 case. +

    + +

    +@@ -1979,9 +2009,10 @@ icmp6 { + For each configuration in the OVN Northbound database, that asks + to change the destination IP address of a packet from A to + B, a priority-50 flow matches ip && +- ip4.dst == B with an action ++ ip4.dst == B or ip && ++ ip6.dst == B with an action + REGBIT_NAT_REDIRECT = 1; next;. This flow is for +- east/west traffic to a NAT destination IPv4 address. By ++ east/west traffic to a NAT destination IPv4/IPv6 address. By + setting the REGBIT_NAT_REDIRECT flag, in the + ingress table Gateway Redirect this will trigger a + redirect to the instance of the gateway port on the +@@ -2136,8 +2167,8 @@ logical_ip{0,1} = LIP{0,1}; +

    + eth.dst = MAC0;
    + eth.src = MAC1;
    +-reg0 = ip4.dst;
    +-reg1 = EIP1;
    ++reg0 = ip4.dst; /* xxreg0 = ip6.dst; in the IPv6 case */
    ++reg1 = EIP1; /* xxreg1 in the IPv6 case */
    + outport = redirect-chassis-port;
    + REGBIT_DISTRIBUTED_NAT = 1; next;.
    +         
    +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 7cfeb60be..84b2a9ff1 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -66,6 +66,15 @@ struct northd_context { + struct ovsdb_idl_index *sbrec_ip_mcast_by_dp; + }; + ++/* An IPv4 or IPv6 address */ ++struct v46_ip { ++ int family; ++ union { ++ ovs_be32 ipv4; ++ struct in6_addr ipv6; ++ }; ++}; ++ + static const char *ovnnb_db; + static const char *ovnsb_db; + static const char *unixctl_path; +@@ -2266,6 +2275,15 @@ get_nat_addresses(const struct ovn_port *op, size_t *n) + break; + } + } ++ if (!is_router_ip) { ++ for (size_t j = 0; j < op->lrp_networks.n_ipv6_addrs; j++) { ++ if (!strcmp(nat->external_ip, ++ op->lrp_networks.ipv6_addrs[j].addr_s)) { ++ is_router_ip = true; ++ break; ++ } ++ } ++ } + + if (!is_router_ip) { + ds_put_format(&c_addresses, " %s", nat->external_ip); +@@ -6013,9 +6031,28 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op) + continue; + } + ++ /* Determine if we need to create IPv4 or IPv6 flows */ ++ ovs_be32 ip; ++ struct in6_addr ipv6; ++ int family = AF_INET; ++ if (!ip_parse(nat->external_ip, &ip) || !ip) { ++ family = AF_INET6; ++ if (!ipv6_parse(nat->external_ip, &ipv6)) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration " ++ "for router %s", nat->external_ip, op->key); ++ /* We'll create IPv6 flows anyway, but the address ++ * is probably bogus ... */ ++ } ++ } ++ + ds_put_format(&match, "inport == %s && " +- "ip4.src == %s && ip4.dst == %s", +- op->json_key, nat->logical_ip, nat->external_ip); ++ "ip%s.src == %s && ip%s.dst == %s", ++ op->json_key, ++ family == AF_INET ? "4" : "6", ++ nat->logical_ip, ++ family == AF_INET ? "4" : "6", ++ nat->external_ip); + ds_put_format(&actions, "outport = %s; eth.dst = %s; " + REGBIT_DISTRIBUTED_NAT" = 1; " + REGBIT_NAT_REDIRECT" = 0; next;", +@@ -6033,17 +6070,38 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op) + !nat2->external_mac || !nat2->external_ip) + continue; + ++ family = AF_INET; ++ if (!ip_parse(nat2->external_ip, &ip) || !ip) { ++ family = AF_INET6; ++ if (!ipv6_parse(nat2->external_ip, &ipv6)) { ++ static struct vlog_rate_limit rl = ++ VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration " ++ "for router %s", nat2->external_ip, op->key); ++ /* We'll create IPv6 flows anyway, but the address ++ * is probably bogus ... */ ++ } ++ } ++ + ds_put_format(&match, "inport == %s && " +- "ip4.src == %s && ip4.dst == %s", +- op->json_key, nat->logical_ip, nat2->external_ip); ++ "ip%s.src == %s && ip%s.dst == %s", ++ op->json_key, ++ family == AF_INET ? "4" : "6", ++ nat->logical_ip, ++ family == AF_INET ? "4" : "6", ++ nat2->external_ip); + ds_put_format(&actions, "outport = %s; " + "eth.src = %s; eth.dst = %s; " +- "reg0 = ip4.dst; reg1 = %s; " ++ "%sreg0 = ip%s.dst; %sreg1 = %s; " + REGBIT_DISTRIBUTED_NAT" = 1; " + REGBIT_NAT_REDIRECT" = 0; next;", + op->od->l3dgw_port->json_key, + op->od->l3dgw_port->lrp_networks.ea_s, +- nat2->external_mac, nat->external_ip); ++ nat2->external_mac, ++ family == AF_INET ? "" : "xx", ++ family == AF_INET ? "4" : "6", ++ family == AF_INET ? "" : "xx", ++ nat->external_ip); + ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400, + ds_cstr(&match), ds_cstr(&actions)); + ds_clear(&match); +@@ -6280,7 +6338,8 @@ op_put_v6_networks(struct ds *ds, const struct ovn_port *op) + } + + static const char * +-get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ovs_be32 *ip) ++get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ++ struct v46_ip *ip) + { + char *key = xasprintf("%s_force_snat_ip", key_type); + const char *ip_address = smap_get(&od->nbr->options, key); +@@ -6288,19 +6347,27 @@ get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ovs_be32 *ip) + + if (ip_address) { + ovs_be32 mask; +- char *error = ip_parse_masked(ip_address, ip, &mask); ++ ip->family = AF_INET; ++ char *error = ip_parse_masked(ip_address, &ip->ipv4, &mask); + if (error || mask != OVS_BE32_MAX) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); +- VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"", +- ip_address, UUID_ARGS(&od->key)); + free(error); +- *ip = 0; +- return NULL; ++ struct in6_addr mask_v6, v6_exact = IN6ADDR_EXACT_INIT; ++ ip->family = AF_INET6; ++ error = ipv6_parse_masked(ip_address, &ip->ipv6, &mask_v6); ++ if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"", ++ ip_address, UUID_ARGS(&od->key)); ++ memset(ip, 0, sizeof *ip); ++ ip->family = AF_UNSPEC; ++ return NULL; ++ } + } + return ip_address; + } + +- *ip = 0; ++ memset(ip, 0, sizeof *ip); ++ ip->family = AF_UNSPEC; + return NULL; + } + +@@ -6866,11 +6933,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + /* A gateway router can have 2 SNAT IP addresses to force DNATed and + * LBed traffic respectively to be SNATed. In addition, there can be + * a number of SNAT rules in the NAT table. */ +- ovs_be32 *snat_ips = xmalloc(sizeof *snat_ips * +- (op->od->nbr->n_nat + 2)); ++ struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips ++ * (op->od->nbr->n_nat + 2)); + size_t n_snat_ips = 0; + +- ovs_be32 snat_ip; ++ struct v46_ip snat_ip; + const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat", + &snat_ip); + if (dnat_force_snat_ip) { +@@ -6889,44 +6956,85 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + nat = op->od->nbr->nat[i]; + + ovs_be32 ip; ++ struct in6_addr ipv6; ++ bool is_v6 = false; + if (!ip_parse(nat->external_ip, &ip) || !ip) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); +- VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration " +- "for router %s", nat->external_ip, op->key); +- continue; ++ if (!ipv6_parse(nat->external_ip, &ipv6)) { ++ static struct vlog_rate_limit rl = ++ VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration " ++ "for router %s", nat->external_ip, op->key); ++ continue; ++ } ++ is_v6 = true; + } + + if (!strcmp(nat->type, "snat")) { +- snat_ips[n_snat_ips++] = ip; ++ if (is_v6) { ++ snat_ips[n_snat_ips].family = AF_INET6; ++ snat_ips[n_snat_ips++].ipv6 = ipv6; ++ } else { ++ snat_ips[n_snat_ips].family = AF_INET; ++ snat_ips[n_snat_ips++].ipv4 = ip; ++ } + continue; + } + +- /* ARP handling for external IP addresses. ++ /* ARP / ND handling for external IP addresses. + * + * DNAT IP addresses are external IP addresses that need ARP + * handling. */ ++ char addr_s[INET6_ADDRSTRLEN + 1]; + ds_clear(&match); +- ds_put_format(&match, +- "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1", +- op->json_key, IP_ARGS(ip)); +- + ds_clear(&actions); +- ds_put_format(&actions, +- "eth.dst = eth.src; " +- "arp.op = 2; /* ARP reply */ " +- "arp.tha = arp.sha; "); ++ if (is_v6) { ++ /* For ND solicitations, we need to listen for both the ++ * unicast IPv6 address and its all-nodes multicast address, ++ * but always respond with the unicast IPv6 address. */ ++ char sn_addr_s[INET6_ADDRSTRLEN + 1]; ++ struct in6_addr sn_addr; ++ in6_addr_solicited_node(&sn_addr, &ipv6); ++ ipv6_string_mapped(sn_addr_s, &sn_addr); ++ ipv6_string_mapped(addr_s, &ipv6); ++ ++ ds_put_format(&match, "inport == %s && " ++ "nd_ns && ip6.dst == {%s, %s} && nd.target == %s", ++ op->json_key, addr_s, sn_addr_s, addr_s); + ++ ds_put_format(&actions, ++ "eth.dst = eth.src; " ++ "nd_na { "); ++ } else { ++ ds_put_format(&match, ++ "inport == %s " ++ "&& arp.tpa == "IP_FMT" && arp.op == 1", ++ op->json_key, IP_ARGS(ip)); ++ ++ ds_put_format(&actions, ++ "eth.dst = eth.src; " ++ "arp.op = 2; /* ARP reply */ " ++ "arp.tha = arp.sha; "); ++ } + if (op->od->l3dgw_port && op == op->od->l3dgw_port) { + struct eth_addr mac; + if (nat->external_mac && + eth_addr_from_string(nat->external_mac, &mac) + && nat->logical_port) { + /* distributed NAT case, use nat->external_mac */ +- ds_put_format(&actions, +- "eth.src = "ETH_ADDR_FMT"; " +- "arp.sha = "ETH_ADDR_FMT"; ", +- ETH_ADDR_ARGS(mac), +- ETH_ADDR_ARGS(mac)); ++ if (is_v6) { ++ ds_put_format(&actions, ++ "eth.src = "ETH_ADDR_FMT"; " ++ "nd.tll = "ETH_ADDR_FMT"; ", ++ ETH_ADDR_ARGS(mac), ++ ETH_ADDR_ARGS(mac)); ++ ++ } else { ++ ds_put_format(&actions, ++ "eth.src = "ETH_ADDR_FMT"; " ++ "arp.sha = "ETH_ADDR_FMT"; ", ++ ETH_ADDR_ARGS(mac), ++ ETH_ADDR_ARGS(mac)); ++ } + /* Traffic with eth.src = nat->external_mac should only be + * sent from the chassis where nat->logical_port is + * resident, so that upstream MAC learning points to the +@@ -6935,11 +7043,20 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_put_format(&match, " && is_chassis_resident(\"%s\")", + nat->logical_port); + } else { +- ds_put_format(&actions, +- "eth.src = %s; " +- "arp.sha = %s; ", +- op->lrp_networks.ea_s, +- op->lrp_networks.ea_s); ++ if (is_v6) { ++ ds_put_format(&actions, ++ "eth.src = %s; " ++ "nd.tll = %s; ", ++ op->lrp_networks.ea_s, ++ op->lrp_networks.ea_s); ++ ++ } else { ++ ds_put_format(&actions, ++ "eth.src = %s; " ++ "arp.sha = %s; ", ++ op->lrp_networks.ea_s, ++ op->lrp_networks.ea_s); ++ } + /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s + * should only be sent from the "redirect-chassis", so that + * upstream MAC learning points to the "redirect-chassis". +@@ -6950,21 +7067,40 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + op->od->l3redirect_port->json_key); + } + } ++ } else { ++ if (is_v6) { ++ ds_put_format(&actions, ++ "eth.src = %s; " ++ "nd.tll = %s; ", ++ op->lrp_networks.ea_s, ++ op->lrp_networks.ea_s); ++ } else { ++ ds_put_format(&actions, ++ "eth.src = %s; " ++ "arp.sha = %s; ", ++ op->lrp_networks.ea_s, ++ op->lrp_networks.ea_s); ++ } ++ } ++ if (is_v6) { ++ ds_put_format(&actions, ++ "ip6.src = %s; " ++ "nd.target = %s; " ++ "outport = %s; " ++ "flags.loopback = 1; " ++ "output; " ++ "};", ++ addr_s, addr_s, op->json_key); + } else { + ds_put_format(&actions, +- "eth.src = %s; " +- "arp.sha = %s; ", +- op->lrp_networks.ea_s, +- op->lrp_networks.ea_s); ++ "arp.tpa = arp.spa; " ++ "arp.spa = "IP_FMT"; " ++ "outport = %s; " ++ "flags.loopback = 1; " ++ "output;", ++ IP_ARGS(ip), ++ op->json_key); + } +- ds_put_format(&actions, +- "arp.tpa = arp.spa; " +- "arp.spa = "IP_FMT"; " +- "outport = %s; " +- "flags.loopback = 1; " +- "output;", +- IP_ARGS(ip), +- op->json_key); + ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, + ds_cstr(&match), ds_cstr(&actions)); + } +@@ -7021,7 +7157,36 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + bool snat_ip_is_router_ip = false; + for (int j = 0; j < n_snat_ips; j++) { + /* Packets to SNAT IPs should not be dropped. */ +- if (op->lrp_networks.ipv4_addrs[i].addr == snat_ips[j]) { ++ if (snat_ips[j].family == AF_INET ++ && op->lrp_networks.ipv4_addrs[i].addr ++ == snat_ips[j].ipv4) { ++ snat_ip_is_router_ip = true; ++ break; ++ } ++ } ++ if (snat_ip_is_router_ip) { ++ continue; ++ } ++ ds_put_format(&match, "%s, ", ++ op->lrp_networks.ipv4_addrs[i].addr_s); ++ has_drop_ips = true; ++ } ++ if (has_drop_ips) { ++ ds_chomp(&match, ' '); ++ ds_chomp(&match, ','); ++ ds_put_cstr(&match, "} || ip6.dst == {"); ++ } else { ++ ds_clear(&match); ++ ds_put_cstr(&match, "ip6.dst == {"); ++ } ++ ++ for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { ++ bool snat_ip_is_router_ip = false; ++ for (int j = 0; j < n_snat_ips; j++) { ++ /* Packets to SNAT IPs should not be dropped. */ ++ if (snat_ips[j].family == AF_INET6 ++ && !memcmp(&op->lrp_networks.ipv6_addrs[i].addr, ++ &snat_ips[j].ipv6, sizeof snat_ips[j].ipv6)) { + snat_ip_is_router_ip = true; + break; + } +@@ -7030,9 +7195,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + ds_put_format(&match, "%s, ", +- op->lrp_networks.ipv4_addrs[i].addr_s); ++ op->lrp_networks.ipv6_addrs[i].addr_s); + has_drop_ips = true; + } ++ + ds_chomp(&match, ' '); + ds_chomp(&match, ','); + ds_put_cstr(&match, "}"); +@@ -7059,14 +7225,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + } + + if (op->lrp_networks.n_ipv6_addrs) { +- /* L3 admission control: drop packets that originate from an +- * IPv6 address owned by the router (priority 100). */ +- ds_clear(&match); +- ds_put_cstr(&match, "ip6.src == "); +- op_put_v6_networks(&match, op); +- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100, +- ds_cstr(&match), "drop;"); +- + /* ICMPv6 echo reply. These flows reply to echo requests + * received for the router's IP address. */ + ds_clear(&match); +@@ -7083,13 +7241,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + "next; "); + ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, + ds_cstr(&match), ds_cstr(&actions)); +- +- /* Drop IPv6 traffic to this router. */ +- ds_clear(&match); +- ds_put_cstr(&match, "ip6.dst == "); +- op_put_v6_networks(&match, op); +- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60, +- ds_cstr(&match), "drop;"); + } + + /* ND reply. These flows reply to ND solicitations for the +@@ -7231,11 +7382,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + +- ovs_be32 snat_ip; ++ struct v46_ip snat_ip, lb_snat_ip; + const char *dnat_force_snat_ip = get_force_snat_ip(od, "dnat", + &snat_ip); + const char *lb_force_snat_ip = get_force_snat_ip(od, "lb", +- &snat_ip); ++ &lb_snat_ip); + + for (int i = 0; i < od->nbr->n_nat; i++) { + const struct nbrec_nat *nat; +@@ -7243,21 +7394,38 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + nat = od->nbr->nat[i]; + + ovs_be32 ip, mask; ++ struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT; ++ bool is_v6 = false; + + char *error = ip_parse_masked(nat->external_ip, &ip, &mask); + if (error || mask != OVS_BE32_MAX) { +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); +- VLOG_WARN_RL(&rl, "bad external ip %s for nat", +- nat->external_ip); + free(error); +- continue; ++ error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6); ++ if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) { ++ /* Invalid for both IPv4 and IPv6 */ ++ static struct vlog_rate_limit rl = ++ VLOG_RATE_LIMIT_INIT(5, 1); ++ VLOG_WARN_RL(&rl, "bad external ip %s for nat", ++ nat->external_ip); ++ free(error); ++ continue; ++ } ++ /* It was an invalid IPv4 address, but valid IPv6. ++ * Treat the rest of the handling of this NAT rule ++ * as IPv6. */ ++ is_v6 = true; + } + + /* Check the validity of nat->logical_ip. 'logical_ip' can + * be a subnet when the type is "snat". */ +- error = ip_parse_masked(nat->logical_ip, &ip, &mask); ++ if (is_v6) { ++ error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6); ++ } else { ++ error = ip_parse_masked(nat->logical_ip, &ip, &mask); ++ } + if (!strcmp(nat->type, "snat")) { + if (error) { ++ /* Invalid for both IPv4 and IPv6 */ + static struct vlog_rate_limit rl = + VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat " +@@ -7267,7 +7435,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + } else { +- if (error || mask != OVS_BE32_MAX) { ++ if (error || (!is_v6 && mask != OVS_BE32_MAX) ++ || (is_v6 && memcmp(&mask_v6, &v6_exact, ++ sizeof mask_v6))) { ++ /* Invalid for both IPv4 and IPv6 */ + static struct vlog_rate_limit rl = + VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad ip %s for dnat in router " +@@ -7308,7 +7479,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (!od->l3dgw_port) { + /* Gateway router. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s", ++ ds_put_format(&match, "ip && ip%s.dst == %s", ++ is_v6 ? "6" : "4", + nat->external_ip); + ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90, + ds_cstr(&match), "ct_snat;"); +@@ -7317,8 +7489,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + /* Traffic received on l3dgw_port is subject to NAT. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s" ++ ds_put_format(&match, "ip && ip%s.dst == %s" + " && inport == %s", ++ is_v6 ? "6" : "4", + nat->external_ip, + od->l3dgw_port->json_key); + if (!distributed && od->l3redirect_port) { +@@ -7334,7 +7507,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * redirected to the central instance of the l3dgw_port + * for NAT processing. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s", ++ ds_put_format(&match, "ip && ip%s.dst == %s", ++ is_v6 ? "6" : "4", + nat->external_ip); + ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50, + ds_cstr(&match), +@@ -7353,7 +7527,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * We need to set flags.loopback because the router can + * send the packet back through the same interface. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s", ++ ds_put_format(&match, "ip && ip%s.dst == %s", ++ is_v6 ? "6" : "4", + nat->external_ip); + ds_clear(&actions); + if (dnat_force_snat_ip) { +@@ -7372,8 +7547,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + /* Traffic received on l3dgw_port is subject to NAT. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s" ++ ds_put_format(&match, "ip && ip%s.dst == %s" + " && inport == %s", ++ is_v6 ? "6" : "4", + nat->external_ip, + od->l3dgw_port->json_key); + if (!distributed && od->l3redirect_port) { +@@ -7392,7 +7568,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * redirected to the central instance of the l3dgw_port + * for NAT processing. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s", ++ ds_put_format(&match, "ip && ip%s.dst == %s", ++ is_v6 ? "6" : "4", + nat->external_ip); + ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50, + ds_cstr(&match), +@@ -7411,8 +7588,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (od->l3dgw_port && (!strcmp(nat->type, "dnat") + || !strcmp(nat->type, "dnat_and_snat"))) { + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.src == %s" ++ ds_put_format(&match, "ip && ip%s.src == %s" + " && outport == %s", ++ is_v6 ? "6" : "4", + nat->logical_ip, + od->l3dgw_port->json_key); + if (!distributed && od->l3redirect_port) { +@@ -7439,7 +7617,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (!od->l3dgw_port) { + /* Gateway router. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.src == %s", ++ ds_put_format(&match, "ip && ip%s.src == %s", ++ is_v6 ? "6" : "4", + nat->logical_ip); + ds_clear(&actions); + ds_put_format(&actions, "ct_snat(%s);", nat->external_ip); +@@ -7455,8 +7634,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + /* Distributed router. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.src == %s" ++ ds_put_format(&match, "ip && ip%s.src == %s" + " && outport == %s", ++ is_v6 ? "6" : "4", + nat->logical_ip, + od->l3dgw_port->json_key); + if (!distributed && od->l3redirect_port) { +@@ -7505,7 +7685,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * can be applied in a distributed manner. */ + if (distributed) { + ds_clear(&match); +- ds_put_format(&match, "ip4.src == %s && outport == %s", ++ ds_put_format(&match, "ip%s.src == %s && outport == %s", ++ is_v6 ? "6" : "4", + nat->logical_ip, + od->l3dgw_port->json_key); + ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100, +@@ -7532,9 +7713,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + ds_clear(&match); + ds_put_format(&match, "is_chassis_resident(\"%s\") && " +- "ip4.src == %s && ip4.dst == %s", +- nat->logical_port, nat2->external_ip, +- nat->external_ip); ++ "ip%s.src == %s && ip%s.dst == %s", ++ nat->logical_port, ++ is_v6 ? "6" : "4", nat2->external_ip, ++ is_v6 ? "6" : "4", nat->external_ip); + ds_clear(&actions); + ds_put_format(&actions, + "inport = outport; outport = \"\"; " +@@ -7546,8 +7728,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + + ds_clear(&match); + ds_put_format(&match, +- "ip4.src == %s && ip4.dst == %s", +- nat2->external_ip, nat->external_ip); ++ "ip%s.src == %s && ip%s.dst == %s", ++ is_v6 ? "6" : "4", nat2->external_ip, ++ is_v6 ? "6" : "4", nat->external_ip); + ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200, + ds_cstr(&match), "next;"); + ds_clear(&match); +@@ -7555,7 +7738,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + } + + ds_clear(&match); +- ds_put_format(&match, "ip4.dst == %s && outport == %s", ++ ds_put_format(&match, "ip%s.dst == %s && outport == %s", ++ is_v6 ? "6" : "4", + nat->external_ip, + od->l3dgw_port->json_key); + ds_clear(&actions); +@@ -7579,7 +7763,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * gateway router (as set in options:dnat_force_snat_ip) is seen, + * UNSNAT it. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s", dnat_force_snat_ip); ++ ds_put_format(&match, "ip && ip%s.dst == %s", ++ snat_ip.family == AF_INET ? "4" : "6", ++ dnat_force_snat_ip); + ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110, + ds_cstr(&match), "ct_snat;"); + +@@ -7598,7 +7784,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + * gateway router (as set in options:lb_force_snat_ip) is seen, + * UNSNAT it. */ + ds_clear(&match); +- ds_put_format(&match, "ip && ip4.dst == %s", lb_force_snat_ip); ++ ds_put_format(&match, "ip && ip%s.dst == %s", ++ lb_snat_ip.family == AF_INET ? "4" : "6", ++ lb_force_snat_ip); + ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100, + ds_cstr(&match), "ct_snat;"); + +@@ -7700,7 +7888,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (addr_family == AF_INET) { + ds_put_format(&match, "ip && ip4.dst == %s", + ip_address); +- } else { ++ } else if (addr_family == AF_INET6) { + ds_put_format(&match, "ip && ip6.dst == %s", + ip_address); + } +@@ -7720,7 +7908,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (addr_family == AF_INET) { + ds_put_format(&match, "ip && ip4.dst == %s", + ip_address); +- } else { ++ } else if (addr_family == AF_INET6) { + ds_put_format(&match, "ip && ip6.dst == %s", + ip_address); + } +-- +2.23.0 + diff --git a/SOURCES/0004-northd-add-empty_lb-controller_event-for-logical-rou.patch b/SOURCES/0004-northd-add-empty_lb-controller_event-for-logical-rou.patch new file mode 100644 index 0000000..f54c103 --- /dev/null +++ b/SOURCES/0004-northd-add-empty_lb-controller_event-for-logical-rou.patch @@ -0,0 +1,195 @@ +From ef13dd074f46a57440c636cba618cc2bb623858a Mon Sep 17 00:00:00 2001 +Message-Id: +From: Lorenzo Bianconi +Date: Tue, 10 Sep 2019 19:00:58 +0200 +Subject: [PATCH ovn] northd: add empty_lb controller_event for logical router + +Add empty load balancer controller_event support to logical router +pipeline. Update northd documentation even for logical switch pipeline + +Signed-off-by: Lorenzo Bianconi +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique +--- + northd/ovn-northd.8.xml | 10 ++++++++ + northd/ovn-northd.c | 24 ++++++++++++------ + tests/ovn.at | 56 +++++++++++++++++++++++++++++++++++------ + 3 files changed, 76 insertions(+), 14 deletions(-) + +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1748,6 +1748,16 @@ icmp6 { + +
      +
    • ++ If controller_event has been enabled for all the configured load ++ balancing rules for a Gateway router or Router with gateway port ++ in OVN_Northbound database that does not have configured ++ backends, a priority-130 flow is added to trigger ovn-controller events ++ whenever the chassis receives a packet for that particular VIP. ++ If event-elb meter has been previously created, it will be ++ associated to the empty_lb logical flow ++
    • ++ ++
    • + For all the configured load balancing rules for a Gateway router or + Router with gateway port in OVN_Northbound database that + includes a L4 port PORT of protocol P and IPv4 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -5998,9 +5998,17 @@ get_force_snat_ip(struct ovn_datapath *o + static void + add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od, + struct ds *match, struct ds *actions, int priority, +- const char *lb_force_snat_ip, char *backend_ips, +- bool is_udp, int addr_family) ++ const char *lb_force_snat_ip, struct smap_node *lb_info, ++ bool is_udp, int addr_family, char *ip_addr, ++ uint16_t l4_port, struct nbrec_load_balancer *lb, ++ struct shash *meter_groups) + { ++ char *backend_ips = lb_info->value; ++ ++ build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb, ++ l4_port, addr_family, S_ROUTER_IN_DNAT, ++ meter_groups); ++ + /* A match and actions for new connections. */ + char *new_match = xasprintf("ct.new && %s", ds_cstr(match)); + if (lb_force_snat_ip) { +@@ -6158,7 +6166,7 @@ copy_ra_to_sb(struct ovn_port *op, const + + static void + build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, +- struct hmap *lflows) ++ struct hmap *lflows, struct shash *meter_groups) + { + /* This flow table structure is documented in ovn-northd(8), so please + * update ovn-northd.8.xml if you change anything. */ +@@ -7371,7 +7379,6 @@ build_lrouter_flows(struct hmap *datapat + ds_put_format(&match, "ip && ip6.dst == %s", + ip_address); + } +- free(ip_address); + + int prio = 110; + bool is_udp = lb->protocol && !strcmp(lb->protocol, "udp") ? +@@ -7392,8 +7399,11 @@ build_lrouter_flows(struct hmap *datapat + od->l3redirect_port->json_key); + } + add_router_lb_flow(lflows, od, &match, &actions, prio, +- lb_force_snat_ip, node->value, is_udp, +- addr_family); ++ lb_force_snat_ip, node, is_udp, ++ addr_family, ip_address, port, lb, ++ meter_groups); ++ ++ free(ip_address); + } + } + sset_destroy(&all_ips); +@@ -8099,7 +8109,7 @@ build_lflows(struct northd_context *ctx, + + build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups, + igmp_groups, meter_groups); +- build_lrouter_flows(datapaths, ports, &lflows); ++ build_lrouter_flows(datapaths, ports, &lflows, meter_groups); + + /* Push changes to the Logical_Flow table to database. */ + const struct sbrec_logical_flow *sbflow, *next_sbflow; +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -14693,9 +14693,22 @@ ovn_start + # Create hypervisors hv[12]. + # Add vif1[12] to hv1, vif2[12] to hv2 + # Add all of the vifs to a single logical switch sw0. ++# Create logical router lr0 + + net_add n1 +-ovn-nbctl ls-add sw0 ++ ++ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1 ++for i in 0 1; do ++ idx=$((i+1)) ++ ovn-nbctl ls-add sw$i ++ ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$idx 192.168.$idx.254/24 ++ ovn-nbctl \ ++ -- lsp-add sw$i lrp$i-attachment \ ++ -- set Logical_Switch_Port lrp$i-attachment type=router \ ++ options:router-port=lrp$i \ ++ addresses='"00:00:00:00:ff:'0$idx'"' ++done ++ + for i in 1 2; do + sim_add hv$i + as hv$i +@@ -14715,10 +14728,24 @@ for i in 1 2; do + done + done + ++as hv1 ++ovn-nbctl lsp-add sw1 sw1-p0 \ ++ -- lsp-set-addresses sw1-p0 "00:00:00:00:00:33 192.168.2.11" ++ovs-vsctl -- add-port br-int vif33 -- \ ++ set interface vif33 \ ++ external-ids:iface-id=sw1-p0 \ ++ options:tx_pcap=hv$i/vif33-tx.pcap \ ++ options:rxq_pcap=hv$i/vif33-rx.pcap \ ++ ofport-request=33 ++ + ovn-nbctl --wait=hv set NB_Global . options:controller_event=true + ovn-nbctl lb-add lb0 192.168.1.100:80 "" + ovn-nbctl ls-lb-add sw0 lb0 +-uuid_lb=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0) ++uuid_lb0=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0) ++ ++ovn-nbctl lb-add lb1 192.168.2.100:80 "" ++ovn-nbctl lr-lb-add lr0 lb1 ++uuid_lb1=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb1) + ovn-nbctl --wait=hv meter-add event-elb drop 100 pktps 10 + + OVN_POPULATE_ARP +@@ -14726,10 +14753,10 @@ ovn-nbctl --timeout=3 --wait=hv sync + ovn-sbctl lflow-list + as hv1 ovs-ofctl dump-flows br-int + +-packet="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 && +- ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.1.100 && +- tcp && tcp.src==10000 && tcp.dst==80" +-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet" ++packet0="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 && ++ ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.1.100 && ++ tcp && tcp.src==10000 && tcp.dst==80" ++as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet0" + + ovn-sbctl list controller_event + uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}') +@@ -14743,12 +14770,27 @@ AT_CHECK([ovn-sbctl get controller_event + tcp + ]) + AT_CHECK_UNQUOTED([ovn-sbctl get controller_event $uuid event_info:load_balancer], [0], [dnl +-"$uuid_lb" ++"$uuid_lb0" + ]) + AT_CHECK([ovn-sbctl get controller_event $uuid seq_num], [0], [dnl + 1 + ]) + ++ovn-sbctl destroy controller_event $uuid ++packet1="inport==\"sw1-p0\" && eth.src==00:00:00:00:00:33 && eth.dst==00:00:00:00:ff:02 && ++ ip4 && ip.ttl==64 && ip4.src==192.168.2.11 && ip4.dst==192.168.2.100 && ++ tcp && tcp.src==10000 && tcp.dst==80" ++ ++as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet1" ++ovn-sbctl list controller_event ++uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}') ++AT_CHECK([ovn-sbctl get controller_event $uuid event_type], [0], [dnl ++empty_lb_backends ++]) ++AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl ++"192.168.2.100:80" ++]) ++ + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP + diff --git a/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch b/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch new file mode 100644 index 0000000..6722a76 --- /dev/null +++ b/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch @@ -0,0 +1,185 @@ +From 92f6a2f668708c677a8b10b0ac861bfd712f6a20 Mon Sep 17 00:00:00 2001 +Message-Id: <92f6a2f668708c677a8b10b0ac861bfd712f6a20.1590585469.git.lorenzo.bianconi@redhat.com> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Mon, 25 May 2020 18:31:27 +0200 +Subject: [PATCH ovn 3/3] northd: manage ARP request locally for FIP traffic + +Modify 100-priority logical flows in Gateway Redirect table of +logical router ingress pipeline (table 15) in order to manage ARP +request locally for FIP traffic. In particular set reg1 and eth.src +to NAT external ip and NAT external mac respectively and do not +distribute ARP traffic using FIP + +Signed-off-by: Lorenzo Bianconi +--- + ovn/northd/ovn-northd.8.xml | 10 +++++++--- + ovn/northd/ovn-northd.c | 23 ++++++++++++++++------- + tests/ovn.at | 28 +++++++++++++++++++++++++--- + tests/system-ovn.at | 30 ++++++++++++++++++++++++++++++ + 4 files changed, 78 insertions(+), 13 deletions(-) + +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -2632,9 +2632,13 @@ icmp4 { + For each NAT rule in the OVN Northbound database that can + be handled in a distributed manner, a priority-100 logical + flow with match ip4.src == B && +- outport == GW, where GW is +- the logical router distributed gateway port, with actions +- next;. ++ outport == GW && ++ is_chassis_resident(P), where GW is ++ the logical router distributed gateway port and P ++ is the NAT logical port. IP traffic matching the above rule ++ will be managed locally setting reg1 to C ++ and eth.src to D, where C is NAT ++ external ip and D is NAT external mac. +
    • + +
    • +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -8306,15 +8306,24 @@ build_lrouter_flows(struct hmap *datapat + /* Ingress Gateway Redirect Table: For NAT on a distributed + * router, add flows that are specific to a NAT rule. These + * flows indicate the presence of an applicable NAT rule that +- * can be applied in a distributed manner. */ ++ * can be applied in a distributed manner. ++ * In particulr reg1 and eth.src are set to NAT external IP and ++ * NAT external mac so the ARP request generated in the following ++ * stage is sent out with proper IP/MAC src addresses ++ */ + if (distributed) { + ds_clear(&match); +- ds_put_format(&match, "ip%s.src == %s && outport == %s", +- is_v6 ? "6" : "4", +- nat->logical_ip, +- od->l3dgw_port->json_key); ++ ds_clear(&actions); ++ ds_put_format(&match, ++ "ip%s.src == %s && outport == %s && " ++ "is_chassis_resident(\"%s\")", ++ is_v6 ? "6" : "4", nat->logical_ip, ++ od->l3dgw_port->json_key, nat->logical_port); ++ ds_put_format(&actions, "eth.src = %s; %sreg1 = %s; next;", ++ nat->external_mac, is_v6 ? "xx" : "", ++ nat->external_ip); + ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100, +- ds_cstr(&match), "next;"); ++ ds_cstr(&match), ds_cstr(&actions)); + } + + /* Egress Loopback table: For NAT on a distributed router. +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -14019,9 +14019,14 @@ ovs-vsctl -- add-port br-int hv2-vif1 -- + set interface hv2-vif1 external-ids:iface-id=sw1-p0 \ + options:tx_pcap=hv2/vif1-tx.pcap \ + options:rxq_pcap=hv2/vif1-rx.pcap \ +- ofport-request=1 ++ ofport-request=2 ++ovs-vsctl -- add-port br-int hv2-vif2 -- \ ++ set interface hv2-vif2 external-ids:iface-id=sw0-p1 \ ++ options:tx_pcap=hv2/vif2-tx.pcap \ ++ options:rxq_pcap=hv2/vif2-rx.pcap \ ++ ofport-request=3 + +-ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1 ++ovn-nbctl create Logical_Router name=lr0 + ovn-nbctl ls-add sw0 + ovn-nbctl ls-add sw1 + +@@ -14030,13 +14035,16 @@ ovn-nbctl lsp-add sw0 rp-sw0 -- set Logi + type=router options:router-port=sw0 \ + -- lsp-set-addresses rp-sw0 router + +-ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64 ++ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64 \ ++ -- set Logical_Router_Port sw1 options:redirect-chassis="hv2" + ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \ + type=router options:router-port=sw1 \ + -- lsp-set-addresses rp-sw1 router + + ovn-nbctl lsp-add sw0 sw0-p0 \ + -- lsp-set-addresses sw0-p0 "f0:00:00:01:02:03 192.168.1.2 2001::2" ++ovn-nbctl lsp-add sw0 sw0-p1 \ ++ -- lsp-set-addresses sw0-p1 "f0:00:00:11:02:03 192.168.1.3 2001::3" + + ovn-nbctl lsp-add sw1 sw1-p0 \ + -- lsp-set-addresses sw1-p0 unknown +@@ -14082,6 +14090,20 @@ send_na 2 1 $dst_mac $router_mac1 $dst_i + + OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) + ++# Create FIP on sw0-p0, add a route on logical router pipeline and ++# ARP request for a unkwon destination is sent using FIP MAC/IP ++ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.1.2 192.168.1.3 sw0-p1 f0:00:00:01:02:04 ++ovn-nbctl lr-route-add lr0 172.16.2.0/24 172.16.1.11 ++ ++dst_ip=$(ip_to_hex 172 16 2 10) ++fip_ip=$(ip_to_hex 172 16 1 2) ++src_ip=$(ip_to_hex 192 168 1 3) ++gw_router=$(ip_to_hex 172 16 1 11) ++send_icmp_packet 2 2 f00000110203 $router_mac0 $src_ip $dst_ip 0000 $data ++echo $(get_arp_req f00000010204 $fip_ip $gw_router) >> expected ++ ++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) ++ + OVN_CLEANUP([hv1],[hv2]) + AT_CLEANUP + +--- a/tests/system-ovn.at ++++ b/tests/system-ovn.at +@@ -1824,6 +1824,19 @@ ADD_VETH(alice1, alice1, br-int, "172.16 + ovn-nbctl lsp-add alice alice1 \ + -- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2" + ++# Add external network ++ADD_NAMESPACES(ext-net) ++AT_CHECK([ip link add alice-ext netns alice1 type veth peer name ext-veth netns ext-net]) ++NS_CHECK_EXEC([ext-net], [ip link set dev ext-veth up], [0], []) ++NS_CHECK_EXEC([ext-net], [ip addr add 10.0.0.1/24 dev ext-veth], [0], []) ++NS_CHECK_EXEC([ext-net], [ip route add default via 10.0.0.2], [0], []) ++ ++NS_CHECK_EXEC([alice1], [ip link set dev alice-ext up], [0], []) ++NS_CHECK_EXEC([alice1], [ip addr add 10.0.0.2/24 dev alice-ext], [0], []) ++NS_CHECK_EXEC([alice1], [sysctl -w net.ipv4.conf.all.forwarding=1],[0], [dnl ++net.ipv4.conf.all.forwarding = 1 ++]) ++ + # Add DNAT rules + AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04]) + AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.1.3 foo2 00:00:02:02:03:05]) +@@ -1831,6 +1844,9 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_a + # Add a SNAT rule + AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16]) + ++# Add default route to ext-net ++AT_CHECK([ovn-nbctl lr-route-add R1 10.0.0.0/24 172.16.1.2]) ++ + ovn-nbctl --wait=hv sync + OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)']) + +@@ -1874,6 +1890,20 @@ sed -e 's/zone=[[0-9]]*/zone=/' + icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=,type=0,code=0),zone= + ]) + ++# Try to ping external network ++NS_CHECK_EXEC([ext-net], [tcpdump -n -c 3 -i ext-veth dst 172.16.1.3 and icmp > ext-net.pcap &]) ++sleep 1 ++AT_CHECK([ovn-nbctl lr-nat-del R1 snat]) ++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.1 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++OVS_WAIT_UNTIL([ ++ total_pkts=$(cat ext-net.pcap | wc -l) ++ test "${total_pkts}" = "3" ++]) ++ + OVS_APP_EXIT_AND_WAIT([ovn-controller]) + + as ovn-sb diff --git a/SOURCES/0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch b/SOURCES/0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch new file mode 100644 index 0000000..f445979 --- /dev/null +++ b/SOURCES/0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch @@ -0,0 +1,1264 @@ +From 85136b292d698a76c5317ec10622bcc015165770 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Wed, 4 Dec 2019 17:28:28 +0100 +Subject: [PATCH ovn 4/4] ovn-controller: Fix use of dangling pointers in I-P + runtime_data. + +The incremental processing engine might stop a run before the +en_runtime_data node is processed. In such cases the ed_runtime_data +fields might contain pointers to already deleted SB records. For +example, if a port binding corresponding to a patch port is removed from +the SB database and the incremental processing engine aborts before the +en_runtime_data node is processed then the corresponding local_datapath +hashtable entry in ed_runtime_data is stale and will store a pointer to +the already freed sbrec_port_binding record. + +This will cause invalid memory accesses in various places (e.g., +pinctrl_run() -> prepare_ipv6_ras()). + +To fix the issue we introduce the engine_get_data() API which must be +called in order to safely access internal node data. If the node is in +state EN_STALE or EN_ABORTED, engine_get_data() returns NULL as the +references might be stale. + +This commit also adds an "is_valid()" method to engine nodes to allow +users to override the default behavior of determining if data is valid in a +node (e.g., for the ct-zones node the data is always safe to access). + +Also, all interactions with node data outside inc-proc-eng.c are now +performed through APIs and never by directly accessing the node->data +field. This makes it easier to ensure that we don't access invalid +(stale) data. + +CC: Han Zhou +Fixes: ca278d98a4f5 ("ovn-controller: Initial use of incremental engine - quiet mode.") +Signed-off-by: Dumitru Ceara +Signed-off-by: Han Zhou + +(cherry picked from upstream commit 94cbc59dc0f1cb56e56d1551956efe5824561864) + +Change-Id: Icdb3d38e4b43e36c4c1924509e2461707cdb57e6 +--- + ovn/controller/ovn-controller.c | 457 +++++++++++++++++++++------------------- + ovn/lib/inc-proc-eng.c | 59 +++++- + ovn/lib/inc-proc-eng.h | 115 +++++++--- + 3 files changed, 370 insertions(+), 261 deletions(-) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index 20e5ce4..cb23bc8 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -702,26 +702,25 @@ struct ed_type_ofctrl_is_connected { + bool connected; + }; + +-static void +-en_ofctrl_is_connected_init(struct engine_node *node) ++static void * ++en_ofctrl_is_connected_init(struct engine_node *node OVS_UNUSED, ++ struct engine_arg *arg OVS_UNUSED) + { +- struct ed_type_ofctrl_is_connected *data = +- (struct ed_type_ofctrl_is_connected *)node->data; +- data->connected = false; ++ struct ed_type_ofctrl_is_connected *data = xzalloc(sizeof *data); ++ return data; + } + + static void +-en_ofctrl_is_connected_cleanup(struct engine_node *node OVS_UNUSED) ++en_ofctrl_is_connected_cleanup(void *data OVS_UNUSED) + { + } + + static void +-en_ofctrl_is_connected_run(struct engine_node *node) ++en_ofctrl_is_connected_run(struct engine_node *node, void *data) + { +- struct ed_type_ofctrl_is_connected *data = +- (struct ed_type_ofctrl_is_connected *)node->data; +- if (data->connected != ofctrl_is_connected()) { +- data->connected = !data->connected; ++ struct ed_type_ofctrl_is_connected *of_data = data; ++ if (of_data->connected != ofctrl_is_connected()) { ++ of_data->connected = !of_data->connected; + engine_set_node_state(node, EN_UPDATED); + return; + } +@@ -736,21 +735,24 @@ struct ed_type_addr_sets { + struct sset updated; + }; + +-static void +-en_addr_sets_init(struct engine_node *node) ++static void * ++en_addr_sets_init(struct engine_node *node OVS_UNUSED, ++ struct engine_arg *arg OVS_UNUSED) + { +- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; ++ struct ed_type_addr_sets *as = xzalloc(sizeof *as); ++ + shash_init(&as->addr_sets); + as->change_tracked = false; + sset_init(&as->new); + sset_init(&as->deleted); + sset_init(&as->updated); ++ return as; + } + + static void +-en_addr_sets_cleanup(struct engine_node *node) ++en_addr_sets_cleanup(void *data) + { +- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; ++ struct ed_type_addr_sets *as = data; + expr_const_sets_destroy(&as->addr_sets); + shash_destroy(&as->addr_sets); + sset_destroy(&as->new); +@@ -759,9 +761,9 @@ en_addr_sets_cleanup(struct engine_node *node) + } + + static void +-en_addr_sets_run(struct engine_node *node) ++en_addr_sets_run(struct engine_node *node, void *data) + { +- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; ++ struct ed_type_addr_sets *as = data; + + sset_clear(&as->new); + sset_clear(&as->deleted); +@@ -779,9 +781,9 @@ en_addr_sets_run(struct engine_node *node) + } + + static bool +-addr_sets_sb_address_set_handler(struct engine_node *node) ++addr_sets_sb_address_set_handler(struct engine_node *node, void *data) + { +- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; ++ struct ed_type_addr_sets *as = data; + + sset_clear(&as->new); + sset_clear(&as->deleted); +@@ -813,21 +815,24 @@ struct ed_type_port_groups{ + struct sset updated; + }; + +-static void +-en_port_groups_init(struct engine_node *node) ++static void * ++en_port_groups_init(struct engine_node *node OVS_UNUSED, ++ struct engine_arg *arg OVS_UNUSED) + { +- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; ++ struct ed_type_port_groups *pg = xzalloc(sizeof *pg); ++ + shash_init(&pg->port_groups); + pg->change_tracked = false; + sset_init(&pg->new); + sset_init(&pg->deleted); + sset_init(&pg->updated); ++ return pg; + } + + static void +-en_port_groups_cleanup(struct engine_node *node) ++en_port_groups_cleanup(void *data) + { +- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; ++ struct ed_type_port_groups *pg = data; + expr_const_sets_destroy(&pg->port_groups); + shash_destroy(&pg->port_groups); + sset_destroy(&pg->new); +@@ -836,9 +841,9 @@ en_port_groups_cleanup(struct engine_node *node) + } + + static void +-en_port_groups_run(struct engine_node *node) ++en_port_groups_run(struct engine_node *node, void *data) + { +- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; ++ struct ed_type_port_groups *pg = data; + + sset_clear(&pg->new); + sset_clear(&pg->deleted); +@@ -856,9 +861,9 @@ en_port_groups_run(struct engine_node *node) + } + + static bool +-port_groups_sb_port_group_handler(struct engine_node *node) ++port_groups_sb_port_group_handler(struct engine_node *node, void *data) + { +- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; ++ struct ed_type_port_groups *pg = data; + + sset_clear(&pg->new); + sset_clear(&pg->deleted); +@@ -899,46 +904,45 @@ struct ed_type_runtime_data { + struct sset active_tunnels; + }; + +-static void +-en_runtime_data_init(struct engine_node *node) ++static void * ++en_runtime_data_init(struct engine_node *node OVS_UNUSED, ++ struct engine_arg *arg OVS_UNUSED) + { +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)node->data; ++ struct ed_type_runtime_data *data = xzalloc(sizeof *data); + + hmap_init(&data->local_datapaths); + sset_init(&data->local_lports); + sset_init(&data->local_lport_ids); + sset_init(&data->active_tunnels); ++ return data; + } + + static void +-en_runtime_data_cleanup(struct engine_node *node) ++en_runtime_data_cleanup(void *data) + { +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)node->data; ++ struct ed_type_runtime_data *rt_data = data; + +- sset_destroy(&data->local_lports); +- sset_destroy(&data->local_lport_ids); +- sset_destroy(&data->active_tunnels); ++ sset_destroy(&rt_data->local_lports); ++ sset_destroy(&rt_data->local_lport_ids); ++ sset_destroy(&rt_data->active_tunnels); + struct local_datapath *cur_node, *next_node; + HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, +- &data->local_datapaths) { ++ &rt_data->local_datapaths) { + free(cur_node->peer_ports); +- hmap_remove(&data->local_datapaths, &cur_node->hmap_node); ++ hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node); + free(cur_node); + } +- hmap_destroy(&data->local_datapaths); ++ hmap_destroy(&rt_data->local_datapaths); + } + + static void +-en_runtime_data_run(struct engine_node *node) ++en_runtime_data_run(struct engine_node *node, void *data) + { +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)node->data; +- struct hmap *local_datapaths = &data->local_datapaths; +- struct sset *local_lports = &data->local_lports; +- struct sset *local_lport_ids = &data->local_lport_ids; +- struct sset *active_tunnels = &data->active_tunnels; ++ struct ed_type_runtime_data *rt_data = data; ++ struct hmap *local_datapaths = &rt_data->local_datapaths; ++ struct sset *local_lports = &rt_data->local_lports; ++ struct sset *local_lport_ids = &rt_data->local_lport_ids; ++ struct sset *active_tunnels = &rt_data->active_tunnels; + + static bool first_run = true; + if (first_run) { +@@ -981,8 +985,7 @@ en_runtime_data_run(struct engine_node *node) + ovs_assert(chassis); + + struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected = +- (struct ed_type_ofctrl_is_connected *)engine_get_input( +- "ofctrl_is_connected", node)->data; ++ engine_get_input_data("ofctrl_is_connected", node); + if (ed_ofctrl_is_connected->connected) { + /* Calculate the active tunnels only if have an an active + * OpenFlow connection to br-int. +@@ -1036,12 +1039,11 @@ en_runtime_data_run(struct engine_node *node) + } + + static bool +-runtime_data_sb_port_binding_handler(struct engine_node *node) ++runtime_data_sb_port_binding_handler(struct engine_node *node, void *data) + { +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)node->data; +- struct sset *local_lports = &data->local_lports; +- struct sset *active_tunnels = &data->active_tunnels; ++ struct ed_type_runtime_data *rt_data = data; ++ struct sset *local_lports = &rt_data->local_lports; ++ struct sset *active_tunnels = &rt_data->active_tunnels; + + struct ovsrec_open_vswitch_table *ovs_table = + (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( +@@ -1080,10 +1082,10 @@ struct ed_type_ct_zones { + struct simap current; + }; + +-static void +-en_ct_zones_init(struct engine_node *node) ++static void * ++en_ct_zones_init(struct engine_node *node, struct engine_arg *arg OVS_UNUSED) + { +- struct ed_type_ct_zones *data = node->data; ++ struct ed_type_ct_zones *data = xzalloc(sizeof *data); + struct ovsrec_open_vswitch_table *ovs_table = + (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( + engine_get_input("OVS_open_vswitch", node)); +@@ -1097,56 +1099,63 @@ en_ct_zones_init(struct engine_node *node) + memset(data->bitmap, 0, sizeof data->bitmap); + bitmap_set1(data->bitmap, 0); /* Zone 0 is reserved. */ + restore_ct_zones(bridge_table, ovs_table, &data->current, data->bitmap); ++ return data; + } + + static void +-en_ct_zones_cleanup(struct engine_node *node) ++en_ct_zones_cleanup(void *data) + { +- struct ed_type_ct_zones *data = node->data; ++ struct ed_type_ct_zones *ct_zones_data = data; + +- simap_destroy(&data->current); +- shash_destroy(&data->pending); ++ simap_destroy(&ct_zones_data->current); ++ shash_destroy(&ct_zones_data->pending); + } + + static void +-en_ct_zones_run(struct engine_node *node) ++en_ct_zones_run(struct engine_node *node, void *data) + { +- struct ed_type_ct_zones *data = node->data; ++ struct ed_type_ct_zones *ct_zones_data = data; + struct ed_type_runtime_data *rt_data = +- (struct ed_type_runtime_data *)engine_get_input( +- "runtime_data", node)->data; ++ engine_get_input_data("runtime_data", node); + + update_ct_zones(&rt_data->local_lports, &rt_data->local_datapaths, +- &data->current, data->bitmap, &data->pending); ++ &ct_zones_data->current, ct_zones_data->bitmap, ++ &ct_zones_data->pending); + + engine_set_node_state(node, EN_UPDATED); + } + ++/* The data in the ct_zones node is always valid (i.e., no stale pointers). */ ++static bool ++en_ct_zones_is_valid(struct engine_node *node OVS_UNUSED) ++{ ++ return true; ++} ++ + struct ed_type_mff_ovn_geneve { + enum mf_field_id mff_ovn_geneve; + }; + +-static void +-en_mff_ovn_geneve_init(struct engine_node *node) ++static void * ++en_mff_ovn_geneve_init(struct engine_node *node OVS_UNUSED, ++ struct engine_arg *arg OVS_UNUSED) + { +- struct ed_type_mff_ovn_geneve *data = +- (struct ed_type_mff_ovn_geneve *)node->data; +- data->mff_ovn_geneve = 0; ++ struct ed_type_mff_ovn_geneve *data = xzalloc(sizeof *data); ++ return data; + } + + static void +-en_mff_ovn_geneve_cleanup(struct engine_node *node OVS_UNUSED) ++en_mff_ovn_geneve_cleanup(void *data OVS_UNUSED) + { + } + + static void +-en_mff_ovn_geneve_run(struct engine_node *node) ++en_mff_ovn_geneve_run(struct engine_node *node, void *data) + { +- struct ed_type_mff_ovn_geneve *data = +- (struct ed_type_mff_ovn_geneve *)node->data; ++ struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = data; + enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id(); +- if (data->mff_ovn_geneve != mff_ovn_geneve) { +- data->mff_ovn_geneve = mff_ovn_geneve; ++ if (ed_mff_ovn_geneve->mff_ovn_geneve != mff_ovn_geneve) { ++ ed_mff_ovn_geneve->mff_ovn_geneve = mff_ovn_geneve; + engine_set_node_state(node, EN_UPDATED); + return; + } +@@ -1166,48 +1175,46 @@ struct ed_type_flow_output { + struct lflow_resource_ref lflow_resource_ref; + }; + +-static void +-en_flow_output_init(struct engine_node *node) ++static void * ++en_flow_output_init(struct engine_node *node OVS_UNUSED, ++ struct engine_arg *arg OVS_UNUSED) + { +- struct ed_type_flow_output *data = +- (struct ed_type_flow_output *)node->data; ++ struct ed_type_flow_output *data = xzalloc(sizeof *data); ++ + ovn_desired_flow_table_init(&data->flow_table); + ovn_extend_table_init(&data->group_table); + ovn_extend_table_init(&data->meter_table); + data->conj_id_ofs = 1; + lflow_resource_init(&data->lflow_resource_ref); ++ return data; + } + + static void +-en_flow_output_cleanup(struct engine_node *node) ++en_flow_output_cleanup(void *data) + { +- struct ed_type_flow_output *data = +- (struct ed_type_flow_output *)node->data; +- ovn_desired_flow_table_destroy(&data->flow_table); +- ovn_extend_table_destroy(&data->group_table); +- ovn_extend_table_destroy(&data->meter_table); +- lflow_resource_destroy(&data->lflow_resource_ref); ++ struct ed_type_flow_output *flow_output_data = data; ++ ovn_desired_flow_table_destroy(&flow_output_data->flow_table); ++ ovn_extend_table_destroy(&flow_output_data->group_table); ++ ovn_extend_table_destroy(&flow_output_data->meter_table); ++ lflow_resource_destroy(&flow_output_data->lflow_resource_ref); + } + + static void +-en_flow_output_run(struct engine_node *node) ++en_flow_output_run(struct engine_node *node, void *data) + { + struct ed_type_runtime_data *rt_data = +- (struct ed_type_runtime_data *)engine_get_input( +- "runtime_data", node)->data; ++ engine_get_input_data("runtime_data", node); + struct hmap *local_datapaths = &rt_data->local_datapaths; + struct sset *local_lports = &rt_data->local_lports; + struct sset *local_lport_ids = &rt_data->local_lport_ids; + struct sset *active_tunnels = &rt_data->active_tunnels; + + struct ed_type_ct_zones *ct_zones_data = +- (struct ed_type_ct_zones *)engine_get_input( +- "ct_zones", node)->data; ++ engine_get_input_data("ct_zones", node); + struct simap *ct_zones = &ct_zones_data->current; + + struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = +- (struct ed_type_mff_ovn_geneve *)engine_get_input( +- "mff_ovn_geneve", node)->data; ++ engine_get_input_data("mff_ovn_geneve", node); + enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve; + + struct ovsrec_open_vswitch_table *ovs_table = +@@ -1224,12 +1231,11 @@ en_flow_output_run(struct engine_node *node) + engine_get_input("SB_chassis", node), + "name"); + struct ed_type_addr_sets *as_data = +- (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data; ++ engine_get_input_data("addr_sets", node); + struct shash *addr_sets = &as_data->addr_sets; + + struct ed_type_port_groups *pg_data = +- (struct ed_type_port_groups *)engine_get_input( +- "port_groups", node)->data; ++ engine_get_input_data("port_groups", node); + struct shash *port_groups = &pg_data->port_groups; + + const struct sbrec_chassis *chassis = NULL; +@@ -1239,8 +1245,7 @@ en_flow_output_run(struct engine_node *node) + + ovs_assert(br_int && chassis); + +- struct ed_type_flow_output *fo = +- (struct ed_type_flow_output *)node->data; ++ struct ed_type_flow_output *fo = data; + struct ovn_desired_flow_table *flow_table = &fo->flow_table; + struct ovn_extend_table *group_table = &fo->group_table; + struct ovn_extend_table *meter_table = &fo->meter_table; +@@ -1320,21 +1325,19 @@ en_flow_output_run(struct engine_node *node) + } + + static bool +-flow_output_sb_logical_flow_handler(struct engine_node *node) +-{ +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)engine_get_input( +- "runtime_data", node)->data; +- struct hmap *local_datapaths = &data->local_datapaths; +- struct sset *local_lport_ids = &data->local_lport_ids; +- struct sset *active_tunnels = &data->active_tunnels; ++flow_output_sb_logical_flow_handler(struct engine_node *node, void *data) ++{ ++ struct ed_type_runtime_data *rt_data = ++ engine_get_input_data("runtime_data", node); ++ struct hmap *local_datapaths = &rt_data->local_datapaths; ++ struct sset *local_lport_ids = &rt_data->local_lport_ids; ++ struct sset *active_tunnels = &rt_data->active_tunnels; + struct ed_type_addr_sets *as_data = +- (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data; ++ engine_get_input_data("addr_sets", node); + struct shash *addr_sets = &as_data->addr_sets; + + struct ed_type_port_groups *pg_data = +- (struct ed_type_port_groups *)engine_get_input( +- "port_groups", node)->data; ++ engine_get_input_data("port_groups", node); + struct shash *port_groups = &pg_data->port_groups; + + struct ovsrec_open_vswitch_table *ovs_table = +@@ -1358,8 +1361,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node) + + ovs_assert(br_int && chassis); + +- struct ed_type_flow_output *fo = +- (struct ed_type_flow_output *)node->data; ++ struct ed_type_flow_output *fo = data; + struct ovn_desired_flow_table *flow_table = &fo->flow_table; + struct ovn_extend_table *group_table = &fo->group_table; + struct ovn_extend_table *meter_table = &fo->meter_table; +@@ -1403,7 +1405,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node) + } + + static bool +-flow_output_sb_mac_binding_handler(struct engine_node *node) ++flow_output_sb_mac_binding_handler(struct engine_node *node, void *data) + { + struct ovsdb_idl_index *sbrec_port_binding_by_name = + engine_ovsdb_node_get_index( +@@ -1414,8 +1416,7 @@ flow_output_sb_mac_binding_handler(struct engine_node *node) + (struct sbrec_mac_binding_table *)EN_OVSDB_GET( + engine_get_input("SB_mac_binding", node)); + +- struct ed_type_flow_output *fo = +- (struct ed_type_flow_output *)node->data; ++ struct ed_type_flow_output *fo = data; + struct ovn_desired_flow_table *flow_table = &fo->flow_table; + + lflow_handle_changed_neighbors(sbrec_port_binding_by_name, +@@ -1426,22 +1427,19 @@ flow_output_sb_mac_binding_handler(struct engine_node *node) + } + + static bool +-flow_output_sb_port_binding_handler(struct engine_node *node) ++flow_output_sb_port_binding_handler(struct engine_node *node, void *data) + { +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)engine_get_input( +- "runtime_data", node)->data; +- struct hmap *local_datapaths = &data->local_datapaths; +- struct sset *active_tunnels = &data->active_tunnels; ++ struct ed_type_runtime_data *rt_data = ++ engine_get_input_data("runtime_data", node); ++ struct hmap *local_datapaths = &rt_data->local_datapaths; ++ struct sset *active_tunnels = &rt_data->active_tunnels; + + struct ed_type_ct_zones *ct_zones_data = +- (struct ed_type_ct_zones *)engine_get_input( +- "ct_zones", node)->data; ++ engine_get_input_data("ct_zones", node); + struct simap *ct_zones = &ct_zones_data->current; + + struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = +- (struct ed_type_mff_ovn_geneve *)engine_get_input( +- "mff_ovn_geneve", node)->data; ++ engine_get_input_data("mff_ovn_geneve", node); + enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve; + + struct ovsrec_open_vswitch_table *ovs_table = +@@ -1463,8 +1461,7 @@ flow_output_sb_port_binding_handler(struct engine_node *node) + } + ovs_assert(br_int && chassis); + +- struct ed_type_flow_output *fo = +- (struct ed_type_flow_output *)node->data; ++ struct ed_type_flow_output *fo = data; + struct ovn_desired_flow_table *flow_table = &fo->flow_table; + + struct ovsdb_idl_index *sbrec_port_binding_by_name = +@@ -1534,21 +1531,18 @@ flow_output_sb_port_binding_handler(struct engine_node *node) + } + + static bool +-flow_output_sb_multicast_group_handler(struct engine_node *node) ++flow_output_sb_multicast_group_handler(struct engine_node *node, void *data) + { +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)engine_get_input( +- "runtime_data", node)->data; +- struct hmap *local_datapaths = &data->local_datapaths; ++ struct ed_type_runtime_data *rt_data = ++ engine_get_input_data("runtime_data", node); ++ struct hmap *local_datapaths = &rt_data->local_datapaths; + + struct ed_type_ct_zones *ct_zones_data = +- (struct ed_type_ct_zones *)engine_get_input( +- "ct_zones", node)->data; ++ engine_get_input_data("ct_zones", node); + struct simap *ct_zones = &ct_zones_data->current; + + struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = +- (struct ed_type_mff_ovn_geneve *)engine_get_input( +- "mff_ovn_geneve", node)->data; ++ engine_get_input_data("mff_ovn_geneve", node); + enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve; + + struct ovsrec_open_vswitch_table *ovs_table = +@@ -1570,8 +1564,7 @@ flow_output_sb_multicast_group_handler(struct engine_node *node) + } + ovs_assert(br_int && chassis); + +- struct ed_type_flow_output *fo = +- (struct ed_type_flow_output *)node->data; ++ struct ed_type_flow_output *fo = data; + struct ovn_desired_flow_table *flow_table = &fo->flow_table; + + struct sbrec_multicast_group_table *multicast_group_table = +@@ -1588,23 +1581,21 @@ flow_output_sb_multicast_group_handler(struct engine_node *node) + } + + static bool +-_flow_output_resource_ref_handler(struct engine_node *node, +- enum ref_type ref_type) ++_flow_output_resource_ref_handler(struct engine_node *node, void *data, ++ enum ref_type ref_type) + { +- struct ed_type_runtime_data *data = +- (struct ed_type_runtime_data *)engine_get_input( +- "runtime_data", node)->data; +- struct hmap *local_datapaths = &data->local_datapaths; +- struct sset *local_lport_ids = &data->local_lport_ids; +- struct sset *active_tunnels = &data->active_tunnels; ++ struct ed_type_runtime_data *rt_data = ++ engine_get_input_data("runtime_data", node); ++ struct hmap *local_datapaths = &rt_data->local_datapaths; ++ struct sset *local_lport_ids = &rt_data->local_lport_ids; ++ struct sset *active_tunnels = &rt_data->active_tunnels; + + struct ed_type_addr_sets *as_data = +- (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data; ++ engine_get_input_data("addr_sets", node); + struct shash *addr_sets = &as_data->addr_sets; + + struct ed_type_port_groups *pg_data = +- (struct ed_type_port_groups *)engine_get_input( +- "port_groups", node)->data; ++ engine_get_input_data("port_groups", node); + struct shash *port_groups = &pg_data->port_groups; + + struct ovsrec_open_vswitch_table *ovs_table = +@@ -1627,8 +1618,7 @@ _flow_output_resource_ref_handler(struct engine_node *node, + + ovs_assert(br_int && chassis); + +- struct ed_type_flow_output *fo = +- (struct ed_type_flow_output *)node->data; ++ struct ed_type_flow_output *fo = data; + struct ovn_desired_flow_table *flow_table = &fo->flow_table; + struct ovn_extend_table *group_table = &fo->group_table; + struct ovn_extend_table *meter_table = &fo->meter_table; +@@ -1735,15 +1725,15 @@ _flow_output_resource_ref_handler(struct engine_node *node, + } + + static bool +-flow_output_addr_sets_handler(struct engine_node *node) ++flow_output_addr_sets_handler(struct engine_node *node, void *data) + { +- return _flow_output_resource_ref_handler(node, REF_TYPE_ADDRSET); ++ return _flow_output_resource_ref_handler(node, data, REF_TYPE_ADDRSET); + } + + static bool +-flow_output_port_groups_handler(struct engine_node *node) ++flow_output_port_groups_handler(struct engine_node *node, void *data) + { +- return _flow_output_resource_ref_handler(node, REF_TYPE_PORTGROUP); ++ return _flow_output_resource_ref_handler(node, data, REF_TYPE_PORTGROUP); + } + + struct ovn_controller_exit_args { +@@ -1851,15 +1841,7 @@ main(int argc, char *argv[]) + stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS); + + /* Define inc-proc-engine nodes. */ +- struct ed_type_ct_zones ed_ct_zones; +- struct ed_type_runtime_data ed_runtime_data; +- struct ed_type_mff_ovn_geneve ed_mff_ovn_geneve; +- struct ed_type_ofctrl_is_connected ed_ofctrl_is_connected; +- struct ed_type_flow_output ed_flow_output; +- struct ed_type_addr_sets ed_addr_sets; +- struct ed_type_port_groups ed_port_groups; +- +- ENGINE_NODE(ct_zones, "ct_zones"); ++ ENGINE_NODE_CUSTOM_DATA(ct_zones, "ct_zones"); + ENGINE_NODE(runtime_data, "runtime_data"); + ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve"); + ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected"); +@@ -1875,18 +1857,6 @@ main(int argc, char *argv[]) + OVS_NODES + #undef OVS_NODE + +- engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name); +- engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath", +- sbrec_multicast_group_by_name_datapath); +- engine_ovsdb_node_add_index(&en_sb_port_binding, "name", +- sbrec_port_binding_by_name); +- engine_ovsdb_node_add_index(&en_sb_port_binding, "key", +- sbrec_port_binding_by_key); +- engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath", +- sbrec_port_binding_by_datapath); +- engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key", +- sbrec_datapath_binding_by_key); +- + /* Add dependencies between inc-proc-engine nodes. */ + + engine_add_input(&en_addr_sets, &en_sb_address_set, +@@ -1935,20 +1905,45 @@ main(int argc, char *argv[]) + engine_add_input(&en_runtime_data, &en_sb_port_binding, + runtime_data_sb_port_binding_handler); + +- engine_init(&en_flow_output); ++ struct engine_arg engine_arg = { ++ .sb_idl = ovnsb_idl_loop.idl, ++ .ovs_idl = ovs_idl_loop.idl, ++ }; ++ engine_init(&en_flow_output, &engine_arg); + +- ofctrl_init(&ed_flow_output.group_table, +- &ed_flow_output.meter_table, ++ engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name); ++ engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath", ++ sbrec_multicast_group_by_name_datapath); ++ engine_ovsdb_node_add_index(&en_sb_port_binding, "name", ++ sbrec_port_binding_by_name); ++ engine_ovsdb_node_add_index(&en_sb_port_binding, "key", ++ sbrec_port_binding_by_key); ++ engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath", ++ sbrec_port_binding_by_datapath); ++ engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key", ++ sbrec_datapath_binding_by_key); ++ ++ struct ed_type_flow_output *flow_output_data = ++ engine_get_internal_data(&en_flow_output); ++ struct ed_type_ct_zones *ct_zones_data = ++ engine_get_internal_data(&en_ct_zones); ++ struct ed_type_runtime_data *runtime_data = NULL; ++ ++ ofctrl_init(&flow_output_data->group_table, ++ &flow_output_data->meter_table, + get_ofctrl_probe_interval(ovs_idl_loop.idl)); + + unixctl_command_register("group-table-list", "", 0, 0, +- group_table_list, &ed_flow_output.group_table); ++ group_table_list, ++ &flow_output_data->group_table); + + unixctl_command_register("meter-table-list", "", 0, 0, +- meter_table_list, &ed_flow_output.meter_table); ++ meter_table_list, ++ &flow_output_data->meter_table); + + unixctl_command_register("ct-zone-list", "", 0, 0, +- ct_zone_list, &ed_ct_zones.current); ++ ct_zone_list, ++ &ct_zones_data->current); + + struct pending_pkt pending_pkt = { .conn = NULL }; + unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt, +@@ -2024,7 +2019,10 @@ main(int argc, char *argv[]) + } + + if (br_int) { +- ofctrl_run(br_int, &ed_ct_zones.pending); ++ ct_zones_data = engine_get_data(&en_ct_zones); ++ if (ct_zones_data) { ++ ofctrl_run(br_int, &ct_zones_data->pending); ++ } + + if (chassis) { + patch_run(ovs_idl_txn, +@@ -2064,41 +2062,50 @@ main(int argc, char *argv[]) + } + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, + time_msec()); ++ ct_zones_data = engine_get_data(&en_ct_zones); + if (ovs_idl_txn) { +- commit_ct_zones(br_int, &ed_ct_zones.pending); ++ if (ct_zones_data) { ++ commit_ct_zones(br_int, &ct_zones_data->pending); ++ } + bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl), + br_int, chassis, + sbrec_ha_chassis_group_table_get( + ovnsb_idl_loop.idl), + sbrec_sb_global_table_get(ovnsb_idl_loop.idl)); + } +- ofctrl_put(&ed_flow_output.flow_table, +- &ed_ct_zones.pending, +- sbrec_meter_table_get(ovnsb_idl_loop.idl), +- get_nb_cfg(sbrec_sb_global_table_get( +- ovnsb_idl_loop.idl)), +- engine_node_changed(&en_flow_output)); +- pinctrl_run(ovnsb_idl_txn, +- sbrec_datapath_binding_by_key, +- sbrec_port_binding_by_datapath, +- sbrec_port_binding_by_key, +- sbrec_port_binding_by_name, +- sbrec_mac_binding_by_lport_ip, +- sbrec_igmp_group, +- sbrec_ip_multicast, +- sbrec_dns_table_get(ovnsb_idl_loop.idl), +- sbrec_controller_event_table_get( +- ovnsb_idl_loop.idl), +- sbrec_service_monitor_table_get( +- ovnsb_idl_loop.idl), +- br_int, chassis, +- &ed_runtime_data.local_datapaths, +- &ed_runtime_data.active_tunnels); + +- if (engine_node_changed(&en_runtime_data)) { +- update_sb_monitors(ovnsb_idl_loop.idl, chassis, +- &ed_runtime_data.local_lports, +- &ed_runtime_data.local_datapaths); ++ flow_output_data = engine_get_data(&en_flow_output); ++ if (flow_output_data && ct_zones_data) { ++ ofctrl_put(&flow_output_data->flow_table, ++ &ct_zones_data->pending, ++ sbrec_meter_table_get(ovnsb_idl_loop.idl), ++ get_nb_cfg(sbrec_sb_global_table_get( ++ ovnsb_idl_loop.idl)), ++ engine_node_changed(&en_flow_output)); ++ } ++ runtime_data = engine_get_data(&en_runtime_data); ++ if (runtime_data) { ++ pinctrl_run(ovnsb_idl_txn, ++ sbrec_datapath_binding_by_key, ++ sbrec_port_binding_by_datapath, ++ sbrec_port_binding_by_key, ++ sbrec_port_binding_by_name, ++ sbrec_mac_binding_by_lport_ip, ++ sbrec_igmp_group, ++ sbrec_ip_multicast, ++ sbrec_dns_table_get(ovnsb_idl_loop.idl), ++ sbrec_controller_event_table_get( ++ ovnsb_idl_loop.idl), ++ sbrec_service_monitor_table_get( ++ ovnsb_idl_loop.idl), ++ br_int, chassis, ++ &runtime_data->local_datapaths, ++ &runtime_data->active_tunnels); ++ if (engine_node_changed(&en_runtime_data)) { ++ update_sb_monitors(ovnsb_idl_loop.idl, chassis, ++ &runtime_data->local_lports, ++ &runtime_data->local_datapaths); ++ } + } + } + +@@ -2133,9 +2140,13 @@ main(int argc, char *argv[]) + + + if (pending_pkt.conn) { +- if (br_int && chassis) { ++ struct ed_type_addr_sets *as_data = ++ engine_get_data(&en_addr_sets); ++ struct ed_type_port_groups *pg_data = ++ engine_get_data(&en_port_groups); ++ if (br_int && chassis && as_data && pg_data) { + char *error = ofctrl_inject_pkt(br_int, pending_pkt.flow_s, +- &ed_addr_sets.addr_sets, &ed_port_groups.port_groups); ++ &as_data->addr_sets, &pg_data->port_groups); + if (error) { + unixctl_command_reply_error(pending_pkt.conn, error); + free(error); +@@ -2173,12 +2184,16 @@ main(int argc, char *argv[]) + } + + if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) { +- struct shash_node *iter, *iter_next; +- SHASH_FOR_EACH_SAFE (iter, iter_next, &ed_ct_zones.pending) { +- struct ct_zone_pending_entry *ctzpe = iter->data; +- if (ctzpe->state == CT_ZONE_DB_SENT) { +- shash_delete(&ed_ct_zones.pending, iter); +- free(ctzpe); ++ ct_zones_data = engine_get_data(&en_ct_zones); ++ if (ct_zones_data) { ++ struct shash_node *iter, *iter_next; ++ SHASH_FOR_EACH_SAFE (iter, iter_next, ++ &ct_zones_data->pending) { ++ struct ct_zone_pending_entry *ctzpe = iter->data; ++ if (ctzpe->state == CT_ZONE_DB_SENT) { ++ shash_delete(&ct_zones_data->pending, iter); ++ free(ctzpe); ++ } + } + } + } +diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c +index 59b5cac..9b1479a 100644 +--- a/ovn/lib/inc-proc-eng.c ++++ b/ovn/lib/inc-proc-eng.c +@@ -103,13 +103,16 @@ engine_get_nodes(struct engine_node *node, size_t *n_count) + } + + void +-engine_init(struct engine_node *node) ++engine_init(struct engine_node *node, struct engine_arg *arg) + { + engine_nodes = engine_get_nodes(node, &engine_n_nodes); + + for (size_t i = 0; i < engine_n_nodes; i++) { + if (engine_nodes[i]->init) { +- engine_nodes[i]->init(engine_nodes[i]); ++ engine_nodes[i]->data = ++ engine_nodes[i]->init(engine_nodes[i], arg); ++ } else { ++ engine_nodes[i]->data = NULL; + } + } + } +@@ -119,8 +122,9 @@ engine_cleanup(void) + { + for (size_t i = 0; i < engine_n_nodes; i++) { + if (engine_nodes[i]->cleanup) { +- engine_nodes[i]->cleanup(engine_nodes[i]); ++ engine_nodes[i]->cleanup(engine_nodes[i]->data); + } ++ free(engine_nodes[i]->data); + } + free(engine_nodes); + engine_nodes = NULL; +@@ -140,9 +144,16 @@ engine_get_input(const char *input_name, struct engine_node *node) + return NULL; + } + ++void * ++engine_get_input_data(const char *input_name, struct engine_node *node) ++{ ++ struct engine_node *input_node = engine_get_input(input_name, node); ++ return engine_get_data(input_node); ++} ++ + void + engine_add_input(struct engine_node *node, struct engine_node *input, +- bool (*change_handler)(struct engine_node *)) ++ bool (*change_handler)(struct engine_node *, void *)) + { + ovs_assert(node->n_inputs < ENGINE_MAX_INPUT); + node->inputs[node->n_inputs].node = input; +@@ -153,7 +164,7 @@ engine_add_input(struct engine_node *node, struct engine_node *input, + struct ovsdb_idl_index * + engine_ovsdb_node_get_index(struct engine_node *node, const char *name) + { +- struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data; ++ struct ed_type_ovsdb_table *ed = node->data; + for (size_t i = 0; i < ed->n_indexes; i++) { + if (!strcmp(ed->indexes[i].name, name)) { + return ed->indexes[i].index; +@@ -167,7 +178,7 @@ void + engine_ovsdb_node_add_index(struct engine_node *node, const char *name, + struct ovsdb_idl_index *index) + { +- struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data; ++ struct ed_type_ovsdb_table *ed = node->data; + ovs_assert(ed->n_indexes < ENGINE_MAX_OVSDB_INDEX); + + ed->indexes[ed->n_indexes].name = name; +@@ -192,6 +203,19 @@ engine_set_node_state_at(struct engine_node *node, + node->state = state; + } + ++static bool ++engine_node_valid(struct engine_node *node) ++{ ++ if (node->state == EN_UPDATED || node->state == EN_VALID) { ++ return true; ++ } ++ ++ if (node->is_valid) { ++ return node->is_valid(node); ++ } ++ return false; ++} ++ + bool + engine_node_changed(struct engine_node *node) + { +@@ -215,6 +239,21 @@ engine_aborted(void) + return engine_run_aborted; + } + ++void * ++engine_get_data(struct engine_node *node) ++{ ++ if (engine_node_valid(node)) { ++ return node->data; ++ } ++ return NULL; ++} ++ ++void * ++engine_get_internal_data(struct engine_node *node) ++{ ++ return node->data; ++} ++ + void + engine_init_run(void) + { +@@ -240,7 +279,7 @@ engine_recompute(struct engine_node *node, bool forced, bool allowed) + } + + /* Run the node handler which might change state. */ +- node->run(node); ++ node->run(node, node->data); + } + + /* Return true if the node could be computed, false otherwise. */ +@@ -256,7 +295,7 @@ engine_compute(struct engine_node *node, bool recompute_allowed) + /* If the input change can't be handled incrementally, run + * the node handler. + */ +- if (!node->inputs[i].change_handler(node)) { ++ if (!node->inputs[i].change_handler(node, node->data)) { + VLOG_DBG("node: %s, can't handle change for input %s, " + "fall back to recompute", + node->name, node->inputs[i].node->name); +@@ -273,7 +312,7 @@ engine_run_node(struct engine_node *node, bool recompute_allowed) + { + if (!node->n_inputs) { + /* Run the node handler which might change state. */ +- node->run(node); ++ node->run(node, node->data); + return; + } + +@@ -345,7 +384,7 @@ engine_need_run(void) + continue; + } + +- engine_nodes[i]->run(engine_nodes[i]); ++ engine_nodes[i]->run(engine_nodes[i], engine_nodes[i]->data); + VLOG_DBG("input node: %s, state: %s", engine_nodes[i]->name, + engine_node_state_name[engine_nodes[i]->state]); + if (engine_nodes[i]->state == EN_UPDATED) { +diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h +index 2f3ff62..9cc99a3 100644 +--- a/ovn/lib/inc-proc-eng.h ++++ b/ovn/lib/inc-proc-eng.h +@@ -68,6 +68,12 @@ struct engine_context { + struct ovsdb_idl_txn *ovnsb_idl_txn; + }; + ++/* Arguments to be passed to the engine at engine_init(). */ ++struct engine_arg { ++ struct ovsdb_idl *sb_idl; ++ struct ovsdb_idl *ovs_idl; ++}; ++ + struct engine_node; + + struct engine_node_input { +@@ -79,7 +85,7 @@ struct engine_node_input { + * - true: if change can be handled + * - false: if change cannot be handled (indicating full recompute needed) + */ +- bool (*change_handler)(struct engine_node *node); ++ bool (*change_handler)(struct engine_node *node, void *data); + }; + + enum engine_node_state { +@@ -106,30 +112,42 @@ struct engine_node { + /* Inputs of this node. */ + struct engine_node_input inputs[ENGINE_MAX_INPUT]; + +- /* Data of this node. It is vague and interpreted by the related functions. +- * The content of the data should be changed only by the change_handlers +- * and run() function of the current node. Users should ensure that the +- * data is read-only in change-handlers of the nodes that depends on this +- * node. */ ++ /* A pointer to node internal data. The data is safely accessible to ++ * users through the engine_get_data() API. For special cases, when the ++ * data is known to be valid (e.g., at init time), users can also call ++ * engine_get_internal_data(). ++ */ + void *data; + + /* State of the node after the last engine run. */ + enum engine_node_state state; + +- /* Method to initialize data. It may be NULL. */ +- void (*init)(struct engine_node *); ++ /* Method to allocate and initialize node data. It may be NULL. ++ * The user supplied argument 'arg' is passed from the call to ++ * engine_init(). ++ */ ++ void *(*init)(struct engine_node *node, struct engine_arg *arg); + + /* Method to clean up data. It may be NULL. */ +- void (*cleanup)(struct engine_node *); ++ void (*cleanup)(void *data); + + /* Fully processes all inputs of this node and regenerates the data +- * of this node */ +- void (*run)(struct engine_node *); ++ * of this node. The pointer to the node's data is passed as argument. ++ */ ++ void (*run)(struct engine_node *node, void *data); ++ ++ /* Method to validate if the 'internal_data' is valid. This allows users ++ * to customize when 'data' can be used (e.g., even if the node ++ * hasn't been refreshed in the last iteration, if 'data' ++ * doesn't store pointers to DB records it's still safe to use). ++ */ ++ bool (*is_valid)(struct engine_node *); + }; + + /* Initialize the data for the engine nodes. It calls each node's +- * init() method if not NULL. It should be called before the main loop. */ +-void engine_init(struct engine_node *node); ++ * init() method if not NULL passing the user supplied 'arg'. ++ * It should be called before the main loop. */ ++void engine_init(struct engine_node *node, struct engine_arg *arg); + + /* Initialize the engine nodes for a new run. It should be called in the + * main processing loop before every potential engine_run(). +@@ -155,12 +173,15 @@ bool engine_need_run(void); + struct engine_node * engine_get_input(const char *input_name, + struct engine_node *); + ++/* Get the data from the input node with for */ ++void *engine_get_input_data(const char *input_name, struct engine_node *); ++ + /* Add an input (dependency) for , with corresponding change_handler, + * which can be NULL. If the change_handler is NULL, the engine will not + * be able to process the change incrementally, and will fall back to call + * the run method to recompute. */ + void engine_add_input(struct engine_node *node, struct engine_node *input, +- bool (*change_handler)(struct engine_node *)); ++ bool (*change_handler)(struct engine_node *, void *)); + + /* Force the engine to recompute everything if set to true. It is used + * in circumstances when we are not sure there is change or not, or +@@ -185,6 +206,25 @@ bool engine_has_run(void); + /* Returns true if during the last engine run we had to abort processing. */ + bool engine_aborted(void); + ++/* Return a pointer to node data accessible for users outside the processing ++ * engine. If the node data is not valid (e.g., last engine_run() failed or ++ * didn't happen), the node's is_valid() method is used to determine if the ++ * data can be safely accessed. If it's not the case, the function returns ++ * NULL. ++ * The content of the data should be changed only by the change_handlers ++ * and run() function of the current node. Users should ensure that the ++ * data is read-only in change-handlers of the nodes that depends on this ++ * node. ++ */ ++void *engine_get_data(struct engine_node *node); ++ ++/* Return a pointer to node data *without* performing any sanity checks on ++ * the state of the node. This may be used only in specific cases when data ++ * is guaranteed to be valid, e.g., immediately after initialization and ++ * before the first engine_run(). ++ */ ++void *engine_get_internal_data(struct engine_node *node); ++ + /* Set the state of the node and log changes. */ + #define engine_set_node_state(node, state) \ + engine_set_node_state_at(node, state, OVS_SOURCE_LOCATOR) +@@ -201,30 +241,42 @@ struct ed_type_ovsdb_table { + }; + + #define EN_OVSDB_GET(NODE) \ +- (((struct ed_type_ovsdb_table *)NODE->data)->table) ++ (((struct ed_type_ovsdb_table *)(NODE)->data)->table) + + struct ovsdb_idl_index * engine_ovsdb_node_get_index(struct engine_node *, + const char *name); + ++/* Adds an OVSDB IDL index to the node. This should be called only after ++ * engine_init() as the index is stored in the node data. ++ */ + void engine_ovsdb_node_add_index(struct engine_node *, const char *name, + struct ovsdb_idl_index *); + + /* Macro to define an engine node. */ +-#define ENGINE_NODE(NAME, NAME_STR) \ ++#define ENGINE_NODE_DEF(NAME, NAME_STR) \ + struct engine_node en_##NAME = { \ + .name = NAME_STR, \ +- .data = &ed_##NAME, \ ++ .data = NULL, \ + .state = EN_STALE, \ + .init = en_##NAME##_init, \ + .run = en_##NAME##_run, \ + .cleanup = en_##NAME##_cleanup, \ ++ .is_valid = en_##NAME##_is_valid, \ + }; + ++#define ENGINE_NODE_CUSTOM_DATA(NAME, NAME_STR) \ ++ ENGINE_NODE_DEF(NAME, NAME_STR) ++ ++#define ENGINE_NODE(NAME, NAME_STR) \ ++ static bool (*en_##NAME##_is_valid)(struct engine_node *node) = NULL; \ ++ ENGINE_NODE_DEF(NAME, NAME_STR) ++ + /* Macro to define member functions of an engine node which represents + * a table of OVSDB */ + #define ENGINE_FUNC_OVSDB(DB_NAME, TBL_NAME) \ + static void \ +-en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \ ++en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node, \ ++ void *data OVS_UNUSED) \ + { \ + const struct DB_NAME##rec_##TBL_NAME##_table *table = \ + EN_OVSDB_GET(node); \ +@@ -234,10 +286,18 @@ en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \ + } \ + engine_set_node_state(node, EN_VALID); \ + } \ +-static void (*en_##DB_NAME##_##TBL_NAME##_init)(struct engine_node *node) \ +- = NULL; \ +-static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \ +- = NULL; ++static void *en_##DB_NAME##_##TBL_NAME##_init( \ ++ struct engine_node *node OVS_UNUSED, \ ++ struct engine_arg *arg) \ ++{ \ ++ struct ovsdb_idl *idl = arg->DB_NAME##_idl; \ ++ struct ed_type_ovsdb_table *data = xzalloc(sizeof *data); \ ++ data->table = DB_NAME##rec_##TBL_NAME##_table_get(idl); \ ++ return data; \ ++} \ ++static void en_##DB_NAME##_##TBL_NAME##_cleanup(void *data OVS_UNUSED) \ ++{ \ ++} + + /* Macro to define member functions of an engine node which represents + * a table of OVN SB DB */ +@@ -250,21 +310,16 @@ static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \ + ENGINE_FUNC_OVSDB(ovs, TBL_NAME) + + /* Macro to define an engine node which represents a table of OVSDB */ +-#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR, IDL) \ +- struct ed_type_ovsdb_table ed_##DB_NAME##_##TBL_NAME; \ +- memset(&ed_##DB_NAME##_##TBL_NAME, 0, sizeof ed_##DB_NAME##_##TBL_NAME); \ +- ovs_assert(IDL); \ +- ed_##DB_NAME##_##TBL_NAME.table = \ +- DB_NAME##rec_##TBL_NAME##_table_get(IDL); \ ++#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR) \ + ENGINE_NODE(DB_NAME##_##TBL_NAME, DB_NAME_STR"_"TBL_NAME_STR) + + /* Macro to define an engine node which represents a table of OVN SB DB */ + #define ENGINE_NODE_SB(TBL_NAME, TBL_NAME_STR) \ +- ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR, ovnsb_idl_loop.idl); ++ ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR); + + /* Macro to define an engine node which represents a table of open_vswitch + * DB */ + #define ENGINE_NODE_OVS(TBL_NAME, TBL_NAME_STR) \ +- ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR, ovs_idl_loop.idl); ++ ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR); + + #endif /* ovn/lib/inc-proc-eng.h */ +-- +1.8.3.1 + diff --git a/SOURCES/0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch b/SOURCES/0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch new file mode 100644 index 0000000..7058cb2 --- /dev/null +++ b/SOURCES/0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch @@ -0,0 +1,148 @@ +From 7a4814d4a50a78a9434df7f55ca0491f4736c658 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Wed, 22 Jan 2020 19:02:18 +0530 +Subject: [PATCH 4/4] ovn-controller: Remove ports from struct local_datapaths. + +struct local_datapaths stores the array of port bindings for each datapath. +These ports are used only in the pinctrl module to check if a mac binding +has been learnt for the buffered packets. + +MAC bindings are always learnt in the router pipeline and so +logical_port column of MAC_Binding table will always refer to a +logical router port. run_buffered_binding() of pinctrl module can use +the peer ports stored in the struct local_datapaths instead. +This would save many calls to mac_binding_lookup(). + +This patch doesn't store the array of port bindings for each local +datapath as it is not required at all. + +Earlier, the peer ports were stored only for patch port bindings. But we can have +peer ports even for l3gateway port bindings. This patch now considers l3gateway +ports also for storing the peer ports in struct local_datapaths. + +Acked-by: Han Zhou +Signed-off-by: Numan Siddique + +(cherry-picked from upstream master commit 6c7d9f1ff6a50f695bfd13be3912f4e82c5045f5) + +Change-Id: I30e984df51c1d8f81b74b863aaf37778fffc6b96 +--- + ovn/controller/binding.c | 26 +++++++++++++------------- + ovn/controller/ovn-controller.c | 2 -- + ovn/controller/ovn-controller.h | 4 ---- + ovn/controller/pinctrl.c | 11 +++++++++-- + 4 files changed, 22 insertions(+), 21 deletions(-) + +diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c +index 1380a3e6e..d94e43893 100644 +--- a/ovn/controller/binding.c ++++ b/ovn/controller/binding.c +@@ -146,7 +146,7 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, + const struct sbrec_port_binding *pb; + SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target, + sbrec_port_binding_by_datapath) { +- if (!strcmp(pb->type, "patch")) { ++ if (!strcmp(pb->type, "patch") || !strcmp(pb->type, "l3gateway")) { + const char *peer_name = smap_get(&pb->options, "peer"); + if (peer_name) { + const struct sbrec_port_binding *peer; +@@ -155,11 +155,18 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, + peer_name); + + if (peer && peer->datapath) { +- add_local_datapath__(sbrec_datapath_binding_by_key, +- sbrec_port_binding_by_datapath, +- sbrec_port_binding_by_name, +- peer->datapath, false, +- depth + 1, local_datapaths); ++ if (!strcmp(pb->type, "patch")) { ++ /* Add the datapath to local datapath only for patch ++ * ports. For l3gateway ports, since gateway router ++ * resides on one chassis, we don't need to add. ++ * Otherwise, all other chassis might create patch ++ * ports between br-int and the provider bridge. */ ++ add_local_datapath__(sbrec_datapath_binding_by_key, ++ sbrec_port_binding_by_datapath, ++ sbrec_port_binding_by_name, ++ peer->datapath, false, ++ depth + 1, local_datapaths); ++ } + ld->n_peer_ports++; + if (ld->n_peer_ports > ld->n_allocated_peer_ports) { + ld->peer_ports = +@@ -172,13 +179,6 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key, + } + } + } +- +- ld->n_ports++; +- if (ld->n_ports > ld->n_allocated_ports) { +- ld->ports = x2nrealloc(ld->ports, &ld->n_allocated_ports, +- sizeof *ld->ports); +- } +- ld->ports[ld->n_ports - 1] = pb; + } + sbrec_port_binding_index_destroy_row(target); + } +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index 8d7ea89c4..b80d6c0dc 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -929,7 +929,6 @@ en_runtime_data_cleanup(void *data) + HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, + &rt_data->local_datapaths) { + free(cur_node->peer_ports); +- free(cur_node->ports); + hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node); + free(cur_node); + } +@@ -953,7 +952,6 @@ en_runtime_data_run(struct engine_node *node, void *data) + struct local_datapath *cur_node, *next_node; + HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, local_datapaths) { + free(cur_node->peer_ports); +- free(cur_node->ports); + hmap_remove(local_datapaths, &cur_node->hmap_node); + free(cur_node); + } +diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h +index 09ef4b632..8bff57c56 100644 +--- a/ovn/controller/ovn-controller.h ++++ b/ovn/controller/ovn-controller.h +@@ -60,10 +60,6 @@ struct local_datapath { + * hypervisor. */ + bool has_local_l3gateway; + +- const struct sbrec_port_binding **ports; +- size_t n_ports; +- size_t n_allocated_ports; +- + struct { + const struct sbrec_port_binding *local; + const struct sbrec_port_binding *remote; +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index 5636fc1e0..800bde7e7 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -2973,10 +2973,17 @@ run_buffered_binding(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, + bool notify = false; + + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { ++ /* MAC_Binding.logical_port will always belong to a ++ * a router datapath. Hence we can skip logical switch ++ * datapaths. ++ * */ ++ if (datapath_is_switch(ld->datapath)) { ++ continue; ++ } + +- for (size_t i = 0; i < ld->n_ports; i++) { ++ for (size_t i = 0; i < ld->n_peer_ports; i++) { + +- const struct sbrec_port_binding *pb = ld->ports[i]; ++ const struct sbrec_port_binding *pb = ld->peer_ports[i].local; + struct buffered_packets *cur_qp, *next_qp; + HMAP_FOR_EACH_SAFE (cur_qp, next_qp, hmap_node, + &buffered_packets_map) { +-- +2.26.2 + diff --git a/SOURCES/0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch b/SOURCES/0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch new file mode 100644 index 0000000..b5f63e2 --- /dev/null +++ b/SOURCES/0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch @@ -0,0 +1,652 @@ +From 193b2aefdb41d55209193af1f5e8c8dacb861c84 Mon Sep 17 00:00:00 2001 +From: Dumitru Ceara +Date: Tue, 24 Sep 2019 10:02:49 +0200 +Subject: [PATCH 4/4] ovn-northd: Add static IP multicast flood configuration + +Add the following new configuration options to the +Logical_Switch_Port:options column in the OVN Northbound database: + +- mcast_flood: if set to 'true' all incoming IP multicast traffic + (except IP multicast reports) entering the switch will also be + flooded on the logical switch port. +- mcast_flood_reports: if set to 'true' all incoming IP multicast + entering the switch will also be flooded on the logical switch + port. A clone of the packets is also processed by ovn-controller + for snooping. + +Add the following new configuration option to the +Logical_Router_Port:options column in the OVN Northbound database: + +- mcast_flood: if set to 'true' all incoming IP multicast traffic + (including IP multicast reports) entering the router will be also + flooded on the logical router port. + +Due to the fact that in the router pipeline multicast reports are +not treated in a special way there's no need for an explicit +'mcast_flood_reports' option for router ports. + +Signed-off-by: Dumitru Ceara +Acked-by: Mark Michelson +Signed-off-by: Mark Michelson + +(cherry-picked from ovn commit 79308138891ae04a02a07068501696ef78157912) +--- + ovn/lib/mcast-group-index.h | 2 + + ovn/northd/ovn-northd.8.xml | 30 ++++- + ovn/northd/ovn-northd.c | 212 +++++++++++++++++++++++++++++++----- + ovn/ovn-nb.xml | 34 ++++++ + tests/ovn.at | 81 +++++++++++++- + 5 files changed, 325 insertions(+), 34 deletions(-) + +diff --git a/ovn/lib/mcast-group-index.h b/ovn/lib/mcast-group-index.h +index 6249cac99..930963b1b 100644 +--- a/ovn/lib/mcast-group-index.h ++++ b/ovn/lib/mcast-group-index.h +@@ -28,6 +28,8 @@ enum ovn_mcast_tunnel_keys { + OVN_MCAST_FLOOD_TUNNEL_KEY = OVN_MIN_MULTICAST, + OVN_MCAST_UNKNOWN_TUNNEL_KEY, + OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY, ++ OVN_MCAST_MROUTER_STATIC_TUNNEL_KEY, ++ OVN_MCAST_STATIC_TUNNEL_KEY, + OVN_MIN_IP_MULTICAST, + OVN_MAX_IP_MULTICAST = OVN_MAX_MULTICAST, + }; +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index ec9020d2a..b9ae28e14 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -954,7 +954,11 @@ output; +
    • + A priority-100 flow that punts all IGMP packets to + ovn-controller if IGMP snooping is enabled on the +- logical switch. ++ logical switch. The flow also forwards the IGMP packets to the ++ MC_MROUTER_STATIC multicast group, which ++ ovn-northd populates with all the logical ports that ++ have ++ :mcast_flood_reports='true'. +
    • + +
    • +@@ -976,10 +980,15 @@ output; + +
    • + A priority-80 flow that forwards all unregistered IP multicast traffic +- to the MC_MROUTER_FLOOD multicast group, if any. +- Otherwise the flow drops all unregistered IP multicast packets. This +- flow is added only if :mcast_flood_unregistered='false'. ++ to the MC_STATIC multicast group, which ++ ovn-northd populates with all the logical ports that ++ have ++ :mcast_flood='true'. The flow also forwards ++ unregistered IP multicast traffic to the MC_MROUTER_FLOOD ++ multicast group, which ovn-northd populates with all the ++ logical ports connected to logical routers that have ++ ++ :mcast_relay='true'. +
    • + +
    • +@@ -2092,6 +2101,17 @@ output; +

      +
    • + ++
    • ++

      ++ Priority-450 flow that matches unregistered IP multicast traffic ++ and sets outport to the MC_STATIC ++ multicast group, which ovn-northd populates with the ++ logical ports that have ++ ++ :mcast_flood='true'. ++

      ++
    • ++ +
    • +

      + For distributed logical routers where one of the logical router +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 2f6826f17..250529eb7 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -459,7 +459,12 @@ struct mcast_switch_info { + * should be flooded to the mrouter. Only + * applicable if flood_unregistered == false. + */ +- ++ bool flood_reports; /* True if the switch has at least one port ++ * configured to flood reports. ++ */ ++ bool flood_static; /* True if the switch has at least one port ++ * configured to flood traffic. ++ */ + int64_t table_size; /* Max number of IP multicast groups. */ + int64_t idle_timeout; /* Timeout after which an idle group is + * flushed. +@@ -477,7 +482,10 @@ struct mcast_switch_info { + }; + + struct mcast_router_info { +- bool relay; /* True if the router should relay IP multicast. */ ++ bool relay; /* True if the router should relay IP multicast. */ ++ bool flood_static; /* True if the router has at least one port configured ++ * to flood traffic. ++ */ + }; + + struct mcast_info { +@@ -492,6 +500,34 @@ struct mcast_info { + }; + }; + ++struct mcast_port_info { ++ bool flood; /* True if the port should flood IP multicast traffic ++ * regardless if it's registered or not. */ ++ bool flood_reports; /* True if the port should flood IP multicast reports ++ * (e.g., IGMP join/leave). */ ++}; ++ ++static void ++init_mcast_port_info(struct mcast_port_info *mcast_info, ++ const struct nbrec_logical_switch_port *nbsp, ++ const struct nbrec_logical_router_port *nbrp) ++{ ++ if (nbsp) { ++ mcast_info->flood = ++ smap_get_bool( ->options, "mcast_flood", false); ++ mcast_info->flood_reports = ++ smap_get_bool( ->options, "mcast_flood_reports", ++ false); ++ } else if (nbrp) { ++ /* We don't process multicast reports in any special way on logical ++ * routers so just treat them as regular multicast traffic. ++ */ ++ mcast_info->flood = ++ smap_get_bool(&nbrp->options, "mcast_flood", false); ++ mcast_info->flood_reports = mcast_info->flood; ++ } ++} ++ + static uint32_t + ovn_mcast_group_allocate_key(struct mcast_info *mcast_info) + { +@@ -1033,7 +1069,7 @@ build_datapaths(struct northd_context *ctx, struct hmap *datapaths, + ovn_datapath_destroy(datapaths, od); + } + } +- ++ + struct ovn_port { + struct hmap_node key_node; /* Index on 'key'. */ + char *key; /* nbs->name, nbr->name, sb->logical_port. */ +@@ -1055,6 +1091,9 @@ struct ovn_port { + + struct lport_addresses lrp_networks; + ++ /* Logical port multicast data. */ ++ struct mcast_port_info mcast_info; ++ + bool derived; /* Indicates whether this is an additional port + * derived from nbsp or nbrp. */ + +@@ -1071,6 +1110,23 @@ struct ovn_port { + struct ovs_list list; /* In list of similar records. */ + }; + ++static void ++ovn_port_set_sb(struct ovn_port *op, ++ const struct sbrec_port_binding *sb) ++{ ++ op->sb = sb; ++} ++ ++static void ++ovn_port_set_nb(struct ovn_port *op, ++ const struct nbrec_logical_switch_port *nbsp, ++ const struct nbrec_logical_router_port *nbrp) ++{ ++ op->nbsp = nbsp; ++ op->nbrp = nbrp; ++ init_mcast_port_info(&op->mcast_info, op->nbsp, op->nbrp); ++} ++ + static struct ovn_port * + ovn_port_create(struct hmap *ports, const char *key, + const struct nbrec_logical_switch_port *nbsp, +@@ -1084,9 +1140,8 @@ ovn_port_create(struct hmap *ports, const char *key, + op->json_key = ds_steal_cstr(&json_key); + + op->key = xstrdup(key); +- op->sb = sb; +- op->nbsp = nbsp; +- op->nbrp = nbrp; ++ ovn_port_set_sb(op, sb); ++ ovn_port_set_nb(op, nbsp, nbrp); + op->derived = false; + hmap_insert(ports, &op->key_node, hash_string(op->key, 0)); + return op; +@@ -1882,7 +1937,7 @@ join_logical_ports(struct northd_context *ctx, + nbsp->name); + continue; + } +- op->nbsp = nbsp; ++ ovn_port_set_nb(op, nbsp, NULL); + ovs_list_remove(&op->list); + + uint32_t queue_id = smap_get_int(&op->sb->options, +@@ -1973,7 +2028,7 @@ join_logical_ports(struct northd_context *ctx, + nbrp->name); + continue; + } +- op->nbrp = nbrp; ++ ovn_port_set_nb(op, NULL, nbrp); + ovs_list_remove(&op->list); + ovs_list_push_back(both, &op->list); + +@@ -2018,7 +2073,7 @@ join_logical_ports(struct northd_context *ctx, + struct ovn_port *crp = ovn_port_find(ports, redirect_name); + if (crp) { + crp->derived = true; +- crp->nbrp = nbrp; ++ ovn_port_set_nb(crp, NULL, nbrp); + ovs_list_remove(&crp->list); + ovs_list_push_back(both, &crp->list); + } else { +@@ -2896,7 +2951,7 @@ build_ports(struct northd_context *ctx, + continue; + } + +- op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn); ++ ovn_port_set_sb(op, sbrec_port_binding_insert(ctx->ovnsb_txn)); + ovn_port_update_sbrec(ctx, sbrec_chassis_by_name, op, + &chassis_qdisc_queues, + &active_ha_chassis_grps); +@@ -2938,6 +2993,14 @@ static const struct multicast_group mc_flood = + static const struct multicast_group mc_mrouter_flood = + { MC_MROUTER_FLOOD, OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY }; + ++#define MC_MROUTER_STATIC "_MC_mrouter_static" ++static const struct multicast_group mc_mrouter_static = ++ { MC_MROUTER_STATIC, OVN_MCAST_MROUTER_STATIC_TUNNEL_KEY }; ++ ++#define MC_STATIC "_MC_static" ++static const struct multicast_group mc_static = ++ { MC_STATIC, OVN_MCAST_STATIC_TUNNEL_KEY }; ++ + #define MC_UNKNOWN "_MC_unknown" + static const struct multicast_group mc_unknown = + { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY }; +@@ -3151,7 +3214,23 @@ ovn_igmp_group_get_ports(const struct sbrec_igmp_group *sb_igmp_group, + + *n_ports = 0; + for (size_t i = 0; i < sb_igmp_group->n_ports; i++) { +- ports[(*n_ports)] = ++ struct ovn_port *port = ++ ovn_port_find(ovn_ports, sb_igmp_group->ports[i]->logical_port); ++ ++ /* If this is already a flood port skip it for the group. */ ++ if (port->mcast_info.flood) { ++ continue; ++ } ++ ++ /* If this is already a port of a router on which relay is enabled, ++ * skip it for the group. Traffic is flooded there anyway. ++ */ ++ if (port->peer && port->peer->od && ++ port->peer->od->mcast_info.rtr.relay) { ++ continue; ++ } ++ ++ ports[(*n_ports)] = port; + ovn_port_find(ovn_ports, sb_igmp_group->ports[i]->logical_port); + if (ports[(*n_ports)]) { + (*n_ports)++; +@@ -5531,9 +5610,18 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw; + + if (mcast_sw_info->enabled) { ++ ds_clear(&actions); ++ if (mcast_sw_info->flood_reports) { ++ ds_put_cstr(&actions, ++ "clone { " ++ "outport = \""MC_MROUTER_STATIC"\"; " ++ "output; " ++ "};"); ++ } ++ ds_put_cstr(&actions, "igmp;"); + /* Punt IGMP traffic to controller. */ + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100, +- "ip4 && ip.proto == 2", "igmp;"); ++ "ip4 && ip.proto == 2", ds_cstr(&actions)); + + /* Flood all IP multicast traffic destined to 224.0.0.X to all + * ports - RFC 4541, section 2.1.2, item 2. +@@ -5542,17 +5630,30 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + "ip4 && ip4.dst == 224.0.0.0/24", + "outport = \""MC_FLOOD"\"; output;"); + +- /* Drop unregistered IP multicast if not allowed. */ ++ /* Forward uregistered IP multicast to routers with relay enabled ++ * and to any ports configured to flood IP multicast traffic. ++ * If configured to flood unregistered traffic this will be ++ * handled by the L2 multicast flow. ++ */ + if (!mcast_sw_info->flood_unregistered) { +- /* Forward unregistered IP multicast to mrouter (if any). */ ++ ds_clear(&actions); ++ + if (mcast_sw_info->flood_relay) { +- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80, +- "ip4 && ip4.mcast", +- "outport = \""MC_MROUTER_FLOOD"\"; output;"); ++ ds_put_cstr(&actions, ++ "clone { " ++ "outport = \""MC_MROUTER_FLOOD"\"; " ++ "output; " ++ "}; "); ++ } ++ ++ if (mcast_sw_info->flood_static) { ++ ds_put_cstr(&actions, "outport =\""MC_STATIC"\"; output;"); + } else { +- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80, +- "ip4 && ip4.mcast", "drop;"); ++ ds_put_cstr(&actions, "drop;"); + } ++ ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80, ++ "ip4 && ip4.mcast", ds_cstr(&actions)); + } + } + +@@ -5582,11 +5683,20 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + + ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ", + igmp_group->mcgroup.name); ++ + /* Also flood traffic to all multicast routers with relay enabled. */ + if (mcast_sw_info->flood_relay) { + ds_put_cstr(&actions, + "clone { " +- "outport = \""MC_MROUTER_FLOOD "\"; output; " ++ "outport = \""MC_MROUTER_FLOOD "\"; " ++ "output; " ++ "};"); ++ } ++ if (mcast_sw_info->flood_static) { ++ ds_put_cstr(&actions, ++ "clone { " ++ "outport =\""MC_STATIC"\"; " ++ "output; " + "};"); + } + ds_put_format(&actions, "outport = \"%s\"; output; ", +@@ -7783,6 +7893,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (!od->nbr || !od->mcast_info.rtr.relay) { + continue; + } ++ + struct ovn_igmp_group *igmp_group; + + LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) { +@@ -7790,11 +7901,35 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_clear(&actions); + ds_put_format(&match, "ip4 && ip4.dst == %s ", + igmp_group->mcgroup.name); ++ if (od->mcast_info.rtr.flood_static) { ++ ds_put_cstr(&actions, ++ "clone { " ++ "outport = \""MC_STATIC"\"; " ++ "ip.ttl--; " ++ "next; " ++ "};"); ++ } + ds_put_format(&actions, "outport = \"%s\"; ip.ttl--; next;", + igmp_group->mcgroup.name); + ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 500, + ds_cstr(&match), ds_cstr(&actions)); + } ++ ++ /* If needed, flood unregistered multicast on statically configured ++ * ports. ++ */ ++ if (od->mcast_info.rtr.flood_static) { ++ ds_clear(&match); ++ ds_clear(&actions); ++ ds_put_format(&match, "ip4.mcast"); ++ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 450, ++ "ip4.mcast", ++ "clone { " ++ "outport = \""MC_STATIC"\"; " ++ "ip.ttl--; " ++ "next; " ++ "};"); ++ } + } + + /* Logical router ingress table 8: Policy. +@@ -8943,11 +9078,15 @@ build_mcast_groups(struct northd_context *ctx, + hmap_init(igmp_groups); + + HMAP_FOR_EACH (op, key_node, ports) { +- if (!op->nbsp) { +- continue; +- } +- +- if (lsp_is_enabled(op->nbsp)) { ++ if (op->nbrp && lrport_is_enabled(op->nbrp)) { ++ /* If this port is configured to always flood multicast traffic ++ * add it to the MC_STATIC group. ++ */ ++ if (op->mcast_info.flood) { ++ ovn_multicast_add(mcast_groups, &mc_static, op); ++ op->od->mcast_info.rtr.flood_static = true; ++ } ++ } else if (op->nbsp && lsp_is_enabled(op->nbsp)) { + ovn_multicast_add(mcast_groups, &mc_flood, op); + + /* If this port is connected to a multicast router then add it +@@ -8957,6 +9096,22 @@ build_mcast_groups(struct northd_context *ctx, + op->peer->od && op->peer->od->mcast_info.rtr.relay) { + ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op); + } ++ ++ /* If this port is configured to always flood multicast reports ++ * add it to the MC_MROUTER_STATIC group. ++ */ ++ if (op->mcast_info.flood_reports) { ++ ovn_multicast_add(mcast_groups, &mc_mrouter_static, op); ++ op->od->mcast_info.sw.flood_reports = true; ++ } ++ ++ /* If this port is configured to always flood multicast traffic ++ * add it to the MC_STATIC group. ++ */ ++ if (op->mcast_info.flood) { ++ ovn_multicast_add(mcast_groups, &mc_static, op); ++ op->od->mcast_info.sw.flood_static = true; ++ } + } + } + +@@ -9017,8 +9172,13 @@ build_mcast_groups(struct northd_context *ctx, + for (size_t i = 0; i < od->n_router_ports; i++) { + struct ovn_port *router_port = od->router_ports[i]->peer; + ++ /* If the router the port connects to doesn't have multicast ++ * relay enabled or if it was already configured to flood ++ * multicast traffic then skip it. ++ */ + if (!router_port || !router_port->od || +- !router_port->od->mcast_info.rtr.relay) { ++ !router_port->od->mcast_info.rtr.relay || ++ router_port->mcast_info.flood) { + continue; + } + +diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml +index c2472a04a..e0051db25 100644 +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -671,6 +671,26 @@ + + + ++ ++

      ++ These options apply when the port is part of a logical switch ++ which has ++ :mcast_snoop set to true. ++

      ++ ++ ++ If set to true, multicast packets (except reports) are ++ unconditionally forwarded to the specific port. ++ ++ ++ ++ If set to true, multicast reports are unconditionally ++ forwarded to the specific port. ++ ++ ++ + + + +@@ -1962,6 +1982,20 @@ + issues. +

      + ++ ++ ++

      ++ If set to true, multicast traffic (including reports) ++ are unconditionally forwarded to the specific port. ++

      ++ ++

      ++ This option applies when the port is part of a logical router which ++ has :mcast_relay set ++ to true. ++

      ++
      +
      + + +diff --git a/tests/ovn.at b/tests/ovn.at +index d52c97541..410a56fe5 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -16258,7 +16258,6 @@ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) + + # Flush IGMP groups. + ovn-sbctl ip-multicast-flush sw1 +-ovn-nbctl --wait=hv -t 3 sync + OVS_WAIT_UNTIL([ + total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` + test "${total_entries}" = "0" +@@ -16310,12 +16309,12 @@ send_ip_multicast_pkt hv2-vif4 hv2 \ + # Sleep a bit to make sure no traffic is received and then check. + sleep 1 + OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty]) +-OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) +-OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty]) + + # Enable IGMP relay on rtr +@@ -16386,6 +16385,82 @@ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) + OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty]) + ++# Flush IGMP groups. ++ovn-sbctl ip-multicast-flush sw1 ++ovn-sbctl ip-multicast-flush sw2 ++ovn-sbctl ip-multicast-flush sw3 ++OVS_WAIT_UNTIL([ ++ total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` ++ test "${total_entries}" = "0" ++]) ++ ++as hv1 reset_pcap_file hv1-vif1 hv1/vif1 ++as hv1 reset_pcap_file hv1-vif2 hv1/vif2 ++as hv1 reset_pcap_file hv1-vif3 hv1/vif3 ++as hv1 reset_pcap_file hv1-vif4 hv1/vif4 ++as hv2 reset_pcap_file hv2-vif1 hv2/vif1 ++as hv2 reset_pcap_file hv2-vif2 hv2/vif2 ++as hv2 reset_pcap_file hv2-vif3 hv2/vif3 ++as hv2 reset_pcap_file hv2-vif4 hv2/vif4 ++ ++truncate -s 0 expected_empty ++truncate -s 0 expected_switched ++truncate -s 0 expected_routed ++truncate -s 0 expected_reports ++ ++# Enable mcast_flood on sw1-p11 ++ovn-nbctl set Logical_Switch_Port sw1-p11 options:mcast_flood='true' ++ ++# Enable mcast_flood_reports on sw1-p21 ++ovn-nbctl set Logical_Switch_Port sw1-p21 options:mcast_flood_reports='true' ++# Enable mcast_flood on rtr-sw2 ++ovn-nbctl set Logical_Router_Port rtr-sw2 options:mcast_flood='true' ++# Enable mcast_flood on sw2-p1 ++ovn-nbctl set Logical_Switch_Port sw2-p1 options:mcast_flood='true' ++ ++ovn-nbctl --wait=hv sync ++ ++# Inject IGMP Join for 239.0.1.68 on sw1-p12. ++send_igmp_v3_report hv1-vif2 hv1 \ ++ 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \ ++ $(ip_to_hex 239 0 1 68) 04 e9b9 \ ++ expected_reports ++ ++# Check that the IGMP Group is learned. ++OVS_WAIT_UNTIL([ ++ total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l` ++ test "${total_entries}" = "1" ++]) ++ ++# Send traffic from sw1-p21 ++send_ip_multicast_pkt hv2-vif1 hv2 \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 ++store_ip_multicast_pkt \ ++ 000000000001 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \ ++ e518e518000a3b3a0000 expected_switched ++store_ip_multicast_pkt \ ++ 000000000200 01005e000144 \ ++ $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \ ++ e518e518000a3b3a0000 expected_routed ++ ++# Sleep a bit to make sure no duplicate traffic is received ++sleep 1 ++ ++# Check that traffic is switched to sw1-p11 and sw1-p12 ++# Check that IGMP join is flooded on sw1-p21 ++# Check that traffic is routed by rtr to rtr-sw2 and then switched to sw2-p1 ++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_switched]) ++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_switched]) ++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_routed]) ++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_reports]) ++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty]) ++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty]) ++ + OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP + +-- +2.21.0 + diff --git a/SOURCES/0004-tests-Updated-expected-log-message.patch b/SOURCES/0004-tests-Updated-expected-log-message.patch new file mode 100644 index 0000000..d29e0fb --- /dev/null +++ b/SOURCES/0004-tests-Updated-expected-log-message.patch @@ -0,0 +1,52 @@ +From fe8a82dfeb6b965472aa66996c85a0a0383ddec1 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Sat, 7 Dec 2019 23:08:56 -0500 +Subject: [PATCH ovn 4/4] tests: Updated expected log message + +A previous commit added more detail to this log message. Fix the test +to reflect the new text. + +Signed-off-by: Russell Bryant +Acked-by: Dumitru Ceara + +(cherry picked from upstream commit b3e0e3b12426d47a153506c92fa578fcc2ccf7c1) +--- + tests/ovn.at | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/tests/ovn.at b/tests/ovn.at +index e2565f2..90830dc 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -13674,22 +13674,22 @@ ovn-nbctl list logical_switch_port + # Now try to add duplicate addresses on a new port. These should all fail + ovn-nbctl --wait=sb lsp-add sw1 sw1-p5 + AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.1"], [1], [], +-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.1 ++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '10.0.0.1' found on logical switch port 'sw1-p1' + ]) + AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.2"], [1], [], +-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.2 ++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '10.0.0.2' found on logical switch port 'sw1-p1' + ]) + AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::1"], [1], [], +-[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::1 ++[ovn-nbctl: Error on switch sw1: duplicate IPv6 address 'aef0::1' found on logical switch port 'sw1-p1' + ]) + AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::2"], [1], [], +-[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::2 ++[ovn-nbctl: Error on switch sw1: duplicate IPv6 address 'aef0::2' found on logical switch port 'sw1-p1' + ]) + AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.2"], [1], [], +-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.2 ++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '192.168.0.2' found on logical switch port 'sw1-p2' + ]) + AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.3"], [1], [], +-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.3 ++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '192.168.0.3' found on logical switch port 'sw1-p3' + ]) + + # Now try re-setting sw1-p1. This should succeed +-- +1.8.3.1 + diff --git a/SOURCES/0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch b/SOURCES/0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch new file mode 100644 index 0000000..2b70413 --- /dev/null +++ b/SOURCES/0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch @@ -0,0 +1,477 @@ +From 6915baff6dabb2605abd055436f6e8ce7a5b5d19 Mon Sep 17 00:00:00 2001 +From: Ankur Sharma +Date: Tue, 10 Sep 2019 21:22:44 +0000 +Subject: [PATCH 05/12] Replace chassis mac with router port mac on destination + chassis + +During E-W routing for vlan backed networks, we replace router port +mac with chassis mac, when packet leaves the source hypervisor. + +As a result, the destination VM (on remote hypervisor) will see +chassis mac as source mac in received packet. + +Although, functionality wise this does not cause any issue, +however chassis mac being see as source on VM, will +lead to following: +a. INCONSISTENT SOURCE MAC: + If the destination VM moves to same hypervisor as sender, + then it will see router port mac as source mac. Whereas, on + a remote hypervisor, source mac will be the sender chassis mac. + + This will cause inconsistency in packet headers for the same + flow and could be confusing for someone looking at packet + captures inside the vm. + +b. SYSTEM MAC BEING EXPOSED TO VM: + Chassis mac is a CMS provided mac, i.e it is an infrastructure + mac. It is not a good practice to expose such values to VM, + which should not be seeing them in first place. + +In order to replace chassis mac with router port mac, we will +do following. + +a. Create conjunction for each chassis mac and router port vlan + id combination. For example, for a 2 node chassis setup, where + we have a logical router, connected to 4 logical switches with + vlan ids: 2000, 1000, 0 and 24, the conjunction flow will look + like following: + + cookie=0x0, duration=9094.608s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_src=aa:bb:cc:dd:ee:22 actions=conjunction(100,1/2) + cookie=0x0, duration=9094.608s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_src=aa:bb:cc:dd:ff:ee actions=conjunction(100,1/2) + + cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_vlan=2000 actions=conjunction(100,2/2) + cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_vlan=1000 actions=conjunction(100,2/2) + cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,vlan_tci=0x0000/0x1fff actions=conjunction(100,2/2) + cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_vlan=24 actions=conjunction(100,2/2) + +b. Using this conjunction as match, we can identify if packet entering destination + hypervisor was routed at the source or not. This will be done in table=0 (Physical to logical) + at priority=180. + For example: + cookie=0x0, duration=9795.957s, table=0, n_packets=1391, n_bytes=141882, idle_age=8396, priority=180,conj_id=100,in_port=146,dl_vlan=1000 actions=.........,mod_dl_src:00:00:01:01:02:03,... + +c. We use conjunction, as it will ensure that we do not end up having lot of flows + as we scale up. If we do not use conjunction, then we will have + N (number of chassis macs) X M (number of router vlans) number of ovs flows. + Conjunction converts it to N + M. + Consider a setup, with 500 Chassis and 500 routed vlans. + Without conjunction we will need 25000 (500 * 500) flows, + whereas with conjunction that number comes down to 1000 (500 + 500). + +Change-Id: Idae7f716703c9fe94bd742ad4bd412a9cabcb550 +Signed-off-by: Ankur Sharma +Signed-off-by: Numan Siddique +--- + ovn/controller/chassis.c | 2 +- + ovn/controller/chassis.h | 3 + + ovn/controller/ovn-controller.c | 5 + + ovn/controller/physical.c | 222 +++++++++++++++++++++++++++++++- + ovn/controller/physical.h | 1 + + ovn/ovn-architecture.7.xml | 10 +- + tests/ovn.at | 14 +- + 7 files changed, 244 insertions(+), 13 deletions(-) + +diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c +index 4cd340124..c53bb5385 100644 +--- a/ovn/controller/chassis.c ++++ b/ovn/controller/chassis.c +@@ -144,7 +144,7 @@ get_bridge_mappings(const struct smap *ext_ids) + return smap_get_def(ext_ids, "ovn-bridge-mappings", ""); + } + +-static const char * ++const char * + get_chassis_mac_mappings(const struct smap *ext_ids) + { + return smap_get_def(ext_ids, "ovn-chassis-mac-mappings", ""); +diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h +index 16a131a3b..bff2ca4c5 100644 +--- a/ovn/controller/chassis.h ++++ b/ovn/controller/chassis.h +@@ -27,6 +27,7 @@ struct sbrec_chassis; + struct sbrec_chassis_table; + struct sset; + struct eth_addr; ++struct smap; + + void chassis_register_ovs_idl(struct ovsdb_idl *); + const struct sbrec_chassis *chassis_run( +@@ -43,4 +44,6 @@ bool chassis_get_mac(const struct sbrec_chassis *chassis, + struct eth_addr *chassis_mac); + const char *chassis_get_id(void); + ++const char * get_chassis_mac_mappings(const struct smap *ext_ids); ++ + #endif /* ovn/chassis.h */ +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index e86410198..cb3d63cbb 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -1270,9 +1270,14 @@ en_flow_output_run(struct engine_node *node) + (struct sbrec_port_binding_table *)EN_OVSDB_GET( + engine_get_input("SB_port_binding", node)); + ++ struct sbrec_chassis_table *chassis_table = ++ (struct sbrec_chassis_table *)EN_OVSDB_GET( ++ engine_get_input("SB_chassis", node)); ++ + physical_run(sbrec_port_binding_by_name, + multicast_group_table, + port_binding_table, ++ chassis_table, + mff_ovn_geneve, + br_int, chassis, ct_zones, + local_datapaths, local_lports, +diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c +index bf1d0d716..9d553da9a 100644 +--- a/ovn/controller/physical.c ++++ b/ovn/controller/physical.c +@@ -47,9 +47,23 @@ + + VLOG_DEFINE_THIS_MODULE(physical); + ++/* Datapath zone IDs for connection tracking and NAT */ ++struct zone_ids { ++ int ct; /* MFF_LOG_CT_ZONE. */ ++ int dnat; /* MFF_LOG_DNAT_ZONE. */ ++ int snat; /* MFF_LOG_SNAT_ZONE. */ ++}; ++ ++static void ++load_logical_ingress_metadata(const struct sbrec_port_binding *binding, ++ const struct zone_ids *zone_ids, ++ struct ofpbuf *ofpacts_p); ++ + /* UUID to identify OF flows not associated with ovsdb rows. */ + static struct uuid *hc_uuid = NULL; + ++#define CHASSIS_MAC_TO_ROUTER_MAC_CONJID 100 ++ + void + physical_register_ovs_idl(struct ovsdb_idl *ovs_idl) + { +@@ -199,12 +213,6 @@ get_localnet_port(const struct hmap *local_datapaths, int64_t tunnel_key) + return ld ? ld->localnet_port : NULL; + } + +-/* Datapath zone IDs for connection tracking and NAT */ +-struct zone_ids { +- int ct; /* MFF_LOG_CT_ZONE. */ +- int dnat; /* MFF_LOG_DNAT_ZONE. */ +- int snat; /* MFF_LOG_SNAT_ZONE. */ +-}; + + static struct zone_ids + get_zone_ids(const struct sbrec_port_binding *binding, +@@ -385,6 +393,200 @@ put_remote_port_redirect_overlay(const struct + match, ofpacts_p, &binding->header_.uuid); + } + ++ ++static struct hmap remote_chassis_macs = ++ HMAP_INITIALIZER(&remote_chassis_macs); ++ ++/* Maps from a physical network name to the chassis macs of remote chassis. */ ++struct remote_chassis_mac { ++ struct hmap_node hmap_node; ++ char *chassis_mac; ++ char *chassis_id; ++}; ++ ++static void ++populate_remote_chassis_macs(const struct sbrec_chassis *my_chassis, ++ const struct sbrec_chassis_table *chassis_table) ++{ ++ const struct sbrec_chassis *chassis; ++ SBREC_CHASSIS_TABLE_FOR_EACH (chassis, chassis_table) { ++ ++ /* We want only remote chassis macs. */ ++ if (!strcmp(my_chassis->name, chassis->name)) { ++ continue; ++ } ++ ++ const char *tokens ++ = get_chassis_mac_mappings(&chassis->external_ids); ++ ++ if (!strlen(tokens)) { ++ continue; ++ } ++ ++ char *save_ptr = NULL; ++ char *token; ++ char *tokstr = xstrdup(tokens); ++ ++ /* Format for a chassis mac configuration is: ++ * ovn-chassis-mac-mappings="bridge-name1:MAC1,bridge-name2:MAC2" ++ */ ++ for (token = strtok_r(tokstr, ",", &save_ptr); ++ token != NULL; ++ token = strtok_r(NULL, ",", &save_ptr)) { ++ char *save_ptr2 = NULL; ++ char *chassis_mac_bridge = strtok_r(token, ":", &save_ptr2); ++ char *chassis_mac_str = strtok_r(NULL, "", &save_ptr2); ++ struct remote_chassis_mac *remote_chassis_mac = NULL; ++ remote_chassis_mac = xmalloc(sizeof *remote_chassis_mac); ++ hmap_insert(&remote_chassis_macs, &remote_chassis_mac->hmap_node, ++ hash_string(chassis_mac_bridge, 0)); ++ remote_chassis_mac->chassis_mac = xstrdup(chassis_mac_str); ++ remote_chassis_mac->chassis_id = xstrdup(chassis->name); ++ } ++ free(tokstr); ++ } ++} ++ ++static void ++free_remote_chassis_macs(void) ++{ ++ struct remote_chassis_mac *mac, *next_mac; ++ ++ HMAP_FOR_EACH_SAFE (mac, next_mac, hmap_node, &remote_chassis_macs) { ++ hmap_remove(&remote_chassis_macs, &mac->hmap_node); ++ free(mac->chassis_mac); ++ free(mac->chassis_id); ++ free(mac); ++ } ++} ++ ++static void ++put_chassis_mac_conj_id_flow(const struct sbrec_chassis_table *chassis_table, ++ const struct sbrec_chassis *chassis, ++ struct ofpbuf *ofpacts_p, ++ struct ovn_desired_flow_table *flow_table) ++{ ++ struct match match; ++ struct remote_chassis_mac *mac; ++ ++ populate_remote_chassis_macs(chassis, chassis_table); ++ ++ HMAP_FOR_EACH (mac, hmap_node, &remote_chassis_macs) { ++ struct eth_addr chassis_mac; ++ char *err_str = NULL; ++ struct ofpact_conjunction *conj; ++ ++ if ((err_str = str_to_mac(mac->chassis_mac, &chassis_mac))) { ++ free(err_str); ++ free_remote_chassis_macs(); ++ return; ++ } ++ ++ ofpbuf_clear(ofpacts_p); ++ match_init_catchall(&match); ++ ++ ++ match_set_dl_src(&match, chassis_mac); ++ ++ conj = ofpact_put_CONJUNCTION(ofpacts_p); ++ conj->id = CHASSIS_MAC_TO_ROUTER_MAC_CONJID; ++ conj->n_clauses = 2; ++ conj->clause = 0; ++ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 180, 0, ++ &match, ofpacts_p, hc_uuid); ++ } ++ ++ free_remote_chassis_macs(); ++} ++ ++static void ++put_replace_chassis_mac_flows(const struct simap *ct_zones, ++ const struct ++ sbrec_port_binding *localnet_port, ++ const struct hmap *local_datapaths, ++ struct ofpbuf *ofpacts_p, ++ ofp_port_t ofport, ++ struct ovn_desired_flow_table *flow_table) ++{ ++ /* Packets arriving on localnet port, could have been routed on ++ * source chassis and hence will have a chassis mac. ++ * conj_match will match source mac with chassis macs conjunction ++ * and replace it with corresponding router port mac. ++ */ ++ struct local_datapath *ld = get_local_datapath(local_datapaths, ++ localnet_port->datapath-> ++ tunnel_key); ++ ovs_assert(ld); ++ ++ int tag = localnet_port->tag ? *localnet_port->tag : 0; ++ struct zone_ids zone_ids = get_zone_ids(localnet_port, ct_zones); ++ ++ for (int i = 0; i < ld->n_peer_ports; i++) { ++ const struct sbrec_port_binding *rport_binding = ld->peer_ports[i]; ++ struct eth_addr router_port_mac; ++ char *err_str = NULL; ++ struct match match; ++ struct ofpact_mac *replace_mac; ++ ++ ovs_assert(rport_binding->n_mac == 1); ++ if ((err_str = str_to_mac(rport_binding->mac[0], &router_port_mac))) { ++ /* Parsing of mac failed. */ ++ VLOG_WARN("Parsing or router port mac failed for router port: %s, " ++ "with error: %s", rport_binding->logical_port, err_str); ++ free(err_str); ++ return; ++ } ++ ofpbuf_clear(ofpacts_p); ++ match_init_catchall(&match); ++ ++ /* Add flow, which will match on conjunction id and will ++ * replace source with router port mac */ ++ ++ /* Match on ingress port, vlan_id and conjunction id */ ++ match_set_in_port(&match, ofport); ++ match_set_conj_id(&match, CHASSIS_MAC_TO_ROUTER_MAC_CONJID); ++ ++ if (tag) { ++ match_set_dl_vlan(&match, htons(tag), 0); ++ } else { ++ match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI)); ++ } ++ ++ /* Actions */ ++ ++ if (tag) { ++ ofpact_put_STRIP_VLAN(ofpacts_p); ++ } ++ load_logical_ingress_metadata(localnet_port, &zone_ids, ofpacts_p); ++ replace_mac = ofpact_put_SET_ETH_SRC(ofpacts_p); ++ replace_mac->mac = router_port_mac; ++ ++ /* Resubmit to first logical ingress pipeline table. */ ++ put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); ++ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, ++ 180, 0, &match, ofpacts_p, hc_uuid); ++ ++ /* Provide second search criteria, i.e localnet port's ++ * vlan ID for conjunction flow */ ++ struct ofpact_conjunction *conj; ++ ofpbuf_clear(ofpacts_p); ++ match_init_catchall(&match); ++ ++ if (tag) { ++ match_set_dl_vlan(&match, htons(tag), 0); ++ } else { ++ match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI)); ++ } ++ ++ conj = ofpact_put_CONJUNCTION(ofpacts_p); ++ conj->id = CHASSIS_MAC_TO_ROUTER_MAC_CONJID; ++ conj->n_clauses = 2; ++ conj->clause = 1; ++ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 180, 0, &match, ++ ofpacts_p, hc_uuid); ++ } ++} ++ + static void + put_replace_router_port_mac_flows(struct ovsdb_idl_index + *sbrec_port_binding_by_name, +@@ -931,6 +1133,11 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, + &binding->header_.uuid); + } + ++ if (!strcmp(binding->type, "localnet")) { ++ put_replace_chassis_mac_flows(ct_zones, binding, local_datapaths, ++ ofpacts_p, ofport, flow_table); ++ } ++ + /* Table 65, Priority 100. + * ======================= + * +@@ -1224,6 +1431,7 @@ void + physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_multicast_group_table *multicast_group_table, + const struct sbrec_port_binding_table *port_binding_table, ++ const struct sbrec_chassis_table *chassis_table, + enum mf_field_id mff_ovn_geneve, + const struct ovsrec_bridge *br_int, + const struct sbrec_chassis *chassis, +@@ -1369,6 +1577,8 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name, + struct ofpbuf ofpacts; + ofpbuf_init(&ofpacts, 0); + ++ put_chassis_mac_conj_id_flow(chassis_table, chassis, &ofpacts, flow_table); ++ + /* Set up flows in table 0 for physical-to-logical translation and in table + * 64 for logical-to-physical translation. */ + const struct sbrec_port_binding *binding; +diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h +index c5544e8de..5934ab13e 100644 +--- a/ovn/controller/physical.h ++++ b/ovn/controller/physical.h +@@ -46,6 +46,7 @@ void physical_register_ovs_idl(struct ovsdb_idl *); + void physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name, + const struct sbrec_multicast_group_table *, + const struct sbrec_port_binding_table *, ++ const struct sbrec_chassis_table *chassis_table, + enum mf_field_id mff_ovn_geneve, + const struct ovsrec_bridge *br_int, + const struct sbrec_chassis *chassis, +diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml +index fa163c023..c43f16db7 100644 +--- a/ovn/ovn-architecture.7.xml ++++ b/ovn/ovn-architecture.7.xml +@@ -1463,7 +1463,7 @@ + (MAC,VLAN) tuple will seen by physical network from other chassis as + well, which could cause these issues: +

      +- ++ +
        +
      • + Continuous MAC moves in top-of-rack switch (ToR). +@@ -1479,9 +1479,11 @@ + +
      • + The destination chassis receives the packet via the localnet port and +- sends it to the integration bridge. The packet enters the +- ingress pipeline and then egress pipeline of the destination localnet +- logical switch and finally gets delivered to the destination VM port. ++ sends it to the integration bridge. Before entering the integration ++ bridge the source mac of the packet will be replaced with ++ router port mac again. The packet enters the ingress pipeline and ++ then egress pipeline of the destination localnet logical switch and ++ finally gets delivered to the destination VM port. +
      • + + +diff --git a/tests/ovn.at b/tests/ovn.at +index fe5d7f130..951a55226 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -14409,6 +14409,14 @@ hv_to_chassis_mac () { + esac + } + ++lrp_to_lrp_mac () { ++ case $1 in dnl ( ++ router-to-ls[[1]]) echo 000001010203 ;; dnl ( ++ router-to-ls[[2]]) echo 000001010205 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ + ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" + } +@@ -14476,7 +14484,9 @@ test_ip() { + # (and checksum). + outport_num=`vif_to_num $outport` + out_lrp=`vif_to_lrp $outport` +- echo f000000000${outport_num}aabbccddee${hv_num}${hv_num}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000 ++ lrp_mac=`lrp_to_lrp_mac $out_lrp` ++ echo f000000000${outport_num}${lrp_mac}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000 ++ + fi >> $outport.expected + done + } +@@ -16519,7 +16529,7 @@ test_ip() { + out_lrp=`vif_to_lrp $outport` + # For North-South, packet will come via gateway chassis, i.e hv3 + if test $inport = vif-north; then +- echo f00000000011aabbccddee3308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected ++ echo f0000000001100000101020308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected + fi + if test $outport = vif-north; then + echo f0f00000001100000101020708004500001c000000003e110200${src_ip}${dst_ip}0035111100080000 >> $outport.expected +-- +2.23.0 + diff --git a/SOURCES/0005-system-ovn-Add-IPv6-NAT-test-cases.patch b/SOURCES/0005-system-ovn-Add-IPv6-NAT-test-cases.patch new file mode 100644 index 0000000..82baf4b --- /dev/null +++ b/SOURCES/0005-system-ovn-Add-IPv6-NAT-test-cases.patch @@ -0,0 +1,918 @@ +From fcf41d43c6286f6794a690541f7ac657b216f46f Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Fri, 25 Oct 2019 11:58:32 -0400 +Subject: [PATCH 5/5] system-ovn: Add IPv6 NAT test cases + +These tests failed prior to the changes leading up to this one. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique +--- + tests/system-ovn.at | 862 +++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 860 insertions(+), 2 deletions(-) + +diff --git a/tests/system-ovn.at b/tests/system-ovn.at +index f88ad31e4..b3f90aae2 100644 +--- a/tests/system-ovn.at ++++ b/tests/system-ovn.at +@@ -176,6 +176,186 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d + /connection dropped.*/d"]) + AT_CLEANUP + ++AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, SNAT and DNAT - IPv6]) ++AT_KEYWORDS([ovnnat]) ++ ++CHECK_CONNTRACK() ++CHECK_CONNTRACK_NAT() ++ovn_start ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_BR([br-int]) ++ ++# Set external-ids in br-int needed for ovn-controller ++ovs-vsctl \ ++ -- set Open_vSwitch . external-ids:system-id=hv1 \ ++ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ ++ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ ++ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ ++ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true ++ ++# Start ovn-controller ++start_daemon ovn-controller ++ ++# Logical network: ++# Two LRs - R1 and R2 that are connected to each other via LS "join" ++# in fd00::/64 network. R1 has switchess foo (fd11::/64) and ++# bar (fd12::/64) connected to it. R2 has alice (fd21::/64) connected ++# to it. R2 is a gateway router on which we add NAT rules. ++# ++# foo -- R1 -- join - R2 -- alice ++# | ++# bar ---- ++ ++ovn-nbctl create Logical_Router name=R1 ++ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 ++ ++ovn-nbctl ls-add foo ++ovn-nbctl ls-add bar ++ovn-nbctl ls-add alice ++ovn-nbctl ls-add join ++ ++# Connect foo to R1 ++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64 ++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ ++ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" ++ ++# Connect bar to R1 ++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64 ++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ ++ type=router options:router-port=bar addresses=\"00:00:01:01:02:04\" ++ ++# Connect alice to R2 ++ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 fd21::1/64 ++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ ++ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" ++ ++# Connect R1 to join ++ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 fd00::1/64 ++ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ ++ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' ++ ++# Connect R2 to join ++ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 fd00::2/64 ++ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ ++ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' ++ ++# Static routes. ++ovn-nbctl lr-route-add R1 fd21::/64 fd00::2 ++ovn-nbctl lr-route-add R2 fd11::/64 fd00::1 ++ovn-nbctl lr-route-add R2 fd12::/64 fd00::1 ++ ++# Logical port 'foo1' in switch 'foo'. ++ADD_NAMESPACES(foo1) ++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \ ++ "fd11::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add foo foo1 \ ++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2" ++ ++# Logical port 'alice1' in switch 'alice'. ++ADD_NAMESPACES(alice1) ++ADD_VETH(alice1, alice1, br-int, "fd21::2/64", "f0:00:00:01:02:04", \ ++ "fd21::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd21::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add alice alice1 \ ++-- lsp-set-addresses alice1 "f0:00:00:01:02:04 fd21::2" ++ ++# Logical port 'bar1' in switch 'bar'. ++ADD_NAMESPACES(bar1) ++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:05", \ ++ "fd12::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add bar bar1 \ ++-- lsp-set-addresses bar1 "f0:00:00:01:02:05 fd12::2" ++ ++# Add a DNAT rule. ++ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=\"fd11::2\" \ ++ external_ip=\"fd30::2\" -- add logical_router R2 nat @nat ++ ++# Add a SNAT rule ++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=\"fd12::2\" \ ++ external_ip=\"fd30::1\" -- add logical_router R2 nat @nat ++ ++# wait for ovn-controller to catch up. ++ovn-nbctl --wait=hv sync ++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd30::1)']) ++ ++# 'alice1' should be able to ping 'foo1' directly. ++NS_CHECK_EXEC([alice1], [ping -6 -v -q -c 3 -i 0.3 -w 2 fd11::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# North-South DNAT: 'alice1' should also be able to ping 'foo1' via fd30::2 ++NS_CHECK_EXEC([alice1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# Check conntrack entries. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd21::2,dst=fd30::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=,type=129,code=0),zone= ++]) ++ ++# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic ++# from fd30::1 ++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd21::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that SNAT indeed happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd12::2,dst=fd21::2,id=,type=128,code=0),reply=(src=fd21::2,dst=fd30::1,id=,type=129,code=0),zone= ++]) ++ ++# Add static routes to handle east-west NAT. ++ovn-nbctl lr-route-add R1 fd30::/64 fd00::2 ++ ++# wait for ovn-controller to catch up. ++ovn-nbctl --wait=hv sync ++ ++# Flush conntrack entries for easier output parsing of next test. ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++ ++# East-west DNAT and SNAT: 'bar1' pings fd30::2. 'foo1' receives it. ++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# As we have a static route that sends all packets with destination ++# fd30::2 to R2, it hits the DNAT rule and converts fd30::2 to fd11::2 ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd12::2,dst=fd30::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd12::2,id=,type=129,code=0),zone= ++]) ++ ++# As we have a SNAT rule that converts fd12::2 to fd30::1, the source is ++# SNATted and 'foo1' receives it. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd12::2,dst=fd11::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd30::1,id=,type=129,code=0),zone= ++]) ++ ++OVS_APP_EXIT_AND_WAIT([ovn-controller]) ++ ++as ovn-sb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as ovn-nb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as northd ++OVS_APP_EXIT_AND_WAIT([ovn-northd]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++AT_CLEANUP ++ + AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, easy SNAT]) + AT_KEYWORDS([ovnnat]) + +@@ -286,6 +466,118 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d + /connection dropped.*/d"]) + AT_CLEANUP + ++AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, easy SNAT - IPv6]) ++AT_KEYWORDS([ovnnat]) ++ ++CHECK_CONNTRACK() ++CHECK_CONNTRACK_NAT() ++ovn_start ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_BR([br-int]) ++ ++# Set external-ids in br-int needed for ovn-controller ++ovs-vsctl \ ++ -- set Open_vSwitch . external-ids:system-id=hv1 \ ++ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ ++ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ ++ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ ++ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true ++ ++# Start ovn-controller ++start_daemon ovn-controller ++ ++# Logical network: ++# Two LRs - R1 and R2 that are connected to each other via LS "join" ++# in fd20::/64 network. R1 has switchess foo (fd10::/64) connected ++# to it. R2 has alice (fd30::/64) connected to it. ++# R2 is a gateway router on which we add NAT rules. ++# ++# foo -- R1 -- join - R2 -- alice ++ ++ovn-nbctl lr-add R1 ++ovn-nbctl lr-add R2 -- set Logical_Router R2 options:chassis=hv1 ++ ++ovn-nbctl ls-add foo ++ovn-nbctl ls-add alice ++ovn-nbctl ls-add join ++ ++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd10::1/64 ++ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 fd30::1/64 ++ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 fd20::1/64 ++ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 fd20::2/64 ++ ++# Connect foo to R1 ++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ ++ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" ++ ++# Connect alice to R2 ++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ ++ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" ++ ++# Connect R1 to join ++ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ ++ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' ++ ++# Connect R2 to join ++ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ ++ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' ++ ++# Static routes. ++ovn-nbctl lr-route-add R1 fd30::/64 fd20::2 ++ovn-nbctl lr-route-add R2 fd10::/64 fd20::1 ++ ++# Logical port 'foo1' in switch 'foo'. ++ADD_NAMESPACES(foo1) ++ADD_VETH(foo1, foo1, br-int, "fd10::2/64", "f0:00:00:01:02:03", \ ++ "fd10::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd10::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add foo foo1 \ ++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd10::2" ++ ++# Logical port 'alice1' in switch 'alice'. ++ADD_NAMESPACES(alice1) ++ADD_VETH(alice1, alice1, br-int, "fd30::2/64", "f0:00:00:01:02:04", \ ++ "fd30::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd30::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add alice alice1 \ ++-- lsp-set-addresses alice1 "f0:00:00:01:02:04 fd30::2" ++ ++# Add a SNAT rule ++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=\"fd10::2\" \ ++ external_ip=\"fd30::1\" -- add logical_router R2 nat @nat ++ ++ovn-nbctl --wait=hv sync ++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd30::1)']) ++ ++# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic ++# from fd30::1 ++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd30::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that SNAT indeed happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd10::2,dst=fd30::2,id=,type=128,code=0),reply=(src=fd30::2,dst=fd30::1,id=,type=129,code=0),zone= ++]) ++ ++OVS_APP_EXIT_AND_WAIT([ovn-controller]) ++ ++as ovn-sb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as ovn-nb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as northd ++OVS_APP_EXIT_AND_WAIT([ovn-northd]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++AT_CLEANUP ++ + AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT]) + AT_KEYWORDS([ovnnat]) + +@@ -485,9 +777,237 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \ + ]) + + # We verify that SNAT indeed happened via 'dump-conntrack' command. +-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=,type=0,code=0),zone= ++]) ++ ++OVS_APP_EXIT_AND_WAIT([ovn-controller]) ++ ++as ovn-sb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as ovn-nb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as northd ++OVS_APP_EXIT_AND_WAIT([ovn-northd]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++AT_CLEANUP ++ ++AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT - IPv6]) ++AT_KEYWORDS([ovnnat]) ++ ++CHECK_CONNTRACK() ++CHECK_CONNTRACK_NAT() ++ovn_start ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_BR([br-int]) ++ ++# Set external-ids in br-int needed for ovn-controller ++ovs-vsctl \ ++ -- set Open_vSwitch . external-ids:system-id=hv1 \ ++ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ ++ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ ++ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ ++ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true ++ ++# Start ovn-controller ++start_daemon ovn-controller ++ ++# Logical network: ++# Three LRs - R1, R2 and R3 that are connected to each other via LS "join" ++# in fd20::/64 network. R1 has switchess foo (fd11::/64) and ++# bar (fd12::/64) connected to it. R2 has alice (fd30::/64) connected ++# to it. R3 has bob (fd30::/64) connected to it. Note how both alice and ++# bob have the same subnet behind it. We are trying to simulate external ++# network via those 2 switches. In real world the switch ports of these ++# switches will have addresses set as "unknown" to make them learning switches. ++# Or those switches will be "localnet" ones. ++# ++# foo -- R1 -- join - R2 -- alice ++# | | ++# bar ---- - R3 --- bob ++ ++ovn-nbctl create Logical_Router name=R1 ++ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 ++ovn-nbctl create Logical_Router name=R3 options:chassis=hv1 ++ ++ovn-nbctl ls-add foo ++ovn-nbctl ls-add bar ++ovn-nbctl ls-add alice ++ovn-nbctl ls-add bob ++ovn-nbctl ls-add join ++ ++# Connect foo to R1 ++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64 ++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ ++ type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" ++ ++# Connect bar to R1 ++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64 ++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ ++ type=router options:router-port=bar addresses=\"00:00:01:01:02:04\" ++ ++# Connect alice to R2 ++ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 fd30::1/64 ++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ ++ type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" ++ ++# Connect bob to R3 ++ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 fd30::2/64 ++ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \ ++ type=router options:router-port=bob addresses=\"00:00:03:01:02:03\" ++ ++# Connect R1 to join ++ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 fd20::1/64 ++ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ ++ type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' ++ ++# Connect R2 to join ++ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 fd20::2/64 ++ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ ++ type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' ++ ++# Connect R3 to join ++ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 fd20::3/64 ++ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \ ++ type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"' ++ ++# Install static routes with source ip address as the policy for routing. ++# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3. ++ovn-nbctl --policy="src-ip" lr-route-add R1 fd11::/64 fd20::2 ++ovn-nbctl --policy="src-ip" lr-route-add R1 fd12::/64 fd20::3 ++ ++# Static routes. ++ovn-nbctl lr-route-add R2 fd11::/64 fd20::1 ++ovn-nbctl lr-route-add R2 fd12::/64 fd20::1 ++ovn-nbctl lr-route-add R3 fd11::/64 fd20::1 ++ovn-nbctl lr-route-add R3 fd12::/64 fd20::1 ++ ++# For gateway routers R2 and R3, set a force SNAT rule. ++ovn-nbctl set logical_router R2 options:dnat_force_snat_ip=fd20::2 ++ovn-nbctl set logical_router R3 options:dnat_force_snat_ip=fd20::3 ++ ++# Logical port 'foo1' in switch 'foo'. ++ADD_NAMESPACES(foo1) ++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \ ++ "fd11::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add foo foo1 \ ++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2" ++ ++# Logical port 'alice1' in switch 'alice'. ++ADD_NAMESPACES(alice1) ++ADD_VETH(alice1, alice1, br-int, "fd30::3/64", "f0:00:00:01:02:04", \ ++ "fd30::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd30::3 | grep tentative)" = ""]) ++ovn-nbctl lsp-add alice alice1 \ ++-- lsp-set-addresses alice1 "f0:00:00:01:02:04 fd30::3" ++ ++# Logical port 'bar1' in switch 'bar'. ++ADD_NAMESPACES(bar1) ++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:05", \ ++ "fd12::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add bar bar1 \ ++-- lsp-set-addresses bar1 "f0:00:00:01:02:05 fd12::2" ++ ++# Logical port 'bob1' in switch 'bob'. ++ADD_NAMESPACES(bob1) ++ADD_VETH(bob1, bob1, br-int, "fd30::4/64", "f0:00:00:01:02:06", \ ++ "fd30::2") ++OVS_WAIT_UNTIL([test "$(ip netns exec bob1 ip a | grep fd30::4 | grep tentative)" = ""]) ++ovn-nbctl lsp-add bob bob1 \ ++-- lsp-set-addresses bob1 "f0:00:00:01:02:06 fd30::4" ++ ++# External IPs -- 30.0.0.N --> fd40::N (from IPv4 version of test case) ++ ++# Router R2 ++# Add a DNAT rule. ++ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \ ++ external_ip='"fd40::2"' -- add logical_router R2 nat @nat ++ ++# Add a SNAT rule ++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd11::2"' \ ++ external_ip='"fd40::1"' -- add logical_router R2 nat @nat ++ ++# Router R3 ++# Add a DNAT rule. ++ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \ ++ external_ip='"fd40::3"' -- add logical_router R3 nat @nat ++ ++# Add a SNAT rule ++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd12::2"' \ ++ external_ip='"fd40::4"' -- add logical_router R3 nat @nat ++ ++# wait for ovn-controller to catch up. ++ovn-nbctl --wait=hv sync ++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd40::4)']) ++ ++# North-South DNAT: 'alice1' should be able to ping 'foo1' via fd30::2 ++NS_CHECK_EXEC([alice1], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# Check conntrack entries. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::3) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd30::3,dst=fd40::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd30::3,id=,type=129,code=0),zone= ++]) ++ ++# But foo1 should receive traffic from fd20::2 ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd30::3,dst=fd11::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd20::2,id=,type=129,code=0),zone= ++]) ++ ++# North-South DNAT: 'bob1' should be able to ping 'foo1' via fd40::3 ++NS_CHECK_EXEC([bob1], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::3 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# Check conntrack entries. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::4) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd30::4,dst=fd40::3,id=,type=128,code=0),reply=(src=fd11::2,dst=fd30::4,id=,type=129,code=0),zone= ++]) ++ ++# But foo1 should receive traffic from fd20::3 ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd30::4,dst=fd11::2,id=,type=128,code=0),reply=(src=fd11::2,dst=fd20::3,id=,type=129,code=0),zone= ++]) ++ ++# South-North SNAT: 'bar1' pings 'bob1'. But 'bob1' receives traffic ++# from fd40::4 ++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::4 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that SNAT indeed happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::4) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd12::2,dst=fd30::4,id=,type=128,code=0),reply=(src=fd30::4,dst=fd40::4,id=,type=129,code=0),zone= ++]) ++ ++# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic ++# from fd40::1 ++NS_CHECK_EXEC([foo1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::3 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that SNAT indeed happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::1) | \ + sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl +-icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=,type=0,code=0),zone= ++icmpv6,orig=(src=fd11::2,dst=fd30::3,id=,type=128,code=0),reply=(src=fd30::3,dst=fd40::1,id=,type=129,code=0),zone= + ]) + + OVS_APP_EXIT_AND_WAIT([ovn-controller]) +@@ -1370,6 +1890,162 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d + /connection dropped.*/d"]) + AT_CLEANUP + ++AT_SETUP([ovn -- DNAT and SNAT on distributed router - N/S - IPv6]) ++AT_KEYWORDS([ovnnat]) ++ ++CHECK_CONNTRACK() ++CHECK_CONNTRACK_NAT() ++ovn_start ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_BR([br-int]) ++ ++# Set external-ids in br-int needed for ovn-controller ++ovs-vsctl \ ++ -- set Open_vSwitch . external-ids:system-id=hv1 \ ++ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ ++ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ ++ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ ++ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true ++ ++# Start ovn-controller ++start_daemon ovn-controller ++ ++# Logical network: ++# One LR R1 with switches foo (fd11::/64), bar (fd12::/64), ++# and alice (fd20::/64) connected to it. The port between R1 and ++# alice is the router gateway port where the R1 NAT rules are applied. ++# ++# foo -- R1 -- alice ++# | ++# bar ---- ++ ++ovn-nbctl lr-add R1 ++ ++ovn-nbctl ls-add foo ++ovn-nbctl ls-add bar ++ovn-nbctl ls-add alice ++ ++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64 ++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64 ++ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 fd20::1/64 \ ++ -- set Logical_Router_Port alice options:redirect-chassis=hv1 ++ ++# Connect foo to R1 ++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ ++ type=router options:router-port=foo \ ++ -- lsp-set-addresses rp-foo router ++ ++# Connect bar to R1 ++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ ++ type=router options:router-port=bar \ ++ -- lsp-set-addresses rp-bar router ++ ++# Connect alice to R1 ++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ ++ type=router options:router-port=alice \ ++ -- lsp-set-addresses rp-alice router ++ ++# Logical port 'foo1' in switch 'foo'. ++ADD_NAMESPACES(foo1) ++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \ ++ "fd11::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add foo foo1 \ ++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2" ++ ++# Logical port 'foo2' in switch 'foo'. ++ADD_NAMESPACES(foo2) ++ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \ ++ "fd11::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec foo2 ip a | grep fd11::3 | grep tentative)" = ""]) ++ovn-nbctl lsp-add foo foo2 \ ++-- lsp-set-addresses foo2 "f0:00:00:01:02:06 fd11::3" ++ ++# Logical port 'bar1' in switch 'bar'. ++ADD_NAMESPACES(bar1) ++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \ ++ "fd12::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add bar bar1 \ ++-- lsp-set-addresses bar1 "f0:00:00:01:02:04 fd12::2" ++ ++# Logical port 'alice1' in switch 'alice'. ++ADD_NAMESPACES(alice1) ++ADD_VETH(alice1, alice1, br-int, "fd20::2/64", "f0:00:00:01:02:05", \ ++ "fd20::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd20::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add alice alice1 \ ++-- lsp-set-addresses alice1 "f0:00:00:01:02:05 fd20::2" ++ ++ovn-nbctl --wait=hv sync ++ ++# Add DNAT rules ++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02:03:04]) ++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd11::3 foo2 00:00:02:02:03:05]) ++ ++# Add a SNAT rule ++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64]) ++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd12::/64]) ++ ++ovn-nbctl --wait=hv sync ++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)']) ++ ++# North-South DNAT: 'alice1' pings 'foo1' using fd20::3 ++NS_CHECK_EXEC([alice1], [ping -6 -q -c 3 -i 0.3 -w 2 fd20::3 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that DNAT indeed happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd20::2,dst=fd20::3,id=,type=128,code=0),reply=(src=fd11::2,dst=fd20::2,id=,type=129,code=0),zone= ++]) ++ ++# South-North SNAT: 'foo2' pings 'alice1'. But 'alice1' receives traffic ++# from 172.16.1.4 ++NS_CHECK_EXEC([foo2], [ping -6 -q -c 3 -i 0.3 -w 2 fd20::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that SNAT indeed happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd11::3,dst=fd20::2,id=,type=128,code=0),reply=(src=fd20::2,dst=fd20::1,id=,type=129,code=0),zone= ++]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++ ++# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic ++# from fd20::1 ++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd20::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that SNAT indeed happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd12::2,dst=fd20::2,id=,type=128,code=0),reply=(src=fd20::2,dst=fd20::1,id=,type=129,code=0),zone= ++]) ++ ++OVS_APP_EXIT_AND_WAIT([ovn-controller]) ++ ++as ovn-sb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as ovn-nb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as northd ++OVS_APP_EXIT_AND_WAIT([ovn-northd]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++AT_CLEANUP ++ + AT_SETUP([ovn -- DNAT and SNAT on distributed router - E/W]) + AT_KEYWORDS([ovnnat]) + +@@ -1547,6 +2223,188 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d + /connection dropped.*/d"]) + AT_CLEANUP + ++AT_SETUP([ovn -- DNAT and SNAT on distributed router - E/W - IPv6]) ++AT_KEYWORDS([ovnnat]) ++ ++CHECK_CONNTRACK() ++CHECK_CONNTRACK_NAT() ++ovn_start ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_BR([br-int]) ++ ++# Set external-ids in br-int needed for ovn-controller ++ovs-vsctl \ ++ -- set Open_vSwitch . external-ids:system-id=hv1 \ ++ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ ++ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ ++ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ ++ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true ++ ++# Start ovn-controller ++start_daemon ovn-controller ++ ++# Logical network: ++# One LR R1 with switches foo (fd11::/64), bar (fd12::/64), ++# and alice (fd20::/64) connected to it. The port between R1 and ++# alice is the router gateway port where the R1 NAT rules are applied. ++# ++# foo -- R1 -- alice ++# | ++# bar ---- ++ ++ovn-nbctl lr-add R1 ++ ++ovn-nbctl ls-add foo ++ovn-nbctl ls-add bar ++ovn-nbctl ls-add alice ++ ++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64 ++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64 ++ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 fd20::1/64 \ ++ -- set Logical_Router_Port alice options:redirect-chassis=hv1 ++ ++# Connect foo to R1 ++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ ++ type=router options:router-port=foo \ ++ -- lsp-set-addresses rp-foo router ++ ++# Connect bar to R1 ++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ ++ type=router options:router-port=bar \ ++ -- lsp-set-addresses rp-bar router ++ ++# Connect alice to R1 ++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ ++ type=router options:router-port=alice \ ++ -- lsp-set-addresses rp-alice router ++ ++# Logical port 'foo1' in switch 'foo'. ++ADD_NAMESPACES(foo1) ++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \ ++ "fd11::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add foo foo1 \ ++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2" ++ ++# Logical port 'foo2' in switch 'foo'. ++ADD_NAMESPACES(foo2) ++ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \ ++ "fd11::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec foo2 ip a | grep fd11::3 | grep tentative)" = ""]) ++ovn-nbctl lsp-add foo foo2 \ ++-- lsp-set-addresses foo2 "f0:00:00:01:02:06 fd11::3" ++ ++# Logical port 'bar1' in switch 'bar'. ++ADD_NAMESPACES(bar1) ++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \ ++ "fd12::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add bar bar1 \ ++-- lsp-set-addresses bar1 "f0:00:00:01:02:04 fd12::2" ++ ++# Logical port 'alice1' in switch 'alice'. ++ADD_NAMESPACES(alice1) ++ADD_VETH(alice1, alice1, br-int, "fd20::2/64", "f0:00:00:01:02:05", \ ++ "fd20::1") ++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd20::2 | grep tentative)" = ""]) ++ovn-nbctl lsp-add alice alice1 \ ++-- lsp-set-addresses alice1 "f0:00:00:01:02:05 fd20::2" ++ ++# Add DNAT rules ++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02:03:04]) ++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd12::2 bar1 00:00:02:02:03:05]) ++ ++# Add a SNAT rule ++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64]) ++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd12::/64]) ++ ++ovn-nbctl --wait=hv sync ++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)']) ++ ++echo "------ hv dump ------" ++ovs-ofctl show br-int ++ovs-ofctl dump-flows br-int ++echo "---------------------" ++ ++# East-West No NAT: 'foo1' pings 'bar1' using fd12::2. ++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that no NAT happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd12::2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/' | wc -l], [0], [0 ++]) ++ ++# East-West No NAT: 'foo2' pings 'bar1' using fd12::2. ++NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that no NAT happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd12::2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/' | wc -l], [0], [0 ++]) ++ ++# East-West No NAT: 'bar1' pings 'foo2' using fd11::3. ++NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 fd11::3 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# We verify that no NAT happened via 'dump-conntrack' command. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd12::2) | \ ++sed -e 's/zone=[[0-9]]*/zone=/' | wc -l], [0], [0 ++]) ++ ++# East-West NAT: 'foo1' pings 'bar1' using fd20::4. ++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# Check conntrack entries. First SNAT of 'foo1' address happens. ++# Then DNAT of 'bar1' address happens (listed first below). ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd11::2,dst=fd20::4,id=,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=,type=129,code=0),zone= ++icmpv6,orig=(src=fd20::1,dst=fd20::4,id=,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=,type=129,code=0),zone= ++]) ++ ++AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++ ++# East-West NAT: 'foo2' pings 'bar1' using fd20::4. ++NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], \ ++[0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ ++# Check conntrack entries. First SNAT of 'foo2' address happens. ++# Then DNAT of 'bar1' address happens (listed first below). ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++icmpv6,orig=(src=fd11::3,dst=fd20::4,id=,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=,type=129,code=0),zone= ++icmpv6,orig=(src=fd20::1,dst=fd20::4,id=,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=,type=129,code=0),zone= ++]) ++ ++OVS_APP_EXIT_AND_WAIT([ovn-controller]) ++ ++as ovn-sb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as ovn-nb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as northd ++OVS_APP_EXIT_AND_WAIT([ovn-northd]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++AT_CLEANUP ++ + AT_SETUP([ovn -- 2 LSs IGMP]) + AT_KEYWORDS([ovnigmp]) + +-- +2.23.0 + diff --git a/SOURCES/0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch b/SOURCES/0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch new file mode 100644 index 0000000..b31b73d --- /dev/null +++ b/SOURCES/0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch @@ -0,0 +1,232 @@ +From d8573c28c74a4e706d1b16dc84ac28a74fe624f0 Mon Sep 17 00:00:00 2001 +From: Ankur Sharma +Date: Fri, 1 Nov 2019 01:27:36 +0000 +Subject: [PATCH 06/12] OVN: ADD nbctl cli to mark a dnat_and_snat rule as + stateless + +Adding ovn-nbctl to mark a dnat_and_snat rule as stateless. +This configuration will be added to "options" column of NAT table. + +Signed-off-by: Ankur Sharma +Signed-off-by: Numan Siddique +--- + ovn/ovn-nb.ovsschema | 6 ++++-- + ovn/ovn-nb.xml | 5 +++++ + ovn/utilities/ovn-nbctl.8.xml | 12 +++++++++++- + ovn/utilities/ovn-nbctl.c | 30 +++++++++++++++++++++++++++- + tests/ovn-nbctl.at | 37 +++++++++++++++++++++++++++++++++++ + 5 files changed, 86 insertions(+), 4 deletions(-) + +diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema +index 2c87cbba7..084305b24 100644 +--- a/ovn/ovn-nb.ovsschema ++++ b/ovn/ovn-nb.ovsschema +@@ -1,7 +1,7 @@ + { + "name": "OVN_Northbound", +- "version": "5.16.0", +- "cksum": "923459061 23095", ++ "version": "5.17.0", ++ "cksum": "1128988054 23237", + "tables": { + "NB_Global": { + "columns": { +@@ -345,6 +345,8 @@ + "snat", + "dnat_and_snat" + ]]}}}, ++ "options": {"type": {"key": "string", "value": "string", ++ "min": 0, "max": "unlimited"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, +diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml +index bce7463d2..cf2c5136a 100644 +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -2324,6 +2324,11 @@ +

        + + ++ ++ Indicates if a dnat_and_snat rule should lead to connection ++ tracking state or not. ++ ++ + + + See External IDs at the beginning of this document. +diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml +index 41d50b694..49aa527a0 100644 +--- a/ovn/utilities/ovn-nbctl.8.xml ++++ b/ovn/utilities/ovn-nbctl.8.xml +@@ -665,7 +665,7 @@ +

        NAT Commands

        + +
        +-
        [--may-exist] lr-nat-add router type external_ip logical_ip [logical_port external_mac]
        ++
        [--may-exist] [--stateless]lr-nat-add router type external_ip logical_ip [logical_port external_mac]
        +
        +

        + Adds the specified NAT to router. +@@ -681,7 +681,17 @@ + The logical_port is the name of an existing logical + switch port where the logical_ip resides. + The external_mac is an Ethernet address. ++ The --stateless +

        ++

        ++ When --stateless is specified then it implies that ++ we will be not use connection tracker, i.e internal ip and external ++ ip are 1:1 mapped. This implies that --stateless is ++ applicable only to dnat_and_snat type NAT rules. ++ An external ip with --stateless NAT cannot be shared ++ with any other NAT rule. ++

        ++ +

        + When type is dnat, the externally + visible IP address external_ip is DNATted to the +diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c +index 112cc1d54..1a1d83e65 100644 +--- a/ovn/utilities/ovn-nbctl.c ++++ b/ovn/utilities/ovn-nbctl.c +@@ -685,6 +685,7 @@ Policy commands:\n\ + lr-policy-list ROUTER print policies for ROUTER\n\ + \n\ + NAT commands:\n\ ++ [--stateless]\n\ + lr-nat-add ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]\n\ + add a NAT to ROUTER\n\ + lr-nat-del ROUTER [TYPE [IP]]\n\ +@@ -3945,6 +3946,13 @@ nbctl_lr_nat_add(struct ctl_context *ctx) + } + + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; ++ bool stateless = shash_find(&ctx->options, "--stateless") != NULL; ++ ++ if (strcmp(nat_type, "dnat_and_snat") && stateless) { ++ ctl_error(ctx, "stateless is not applicable to dnat or snat types"); ++ return; ++ } ++ + int is_snat = !strcmp("snat", nat_type); + for (size_t i = 0; i < lr->n_nat; i++) { + const struct nbrec_nat *nat = lr->nat[i]; +@@ -3976,10 +3984,25 @@ nbctl_lr_nat_add(struct ctl_context *ctx) + return; + } + } ++ ++ } ++ if (!strcmp(nat_type, "dnat_and_snat") || ++ !strcmp(nat->type, "dnat_and_snat")) { ++ ++ if (!strcmp(nat->external_ip, external_ip)) { ++ struct smap nat_options = SMAP_INITIALIZER(&nat_options); ++ if (!strcmp(smap_get(&nat->options, "stateless"), ++ "true") || stateless) { ++ ctl_error(ctx, "%s, %s: External ip cannot be shared " ++ "across stateless and stateful NATs", ++ external_ip, new_logical_ip); ++ } ++ } + } + } + + /* Create the NAT. */ ++ struct smap nat_options = SMAP_INITIALIZER(&nat_options); + struct nbrec_nat *nat = nbrec_nat_insert(ctx->txn); + nbrec_nat_set_type(nat, nat_type); + nbrec_nat_set_external_ip(nat, external_ip); +@@ -3988,7 +4011,12 @@ nbctl_lr_nat_add(struct ctl_context *ctx) + nbrec_nat_set_logical_port(nat, logical_port); + nbrec_nat_set_external_mac(nat, external_mac); + } ++ ++ smap_add(&nat_options, "stateless", stateless ? "true":"false"); ++ nbrec_nat_set_options(nat, &nat_options); ++ + free(new_logical_ip); ++ smap_destroy(&nat_options); + + /* Insert the NAT into the logical router. */ + nbrec_logical_router_verify_nat(lr); +@@ -5707,7 +5735,7 @@ static const struct ctl_command_syntax nbctl_commands[] = { + /* NAT commands. */ + { "lr-nat-add", 4, 6, + "ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]", NULL, +- nbctl_lr_nat_add, NULL, "--may-exist", RW }, ++ nbctl_lr_nat_add, NULL, "--may-exist,--stateless", RW }, + { "lr-nat-del", 1, 3, "ROUTER [TYPE [IP]]", NULL, + nbctl_lr_nat_del, NULL, "--if-exists", RW }, + { "lr-nat-list", 1, 1, "ROUTER", NULL, nbctl_lr_nat_list, NULL, "", RO }, +diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at +index 620b778b7..d5dce90f4 100644 +--- a/tests/ovn-nbctl.at ++++ b/tests/ovn-nbctl.at +@@ -533,6 +533,39 @@ snat 30.0.0.1 192.168.1.0/24 + snat fd01::1 fd11::/64 + ]) + ++AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0], ++[0 ++]) ++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat 40.0.0.2 192.168.1.4]) ++AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0], ++[1 ++]) ++ ++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat fd21::1 fd11::2]) ++AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0], ++[2 ++]) ++ ++AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat fd21::1]) ++ ++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat 40.0.0.2 192.168.1.4], [1], [], ++[ovn-nbctl: stateless is not applicable to dnat or snat types ++]) ++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 snat 40.0.0.2 192.168.1.4], [1], [], ++[ovn-nbctl: stateless is not applicable to dnat or snat types ++]) ++AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 40.0.0.2 192.168.1.5], [1], [], ++[ovn-nbctl: 40.0.0.2, 192.168.1.5: External ip cannot be shared across stateless and stateful NATs ++]) ++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 40.0.0.2 192.168.1.5], [1], [], ++[ovn-nbctl: 40.0.0.2, 192.168.1.5: External ip cannot be shared across stateless and stateful NATs ++]) ++ ++AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 40.0.0.3 192.168.1.6]) ++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat 40.0.0.3 192.168.1.7], [1], [], ++[ovn-nbctl: 40.0.0.3, 192.168.1.7: External ip cannot be shared across stateless and stateful NATs ++]) ++ + dnl Deletes the NATs + AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.3], [1], [], + [ovn-nbctl: no matching NAT with the type (dnat_and_snat) and external_ip (30.0.0.3) +@@ -552,8 +585,10 @@ TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC L + dnat 30.0.0.1 192.168.1.2 + dnat fd01::1 fd11::2 + dnat_and_snat 30.0.0.2 192.168.1.3 ++dnat_and_snat 40.0.0.2 192.168.1.4 + dnat_and_snat fd01::2 fd11::3 + snat 30.0.0.1 192.168.1.0/24 ++snat 40.0.0.3 192.168.1.6 + snat fd01::1 fd11::/64 + ]) + +@@ -561,8 +596,10 @@ AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat]) + AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl + TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT + dnat_and_snat 30.0.0.2 192.168.1.3 ++dnat_and_snat 40.0.0.2 192.168.1.4 + dnat_and_snat fd01::2 fd11::3 + snat 30.0.0.1 192.168.1.0/24 ++snat 40.0.0.3 192.168.1.6 + snat fd01::1 fd11::/64 + ]) + +-- +2.23.0 + diff --git a/SOURCES/0006-controller-Downgrade-a-warning-log-message.patch b/SOURCES/0006-controller-Downgrade-a-warning-log-message.patch new file mode 100644 index 0000000..429a227 --- /dev/null +++ b/SOURCES/0006-controller-Downgrade-a-warning-log-message.patch @@ -0,0 +1,38 @@ +From 610bac28e0e241b3ea266fb7c3745415c54452f4 Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Fri, 25 Oct 2019 09:34:49 -0400 +Subject: [PATCH 6/6] controller: Downgrade a warning log message + +This log message was introduced in commit 5344f24ecb. It gets hit +under normal circumstances, so it would be better as a debug message +instead of a warning. I also expanded it to clarify that the next +step will be to create the chassis record. + +This was found by trying to run the system-ovn.at tests, and they +failed because of these unexpected warning log messages. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique +--- + ovn/controller/chassis.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c +index b74a42cc8..4cd340124 100644 +--- a/ovn/controller/chassis.c ++++ b/ovn/controller/chassis.c +@@ -484,8 +484,9 @@ chassis_get_record(struct ovsdb_idl_txn *ovnsb_idl_txn, + chassis_rec = chassis_lookup_by_name(sbrec_chassis_by_name, + chassis_info_id(&chassis_state)); + if (!chassis_rec) { +- VLOG_WARN("Could not find Chassis : stored (%s) ovs (%s)", +- chassis_info_id(&chassis_state), chassis_id); ++ VLOG_DBG("Could not find Chassis, will create it" ++ ": stored (%s) ovs (%s)", ++ chassis_info_id(&chassis_state), chassis_id); + if (ovnsb_idl_txn) { + /* Recreate the chassis record. */ + chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn); +-- +2.23.0 + diff --git a/SOURCES/0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch b/SOURCES/0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch new file mode 100644 index 0000000..c9e13f0 --- /dev/null +++ b/SOURCES/0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch @@ -0,0 +1,693 @@ +From 22b415470df9cafe1fec9f3095faf14172d1c4a6 Mon Sep 17 00:00:00 2001 +From: Ankur Sharma +Date: Fri, 1 Nov 2019 01:27:39 +0000 +Subject: [PATCH 07/12] OVN: Use ip4.src and ip4.dst actions for NAT rules + +For dnat_and_snat rules which are meant to be stateless +instead of using ct_snat/dnat OVN actions, we will use +ip4.src/ip4.dst. + +This actions will do 1:1 mapping to inner ip to external ip, +while recalculating the checksums. + +Signed-off-by: Ankur Sharma +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 33 +++- + ovn/northd/ovn-northd.c | 84 ++++++++-- + tests/ovn-northd.at | 95 +++++++++++ + tests/ovn.at | 311 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 508 insertions(+), 15 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 83c90d680..d1af97d15 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1840,7 +1840,10 @@ icmp6 { + B, a priority-90 flow matches + ip && ip4.dst == B or + ip && ip6.dst == B +- with an action ct_snat; . ++ with an action ct_snat; . If the NAT rule is of type ++ dnat_and_snat and has stateless=true in the ++ options, then the action would be ip4/6.dst= ++ (B). +

        + +

        +@@ -1862,7 +1865,10 @@ icmp6 { + ip && + ip6.dst == B && inport == GW + where GW is the logical router gateway port, with an +- action ct_snat;. ++ action ct_snat;. If the NAT rule is of type ++ dnat_and_snat and has stateless=true in the ++ options, then the action would be ip4/6.dst= ++ (B). +

        + +

        +@@ -1990,7 +1996,10 @@ icmp6 { + Gateway router is configured to force SNAT any DNATed packet, + the above action will be replaced by + flags.force_snat_for_dnat = 1; flags.loopback = 1; +- ct_dnat(B);. ++ ct_dnat(B);. If the NAT rule is of type ++ dnat_and_snat and has stateless=true in the ++ options, then the action would be ip4/6.dst= ++ (B). + + +

      • +@@ -2024,6 +2033,9 @@ icmp6 { + where GW is the logical router gateway port, with an + action ct_dnat(B);. The match will + include ip6.dst == B in the IPv6 case. ++ If the NAT rule is of type dnat_and_snat and has ++ stateless=true in the options, then the action ++ would be ip4/6.dst=(B). +

        + +

        +@@ -2698,7 +2710,10 @@ nd_ns { + matches ip && ip4.src == B + && outport == GW, where GW + is the logical router gateway port, with an action +- ct_dnat;. ++ ct_dnat;. If the NAT rule is of type ++ dnat_and_snat and has stateless=true in the ++ options, then the action would be ip4/6.src= ++ (B). +

        + +

        +@@ -2765,7 +2780,10 @@ nd_ns { + ip && ip4.src == A with an action + ct_snat(B);. The priority of the flow + is calculated based on the mask of A, with matches +- having larger masks getting higher priorities. ++ having larger masks getting higher priorities. If the NAT rule is ++ of type dnat_and_snat and has stateless=true in the ++ options, then the action would be ip4/6.src= ++ (B). +

        +

        + A priority-0 logical flow with match 1 has actions +@@ -2788,7 +2806,10 @@ nd_ns { + logical router gateway port, with an action + ct_snat(B);. The priority of the flow + is calculated based on the mask of A, with matches +- having larger masks getting higher priorities. ++ having larger masks getting higher priorities. If the NAT rule ++ is of type dnat_and_snat and has stateless=true ++ in the options, then the action would be ip4/6.src= ++ (B). +

        + +

        +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 27e90fcb2..8938e458f 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6779,6 +6779,18 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode) + smap_destroy(&options); + } + ++static inline bool ++lrouter_nat_is_stateless(const struct nbrec_nat *nat) ++{ ++ const char *stateless = smap_get(&nat->options, "stateless"); ++ ++ if (stateless && !strcmp(stateless, "true")) { ++ return true; ++ } ++ ++ return false; ++} ++ + static void + build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + struct hmap *lflows, struct shash *meter_groups) +@@ -7636,6 +7648,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ovs_be32 ip, mask; + struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT; + bool is_v6 = false; ++ bool stateless = lrouter_nat_is_stateless(nat); + + char *error = ip_parse_masked(nat->external_ip, &ip, &mask); + if (error || mask != OVS_BE32_MAX) { +@@ -7719,16 +7732,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + if (!od->l3dgw_port) { + /* Gateway router. */ + ds_clear(&match); ++ ds_clear(&actions); + ds_put_format(&match, "ip && ip%s.dst == %s", + is_v6 ? "6" : "4", + nat->external_ip); ++ if (!strcmp(nat->type, "dnat_and_snat") && stateless) { ++ ds_put_format(&actions, "ip%s.dst=%s; next;", ++ is_v6 ? "6" : "4", nat->logical_ip); ++ } else { ++ ds_put_cstr(&actions, "ct_snat;"); ++ } ++ + ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90, +- ds_cstr(&match), "ct_snat;"); ++ ds_cstr(&match), ds_cstr(&actions)); + } else { + /* Distributed router. */ + + /* Traffic received on l3dgw_port is subject to NAT. */ + ds_clear(&match); ++ ds_clear(&actions); + ds_put_format(&match, "ip && ip%s.dst == %s" + " && inport == %s", + is_v6 ? "6" : "4", +@@ -7740,8 +7762,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_put_format(&match, " && is_chassis_resident(%s)", + od->l3redirect_port->json_key); + } ++ ++ if (!strcmp(nat->type, "dnat_and_snat") && stateless) { ++ ds_put_format(&actions, "ip%s.dst=%s; next;", ++ is_v6 ? "6" : "4", nat->logical_ip); ++ } else { ++ ds_put_cstr(&actions, "ct_snat;"); ++ } ++ + ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100, +- ds_cstr(&match), "ct_snat;"); ++ ds_cstr(&match), ds_cstr(&actions)); + + /* Traffic received on other router ports must be + * redirected to the central instance of the l3dgw_port +@@ -7778,8 +7808,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_put_format(&actions, + "flags.force_snat_for_dnat = 1; "); + } +- ds_put_format(&actions, "flags.loopback = 1; ct_dnat(%s);", +- nat->logical_ip); ++ ++ if (!strcmp(nat->type, "dnat_and_snat") && stateless) { ++ ds_put_format(&actions, "flags.loopback = 1; " ++ "ip%s.dst=%s; next;", ++ is_v6 ? "6" : "4", nat->logical_ip); ++ } else { ++ ds_put_format(&actions, "flags.loopback = 1; " ++ "ct_dnat(%s);", nat->logical_ip); ++ } ++ + ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100, + ds_cstr(&match), ds_cstr(&actions)); + } else { +@@ -7799,8 +7837,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + od->l3redirect_port->json_key); + } + ds_clear(&actions); +- ds_put_format(&actions, "ct_dnat(%s);", +- nat->logical_ip); ++ ++ if (!strcmp(nat->type, "dnat_and_snat") && stateless) { ++ ds_put_format(&actions, "ip%s.dst=%s; next;", ++ is_v6 ? "6" : "4", nat->logical_ip); ++ } else { ++ ds_put_format(&actions, "ct_dnat(%s);", ++ nat->logical_ip); ++ } ++ + ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100, + ds_cstr(&match), ds_cstr(&actions)); + +@@ -7844,7 +7889,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ", + ETH_ADDR_ARGS(mac)); + } +- ds_put_format(&actions, "ct_dnat;"); ++ ++ if (!strcmp(nat->type, "dnat_and_snat") && stateless) { ++ ds_put_format(&actions, "ip%s.src=%s; next;", ++ is_v6 ? "6" : "4", nat->external_ip); ++ } else { ++ ds_put_format(&actions, "ct_dnat;"); ++ } ++ + ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100, + ds_cstr(&match), ds_cstr(&actions)); + } +@@ -7861,7 +7913,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + is_v6 ? "6" : "4", + nat->logical_ip); + ds_clear(&actions); +- ds_put_format(&actions, "ct_snat(%s);", nat->external_ip); ++ ++ if (!strcmp(nat->type, "dnat_and_snat") && stateless) { ++ ds_put_format(&actions, "ip%s.src=%s; next;", ++ is_v6 ? "6" : "4", nat->external_ip); ++ } else { ++ ds_put_format(&actions, "ct_snat(%s);", ++ nat->external_ip); ++ } + + /* The priority here is calculated such that the + * nat->logical_ip with the longest mask gets a higher +@@ -7891,7 +7950,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, + ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ", + ETH_ADDR_ARGS(mac)); + } +- ds_put_format(&actions, "ct_snat(%s);", nat->external_ip); ++ ++ if (!strcmp(nat->type, "dnat_and_snat") && stateless) { ++ ds_put_format(&actions, "ip%s.src=%s; next;", ++ is_v6 ? "6" : "4", nat->external_ip); ++ } else { ++ ds_put_format(&actions, "ct_snat(%s);", ++ nat->external_ip); ++ } + + /* The priority here is calculated such that the + * nat->logical_ip with the longest mask gets a higher +diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at +index 42033d589..989ed4f47 100644 +--- a/tests/ovn-northd.at ++++ b/tests/ovn-northd.at +@@ -966,3 +966,98 @@ OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0], + ]) + + AT_CLEANUP ++ ++AT_SETUP([ovn -- check stateless dnat_and_snat rule]) ++AT_SKIP_IF([test $HAVE_PYTHON = no]) ++ovn_start ++ ++ovn-sbctl chassis-add gw1 geneve 127.0.0.1 ++ ++ovn-nbctl lr-add R1 ++ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24 ++ ++ovn-nbctl ls-add S1 ++ovn-nbctl lsp-add S1 S1-R1 ++ovn-nbctl lsp-set-type S1-R1 router ++ovn-nbctl lsp-set-addresses S1-R1 router ++ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 ++ ++ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1 ++ ++uuid=`ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=cr-R1-S1` ++echo "CR-LRP UUID is: " $uuid ++ ++# IPV4 ++ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11 ++ ++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ ++wc -l`]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [2 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.dst=| wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [0 ++]) ++ ++ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1 ++ ++ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat 172.16.1.1 50.0.0.11 ++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ ++wc -l`]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.dst=| wc -l], [0], [2 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [2 ++]) ++ovn-nbctl lr-nat-del R1 dnat_and_snat 172.16.1.1 ++ ++# IPV6 ++ovn-nbctl lr-nat-add R1 dnat_and_snat fd01::1 fd11::2 ++ ++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ ++wc -l`]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [2 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.dst=| wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [0 ++]) ++ ++ovn-nbctl lr-nat-del R1 dnat_and_snat fd01::1 ++ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2 ++ ++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \ ++wc -l`]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.dst=| wc -l], [0], [2 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [2 ++]) ++ ++AT_CLEANUP +diff --git a/tests/ovn.at b/tests/ovn.at +index 951a55226..89bd3f174 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -16610,3 +16610,314 @@ as hv4 ovs-appctl fdb/show br-phys + OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) + + AT_CLEANUP ++ ++AT_SETUP([ovn -- Stateless Floating IP]) ++ovn_start ++ ++# In this test cases we create 3 switches, all connected to same ++# physical network (through br-phys on each HV). LS1 and LS2 have ++# 1 VIF each. Each HV has 1 VIF port. The first digit ++# of VIF port name indicates the hypervisor it is bound to, e.g. ++# lp23 means VIF 3 on hv2. ++# ++# All the switches are connected to a logical router "router". ++# ++# There is an external logical switch, ls-north. ++# This test validates the stateless floating ip implementation. ++# ++# Each switch's VLAN tag and their logical switch ports are: ++# - ls1: ++# - tagged with VLAN 101 ++# - ports: lp11 ++# - ls2: ++# - tagged with VLAN 201 ++# - ports: lp22 ++# - ls-north: ++# - tagged with VLAN 1000 ++# Note: a localnet port is created for each switch to connect to ++# physical network. ++ ++for i in 1 2; do ++ ls_name=ls$i ++ ovn-nbctl ls-add $ls_name ++ ln_port_name=ln$i ++ if test $i -eq 1; then ++ ovn-nbctl lsp-add $ls_name $ln_port_name "" 101 ++ elif test $i -eq 2; then ++ ovn-nbctl lsp-add $ls_name $ln_port_name "" 201 ++ fi ++ ovn-nbctl lsp-set-addresses $ln_port_name unknown ++ ovn-nbctl lsp-set-type $ln_port_name localnet ++ ovn-nbctl lsp-set-options $ln_port_name network_name=phys ++done ++ ++# lsp_to_ls LSP ++# ++# Prints the name of the logical switch that contains LSP. ++lsp_to_ls () { ++ case $1 in dnl ( ++ lp?[[11]]) echo ls1 ;; dnl ( ++ lp?[[12]]) echo ls2 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_hv () { ++ case $1 in dnl ( ++ vif[[1]]?) echo hv1 ;; dnl ( ++ vif[[2]]?) echo hv2 ;; dnl ( ++ vif?[[north]]?) echo hv4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++net_add n1 ++for i in 1 2; do ++ sim_add hv$i ++ as hv$i ++ ovs-vsctl add-br br-phys ++ ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++ ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i" ++ ovn_attach n1 br-phys 192.168.0.$i ++ ++ ovs-vsctl add-port br-int vif$i$i -- \ ++ set Interface vif$i$i external-ids:iface-id=lp$i$i \ ++ options:tx_pcap=hv$i/vif$i$i-tx.pcap \ ++ options:rxq_pcap=hv$i/vif$i$i-rx.pcap \ ++ ofport-request=$i$i ++ ++ lsp_name=lp$i$i ++ ls_name=$(lsp_to_ls $lsp_name) ++ ++ ovn-nbctl lsp-add $ls_name $lsp_name ++ ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i" ++ ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i ++ ++ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup]) ++ ++done ++ ++ovn-nbctl ls-add ls-underlay ++ovn-nbctl lsp-add ls-underlay ln3 "" 1000 ++ovn-nbctl lsp-set-addresses ln3 unknown ++ovn-nbctl lsp-set-type ln3 localnet ++ovn-nbctl lsp-set-options ln3 network_name=phys ++ ++ovn-nbctl ls-add ls-north ++ovn-nbctl lsp-add ls-north ln4 "" 1000 ++ovn-nbctl lsp-set-addresses ln4 unknown ++ovn-nbctl lsp-set-type ln4 localnet ++ovn-nbctl lsp-set-options ln4 network_name=phys ++ ++# Add a VM on ls-north ++ovn-nbctl lsp-add ls-north lp-north ++ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10" ++ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11 ++ ++# Add 3rd hypervisor ++sim_add hv3 ++as hv3 ovs-vsctl add-br br-phys ++as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++as hv3 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33" ++as hv3 ovn_attach n1 br-phys 192.168.0.3 ++ ++# Add 4th hypervisor ++sim_add hv4 ++as hv4 ovs-vsctl add-br br-phys ++as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys ++as hv4 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44" ++as hv4 ovn_attach n1 br-phys 192.168.0.4 ++ ++as hv4 ovs-vsctl add-port br-int vif-north -- \ ++ set Interface vif-north external-ids:iface-id=lp-north \ ++ options:tx_pcap=hv4/vif-north-tx.pcap \ ++ options:rxq_pcap=hv4/vif-north-rx.pcap \ ++ ofport-request=44 ++ ++ovn-nbctl lr-add router ++ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24 ++ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24 ++ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24 ++ ++ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router \ ++ options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router ++ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router \ ++ options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router ++ovn-nbctl lsp-add ls-underlay underlay-to-router -- set Logical_Switch_Port \ ++ underlay-to-router type=router \ ++ options:router-port=router-to-underlay \ ++ -- lsp-set-addresses underlay-to-router router ++ ++ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3 ++ovn-nbctl --stateless lr-nat-add router dnat_and_snat 172.31.0.100 192.168.1.1 ++ovn-nbctl lrp-set-redirect-type router-to-underlay bridged ++ ++ovn-nbctl --wait=sb sync ++ ++ ++OVN_POPULATE_ARP ++ ++# lsp_to_ls LSP ++# ++# Prints the name of the logical switch that contains LSP. ++lsp_to_ls () { ++ case $1 in dnl ( ++ lp?[[11]]) echo ls1 ;; dnl ( ++ lp?[[12]]) echo ls2 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_ls () { ++ case $1 in dnl ( ++ vif?[[11]]) echo ls1 ;; dnl ( ++ vif?[[12]]) echo ls2 ;; dnl ( ++ vif-north) echo ls-north ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++hv_to_num () { ++ case $1 in dnl ( ++ hv1) echo 1 ;; dnl ( ++ hv2) echo 2 ;; dnl ( ++ hv3) echo 3 ;; dnl ( ++ hv4) echo 4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_num () { ++ case $1 in dnl ( ++ vif22) echo 22 ;; dnl ( ++ vif21) echo 21 ;; dnl ( ++ vif11) echo 11 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_hv () { ++ case $1 in dnl ( ++ vif[[1]]?) echo hv1 ;; dnl ( ++ vif[[2]]?) echo hv2 ;; dnl ( ++ vif-north) echo hv4 ;; dnl ( ++ *) AT_FAIL_IF([:]) ;; ++ esac ++} ++ ++vif_to_lrp () { ++ echo router-to-`vif_to_ls $1` ++} ++ ++ip_to_hex() { ++ printf "%02x%02x%02x%02x" "$@" ++} ++ ++ ++test_ip() { ++ # This packet has bad checksums but logical L3 routing doesn't check. ++ local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 outport=$6 ++ local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000 ++ shift; shift; shift; shift; shift ++ hv=`vif_to_hv $inport` ++ as $hv ovs-appctl netdev-dummy/receive $inport $packet ++ in_ls=`vif_to_ls $inport` ++ for outport; do ++ out_ls=`vif_to_ls $outport` ++ if test $in_ls = $out_ls; then ++ # Ports on the same logical switch receive exactly the same packet. ++ echo $packet ++ else ++ # Routing decrements TTL and updates source and dest MAC ++ # (and checksum). ++ out_lrp=`vif_to_lrp $outport` ++ # For North-South, packet will come via gateway chassis, i.e hv3 ++ if test $inport = vif-north; then ++ echo f0000000001100000101020308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected ++ fi ++ if test $outport = vif-north; then ++ echo f0f00000001100000101020708004500001c000000003e111726ac1f0064${dst_ip}0035111100080000 >> $outport.expected ++ fi ++ fi >> $outport.expected ++ done ++} ++ ++# Dump a bunch of info helpful for debugging if there's a failure. ++ ++echo "------ OVN dump ------" ++ovn-nbctl show ++ovn-nbctl lr-nat-list router ++ovn-sbctl show ++ovn-sbctl list port_binding ++ovn-sbctl list mac_binding ++ovn-sbctl dump-flows ++ ++echo "------ hv1 dump ------" ++as hv1 ovs-vsctl show ++as hv1 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv2 dump ------" ++as hv2 ovs-vsctl show ++as hv2 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv3 dump ------" ++as hv3 ovs-vsctl show ++as hv3 ovs-vsctl list Open_Vswitch ++ ++echo "------ hv4 dump ------" ++as hv4 ovs-vsctl show ++as hv4 ovs-vsctl list Open_Vswitch ++ ++echo "Send traffic South to Nouth" ++sip=`ip_to_hex 192 168 1 1` ++dip=`ip_to_hex 172 31 0 10` ++test_ip vif11 f00000000011 000001010203 $sip $dip vif-north ++ ++# Confirm that South to North traffic works fine. ++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected]) ++ ++# Confirm that NATing happened without connection tracker ++ ++AT_CHECK([ovn-sbctl dump-flows router | grep ct_snat | wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows router | grep ct_dnat | wc -l], [0], [0 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows router | grep ip4.dst=| wc -l], [0], [2 ++]) ++ ++AT_CHECK([ovn-sbctl dump-flows router | grep ip4.src=| wc -l], [0], [2 ++]) ++ ++echo "----------- Post Traffic hv1 dump -----------" ++as hv1 ovs-ofctl dump-flows br-int ++as hv1 ovs-ofctl show br-phys ++as hv1 ovs-appctl fdb/show br-phys ++as hv1 ovs-dpctl dump-flows ++ ++echo "----------- Post Traffic hv2 dump -----------" ++as hv2 ovs-ofctl dump-flows br-int ++as hv2 ovs-ofctl show br-phys ++as hv2 ovs-appctl fdb/show br-phys ++ ++echo "----------- Post Traffic hv3 dump -----------" ++as hv3 ovs-ofctl dump-flows br-int ++as hv3 ovs-ofctl show br-phys ++as hv3 ovs-appctl dpctl/dump-conntrack ++as hv3 ovs-appctl fdb/show br-phys ++as hv3 ovs-dpctl dump-flows ++as hv3 ovs-ofctl dump-flows br-int ++ ++echo "----------- Post Traffic hv4 dump -----------" ++as hv4 ovs-ofctl dump-flows br-int ++as hv4 ovs-ofctl show br-phys ++as hv4 ovs-appctl fdb/show br-phys ++ ++OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) ++ ++AT_CLEANUP +-- +2.23.0 + diff --git a/SOURCES/0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch b/SOURCES/0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch new file mode 100644 index 0000000..c6cad27 --- /dev/null +++ b/SOURCES/0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch @@ -0,0 +1,45 @@ +From ac3a1c6b5e29af2de1ae0e87fd186430e8f27229 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Sun, 8 Sep 2019 19:29:29 +0530 +Subject: [PATCH 08/12] Fix the segfault seen in ovn-controller when running + tests + +The test case - "116: ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S Ping" +is failing with the segfault in ovn-controller occationally. + +This patch fixes it. + +backtrace +------ +Program terminated with signal SIGSEGV, Segmentation fault. +0x0000000000422414 in put_remote_port_redirect_bridged (...) + at /usr/include/bits/byteswap.h:52 +52 return __builtin_bswap32 (__bsx); +[Current thread is 1 (Thread 0x7f985fbe04c0 (LWP 18625))] +------ + +Fixes: 03493b33c073("OVN: Vlan backed DVR N-S, redirect packet via localnet port") +CC: Ankur Sharma +Acked-by: Lorenzo Bianconi +Signed-off-by: Numan Siddique +--- + ovn/controller/physical.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c +index 9d553da9a..8f8a5ba27 100644 +--- a/ovn/controller/physical.c ++++ b/ovn/controller/physical.c +@@ -280,6 +280,9 @@ put_remote_port_redirect_bridged(const struct + const struct sbrec_port_binding *ls_localnet_port; + + ls_localnet_port = get_localnet_port(local_datapaths, ls_dp_key); ++ if (!ls_localnet_port) { ++ return; ++ } + + src_mac = ofpact_put_SET_ETH_SRC(ofpacts_p); + src_mac->mac = binding_mac; +-- +2.23.0 + diff --git a/SOURCES/0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch b/SOURCES/0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch new file mode 100644 index 0000000..9f4a0b6 --- /dev/null +++ b/SOURCES/0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch @@ -0,0 +1,1258 @@ +From 8c91aefae19e159549f27029ccf799c209c5d7dc Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Tue, 21 May 2019 23:30:26 +0530 +Subject: [PATCH 09/12] ovn-northd: Add support for Load Balancer health check + +The present Load balancer feature in OVN provides load balancing +functionality to the back end ips without checking if the chosen +backend ip is reachable or not. In case a back end service is +down and if that IP is chosen, then packet will be lost. + +This patch series adds the health check feature for these +backend IPs and only active backend IPs are considered for +load balancing. CMS needs to enable this functionality. +In the SB DB a new table Service_Monitor is added. For every +backend IP in the load balancer for which health check is configured, +a new row in the Service_Monitor table is created. In the upcoming +patch in this series, ovn-controller will monitor the services set +in this table by generating a health check packet. + +Health checks are supported only for IPv4 Load balancers in this patch. + +Existing load balancers will be unaffected after this patch. + +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique +--- + ovn/northd/ovn-northd.8.xml | 75 +++++- + ovn/northd/ovn-northd.c | 492 +++++++++++++++++++++++++++++++++--- + ovn/ovn-nb.ovsschema | 25 +- + ovn/ovn-nb.xml | 68 +++++ + ovn/ovn-sb.ovsschema | 33 ++- + ovn/ovn-sb.xml | 85 +++++++ + tests/ovn-northd.at | 215 ++++++++++++++++ + 7 files changed, 947 insertions(+), 46 deletions(-) + +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index d1af97d15..9515c7790 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -308,6 +308,16 @@ + previously created, it will be associated to the empty_lb logical flow +

        + ++

        ++ This table also has a priority-110 flow with the match ++ eth.src == E for all logical switch ++ datapaths to move traffic to the next table. Where E ++ is the service monitor mac defined in the ++ colum of table. ++

        ++ +

        Ingress Table 5: Pre-stateful

        + +

        +@@ -476,7 +486,10 @@ + , where args contains comma separated IP addresses + (and optional port numbers) to load balance to. The address family of + the IP addresses of args is the same as the address family +- of VIP ++ of VIP. If health check is enabled, then args ++ will only contain those endpoints whose service monitor status entry ++ in OVN_Southbound db is either online or ++ empty. +

      • +
      • + For all the configured load balancing rules for a switch in +@@ -698,6 +711,51 @@ nd_na_router { +

        +
      • + ++
      • ++

        ++ For each SVC_MON_SRC_IP defined in the value of ++ the column of ++ table, priority-110 ++ logical flow is added with the match ++ arp.tpa == SVC_MON_SRC_IP ++ && && arp.op == 1 and applies the action ++

        ++ ++
        ++eth.dst = eth.src;
        ++eth.src = E;
        ++arp.op = 2; /* ARP reply. */
        ++arp.tha = arp.sha;
        ++arp.sha = E;
        ++arp.tpa = arp.spa;
        ++arp.spa = A;
        ++outport = inport;
        ++flags.loopback = 1;
        ++output;
        ++        
        ++ ++

        ++ where E is the service monitor source mac defined in ++ the column in the table. This mac is used as the source mac ++ in the service monitor packets for the load balancer endpoint IP ++ health checks. ++

        ++ ++

        ++ SVC_MON_SRC_IP is used as the source ip in the ++ service monitor IPv4 packets for the load balancer endpoint IP ++ health checks. ++

        ++ ++

        ++ These flows are required if an ARP request is sent for the IP ++ SVC_MON_SRC_IP. ++

        ++
      • ++ +
      • + One priority-0 fallback flow that matches all packets and advances to + the next table. +@@ -1100,6 +1158,16 @@ output; + tracker for packet de-fragmentation. +

        + ++

        ++ This table also has a priority-110 flow with the match ++ eth.src == E for all logical switch ++ datapaths to move traffic to the next table. Where E ++ is the service monitor mac defined in the ++ colum of table. ++

        ++ +

        Egress Table 1: to-lport Pre-ACLs

        + +

        +@@ -1940,7 +2008,10 @@ icmp6 { + (and optional port numbers) to load balance to. If the router is + configured to force SNAT any load-balanced packets, the above action + will be replaced by flags.force_snat_for_lb = 1; +- ct_lb(args);. ++ ct_lb(args);. If health check is enabled, then ++ args will only contain those endpoints whose service ++ monitor status entry in OVN_Southbound db is ++ either online or empty. +

      • + +
      • +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 8938e458f..1ded5dd9b 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -88,6 +88,13 @@ static struct eth_addr mac_prefix; + + static bool controller_event_en; + ++/* MAC allocated for service monitor usage. Just one mac is allocated ++ * for this purpose and ovn-controller's on each chassis will make use ++ * of this mac when sending out the packets to monitor the services ++ * defined in Service_Monitor Southbound table. Since these packets ++ * all locally handled, having just one mac is good enough. */ ++static char svc_monitor_mac[ETH_ADDR_STRLEN + 1]; ++ + #define MAX_OVN_TAGS 4096 + + /* Pipeline stages. */ +@@ -2978,6 +2985,291 @@ cleanup_sb_ha_chassis_groups(struct northd_context *ctx, + } + } + ++struct ovn_lb { ++ struct hmap_node hmap_node; ++ ++ const struct nbrec_load_balancer *nlb; /* May be NULL. */ ++ ++ struct lb_vip *vips; ++ size_t n_vips; ++}; ++ ++struct lb_vip { ++ char *vip; ++ uint16_t vip_port; ++ int addr_family; ++ char *backend_ips; ++ ++ bool health_check; ++ struct lb_vip_backend *backends; ++ size_t n_backends; ++}; ++ ++struct lb_vip_backend { ++ char *ip; ++ uint16_t port; ++ ++ struct ovn_port *op; /* Logical port to which the ip belong to. */ ++ bool health_check; ++ char *svc_mon_src_ip; /* Source IP to use for monitoring. */ ++ const struct sbrec_service_monitor *sbrec_monitor; ++}; ++ ++ ++static inline struct ovn_lb * ++ovn_lb_find(struct hmap *lbs, struct uuid *uuid) ++{ ++ struct ovn_lb *lb; ++ size_t hash = uuid_hash(uuid); ++ HMAP_FOR_EACH_WITH_HASH (lb, hmap_node, hash, lbs) { ++ if (uuid_equals(&lb->nlb->header_.uuid, uuid)) { ++ return lb; ++ } ++ } ++ ++ return NULL; ++} ++ ++ ++struct service_monitor_info { ++ struct hmap_node hmap_node; ++ const struct sbrec_service_monitor *sbrec_mon; ++ bool required; ++}; ++ ++ ++static struct service_monitor_info * ++create_or_get_service_mon(struct northd_context *ctx, ++ struct hmap *monitor_map, ++ const char *ip, const char *logical_port, ++ uint16_t service_port, const char *protocol) ++{ ++ uint32_t hash = service_port; ++ hash = hash_string(ip, hash); ++ hash = hash_string(logical_port, hash); ++ struct service_monitor_info *mon_info; ++ ++ HMAP_FOR_EACH_WITH_HASH (mon_info, hmap_node, hash, monitor_map) { ++ if (mon_info->sbrec_mon->port == service_port && ++ !strcmp(mon_info->sbrec_mon->ip, ip) && ++ !strcmp(mon_info->sbrec_mon->protocol, protocol) && ++ !strcmp(mon_info->sbrec_mon->logical_port, logical_port)) { ++ return mon_info; ++ } ++ } ++ ++ struct sbrec_service_monitor *sbrec_mon = ++ sbrec_service_monitor_insert(ctx->ovnsb_txn); ++ sbrec_service_monitor_set_ip(sbrec_mon, ip); ++ sbrec_service_monitor_set_port(sbrec_mon, service_port); ++ sbrec_service_monitor_set_logical_port(sbrec_mon, logical_port); ++ sbrec_service_monitor_set_protocol(sbrec_mon, protocol); ++ mon_info = xzalloc(sizeof *mon_info); ++ mon_info->sbrec_mon = sbrec_mon; ++ hmap_insert(monitor_map, &mon_info->hmap_node, hash); ++ return mon_info; ++} ++ ++static struct ovn_lb * ++ovn_lb_create(struct northd_context *ctx, struct hmap *lbs, ++ const struct nbrec_load_balancer *nbrec_lb, ++ struct hmap *ports, struct hmap *monitor_map) ++{ ++ struct ovn_lb *lb = xzalloc(sizeof *lb); ++ ++ size_t hash = uuid_hash(&nbrec_lb->header_.uuid); ++ lb->nlb = nbrec_lb; ++ hmap_insert(lbs, &lb->hmap_node, hash); ++ ++ lb->n_vips = smap_count(&nbrec_lb->vips); ++ lb->vips = xcalloc(lb->n_vips, sizeof (struct lb_vip)); ++ struct smap_node *node; ++ size_t n_vips = 0; ++ ++ SMAP_FOR_EACH (node, &nbrec_lb->vips) { ++ char *vip = NULL; ++ uint16_t port; ++ int addr_family; ++ ++ ip_address_and_port_from_lb_key(node->key, &vip, &port, ++ &addr_family); ++ if (!vip) { ++ continue; ++ } ++ ++ lb->vips[n_vips].vip = vip; ++ lb->vips[n_vips].vip_port = port; ++ lb->vips[n_vips].addr_family = addr_family; ++ lb->vips[n_vips].backend_ips = xstrdup(node->value); ++ ++ struct nbrec_load_balancer_health_check *lb_health_check = NULL; ++ for (size_t i = 0; i < nbrec_lb->n_health_check; i++) { ++ if (!strcmp(nbrec_lb->health_check[i]->vip, node->key)) { ++ lb_health_check = nbrec_lb->health_check[i]; ++ break; ++ } ++ } ++ ++ char *tokstr = xstrdup(node->value); ++ char *save_ptr = NULL; ++ char *token; ++ size_t n_backends = 0; ++ /* Format for a backend ips : IP1:port1,IP2:port2,...". */ ++ for (token = strtok_r(tokstr, ",", &save_ptr); ++ token != NULL; ++ token = strtok_r(NULL, ",", &save_ptr)) { ++ n_backends++; ++ } ++ ++ free(tokstr); ++ tokstr = xstrdup(node->value); ++ save_ptr = NULL; ++ ++ lb->vips[n_vips].n_backends = n_backends; ++ lb->vips[n_vips].backends = xcalloc(n_backends, ++ sizeof (struct lb_vip_backend)); ++ lb->vips[n_vips].health_check = lb_health_check ? true: false; ++ ++ size_t i = 0; ++ for (token = strtok_r(tokstr, ",", &save_ptr); ++ token != NULL; ++ token = strtok_r(NULL, ",", &save_ptr)) { ++ char *backend_ip; ++ uint16_t backend_port; ++ ++ ip_address_and_port_from_lb_key(token, &backend_ip, &backend_port, ++ &addr_family); ++ ++ if (!backend_ip) { ++ continue; ++ } ++ ++ /* Get the logical port to which this ip belongs to. */ ++ struct ovn_port *op = NULL; ++ char *svc_mon_src_ip = NULL; ++ const char *s = smap_get(&nbrec_lb->ip_port_mappings, ++ backend_ip); ++ if (s) { ++ char *port_name = xstrdup(s); ++ char *p = strstr(port_name, ":"); ++ if (p) { ++ *p = 0; ++ p++; ++ op = ovn_port_find(ports, port_name); ++ svc_mon_src_ip = xstrdup(p); ++ } ++ free(port_name); ++ } ++ ++ lb->vips[n_vips].backends[i].ip = backend_ip; ++ lb->vips[n_vips].backends[i].port = backend_port; ++ lb->vips[n_vips].backends[i].op = op; ++ lb->vips[n_vips].backends[i].svc_mon_src_ip = svc_mon_src_ip; ++ ++ if (lb_health_check && op && svc_mon_src_ip) { ++ const char *protocol = nbrec_lb->protocol; ++ if (!protocol || !protocol[0]) { ++ protocol = "tcp"; ++ } ++ lb->vips[n_vips].backends[i].health_check = true; ++ struct service_monitor_info *mon_info = ++ create_or_get_service_mon(ctx, monitor_map, backend_ip, ++ op->nbsp->name, backend_port, ++ protocol); ++ ++ ovs_assert(mon_info); ++ sbrec_service_monitor_set_options( ++ mon_info->sbrec_mon, &lb_health_check->options); ++ if (!mon_info->sbrec_mon->src_mac || ++ strcmp(mon_info->sbrec_mon->src_mac, svc_monitor_mac)) { ++ sbrec_service_monitor_set_src_mac(mon_info->sbrec_mon, ++ svc_monitor_mac); ++ } ++ ++ if (!mon_info->sbrec_mon->src_ip || ++ strcmp(mon_info->sbrec_mon->src_ip, svc_mon_src_ip)) { ++ sbrec_service_monitor_set_src_ip(mon_info->sbrec_mon, ++ svc_mon_src_ip); ++ } ++ ++ lb->vips[n_vips].backends[i].sbrec_monitor = ++ mon_info->sbrec_mon; ++ mon_info->required = true; ++ } else { ++ lb->vips[n_vips].backends[i].health_check = false; ++ } ++ ++ i++; ++ } ++ ++ free(tokstr); ++ n_vips++; ++ } ++ ++ return lb; ++} ++ ++static void ++ovn_lb_destroy(struct ovn_lb *lb) ++{ ++ for (size_t i = 0; i < lb->n_vips; i++) { ++ free(lb->vips[i].vip); ++ free(lb->vips[i].backend_ips); ++ ++ for (size_t j = 0; j < lb->vips[i].n_backends; j++) { ++ free(lb->vips[i].backends[j].ip); ++ free(lb->vips[i].backends[j].svc_mon_src_ip); ++ } ++ ++ free(lb->vips[i].backends); ++ } ++ free(lb->vips); ++} ++ ++static void ++build_ovn_lbs(struct northd_context *ctx, struct hmap *ports, ++ struct hmap *lbs) ++{ ++ hmap_init(lbs); ++ struct hmap monitor_map = HMAP_INITIALIZER(&monitor_map); ++ ++ const struct sbrec_service_monitor *sbrec_mon; ++ SBREC_SERVICE_MONITOR_FOR_EACH (sbrec_mon, ctx->ovnsb_idl) { ++ uint32_t hash = sbrec_mon->port; ++ hash = hash_string(sbrec_mon->ip, hash); ++ hash = hash_string(sbrec_mon->logical_port, hash); ++ struct service_monitor_info *mon_info = xzalloc(sizeof *mon_info); ++ mon_info->sbrec_mon = sbrec_mon; ++ mon_info->required = false; ++ hmap_insert(&monitor_map, &mon_info->hmap_node, hash); ++ } ++ ++ const struct nbrec_load_balancer *nbrec_lb; ++ NBREC_LOAD_BALANCER_FOR_EACH (nbrec_lb, ctx->ovnnb_idl) { ++ ovn_lb_create(ctx, lbs, nbrec_lb, ports, &monitor_map); ++ } ++ ++ struct service_monitor_info *mon_info; ++ HMAP_FOR_EACH_POP (mon_info, hmap_node, &monitor_map) { ++ if (!mon_info->required) { ++ sbrec_service_monitor_delete(mon_info->sbrec_mon); ++ } ++ ++ free(mon_info); ++ } ++ hmap_destroy(&monitor_map); ++} ++ ++static void ++destroy_ovn_lbs(struct hmap *lbs) ++{ ++ struct ovn_lb *lb; ++ HMAP_FOR_EACH_POP (lb, hmap_node, lbs) { ++ ovn_lb_destroy(lb); ++ free(lb); ++ } ++} ++ + /* Updates the southbound Port_Binding table so that it contains the logical + * switch ports specified by the northbound database. + * +@@ -4335,6 +4627,14 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows, + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110, + "nd || nd_rs || nd_ra", "next;"); + ++ /* Do not send service monitor packets to conntrack. */ ++ char *svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac); ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110, ++ svc_check_match, "next;"); ++ ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110, ++ svc_check_match, "next;"); ++ free(svc_check_match); ++ + /* Allow all packets to go to next tables by default. */ + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;"); +@@ -5002,7 +5302,7 @@ build_lb(struct ovn_datapath *od, struct hmap *lflows) + } + + static void +-build_stateful(struct ovn_datapath *od, struct hmap *lflows) ++build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs) + { + /* Ingress and Egress stateful Table (Priority 0): Packets are + * allowed by default. */ +@@ -5036,47 +5336,69 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows) + * connection, so it is okay if we do not hit the above match on + * REGBIT_CONNTRACK_COMMIT. */ + for (int i = 0; i < od->nbs->n_load_balancer; i++) { +- struct nbrec_load_balancer *lb = od->nbs->load_balancer[i]; +- struct smap *vips = &lb->vips; +- struct smap_node *node; ++ struct ovn_lb *lb = ++ ovn_lb_find(lbs, &od->nbs->load_balancer[i]->header_.uuid); ++ ovs_assert(lb); + +- SMAP_FOR_EACH (node, vips) { +- uint16_t port = 0; +- int addr_family; ++ for (size_t j = 0; j < lb->n_vips; j++) { ++ struct lb_vip *lb_vip = &lb->vips[j]; ++ /* New connections in Ingress table. */ ++ struct ds action = DS_EMPTY_INITIALIZER; ++ if (lb_vip->health_check) { ++ ds_put_cstr(&action, "ct_lb("); ++ ++ size_t n_active_backends = 0; ++ for (size_t k = 0; k < lb_vip->n_backends; k++) { ++ struct lb_vip_backend *backend = &lb_vip->backends[k]; ++ bool is_up = true; ++ if (backend->health_check && backend->sbrec_monitor && ++ backend->sbrec_monitor->status && ++ strcmp(backend->sbrec_monitor->status, "online")) { ++ is_up = false; ++ } + +- /* node->key contains IP:port or just IP. */ +- char *ip_address = NULL; +- ip_address_and_port_from_lb_key(node->key, &ip_address, &port, +- &addr_family); +- if (!ip_address) { +- continue; ++ if (is_up) { ++ n_active_backends++; ++ ds_put_format(&action, "%s:%"PRIu16",", ++ backend->ip, backend->port); ++ } ++ } ++ ++ if (!n_active_backends) { ++ ds_clear(&action); ++ ds_put_cstr(&action, "drop;"); ++ } else { ++ ds_chomp(&action, ','); ++ ds_put_cstr(&action, ");"); ++ } ++ } else { ++ ds_put_format(&action, "ct_lb(%s);", lb_vip->backend_ips); + } + +- /* New connections in Ingress table. */ +- char *action = xasprintf("ct_lb(%s);", node->value); + struct ds match = DS_EMPTY_INITIALIZER; +- if (addr_family == AF_INET) { +- ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address); ++ if (lb_vip->addr_family == AF_INET) { ++ ds_put_format(&match, "ct.new && ip4.dst == %s", lb_vip->vip); + } else { +- ds_put_format(&match, "ct.new && ip6.dst == %s", ip_address); ++ ds_put_format(&match, "ct.new && ip6.dst == %s", lb_vip->vip); + } +- if (port) { +- if (lb->protocol && !strcmp(lb->protocol, "udp")) { +- ds_put_format(&match, " && udp.dst == %d", port); ++ if (lb_vip->vip_port) { ++ if (lb->nlb->protocol && !strcmp(lb->nlb->protocol, "udp")) { ++ ds_put_format(&match, " && udp.dst == %d", ++ lb_vip->vip_port); + } else { +- ds_put_format(&match, " && tcp.dst == %d", port); ++ ds_put_format(&match, " && tcp.dst == %d", ++ lb_vip->vip_port); + } + ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, +- 120, ds_cstr(&match), action); ++ 120, ds_cstr(&match), ds_cstr(&action)); + } else { + ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, +- 110, ds_cstr(&match), action); ++ 110, ds_cstr(&match), ds_cstr(&action)); + } + +- free(ip_address); + ds_destroy(&match); +- free(action); +- } ++ ds_destroy(&action); ++ } + } + } + +@@ -5354,7 +5676,8 @@ static void + build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + struct hmap *port_groups, struct hmap *lflows, + struct hmap *mcgroups, struct hmap *igmp_groups, +- struct shash *meter_groups) ++ struct shash *meter_groups, ++ struct hmap *lbs) + { + /* This flow table structure is documented in ovn-northd(8), so please + * update ovn-northd.8.xml if you change anything. */ +@@ -5376,7 +5699,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + build_acls(od, lflows, port_groups); + build_qos(od, lflows); + build_lb(od, lflows); +- build_stateful(od, lflows); ++ build_stateful(od, lflows, lbs); + } + + /* Logical switch ingress table 0: Admission control framework (priority +@@ -5578,6 +5901,46 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;"); + } + ++ /* Ingress table 11: ARP/ND responder for service monitor source ip. ++ * (priority 110)*/ ++ struct ovn_lb *lb; ++ HMAP_FOR_EACH (lb, hmap_node, lbs) { ++ for (size_t i = 0; i < lb->n_vips; i++) { ++ if (!lb->vips[i].health_check) { ++ continue; ++ } ++ ++ for (size_t j = 0; j < lb->vips[i].n_backends; j++) { ++ if (!lb->vips[i].backends[j].op || ++ !lb->vips[i].backends[j].svc_mon_src_ip) { ++ continue; ++ } ++ ++ ds_clear(&match); ++ ds_put_format(&match, "arp.tpa == %s && arp.op == 1", ++ lb->vips[i].backends[j].svc_mon_src_ip); ++ ds_clear(&actions); ++ ds_put_format(&actions, ++ "eth.dst = eth.src; " ++ "eth.src = %s; " ++ "arp.op = 2; /* ARP reply */ " ++ "arp.tha = arp.sha; " ++ "arp.sha = %s; " ++ "arp.tpa = arp.spa; " ++ "arp.spa = %s; " ++ "outport = inport; " ++ "flags.loopback = 1; " ++ "output;", ++ svc_monitor_mac, svc_monitor_mac, ++ lb->vips[i].backends[j].svc_mon_src_ip); ++ ovn_lflow_add(lflows, lb->vips[i].backends[j].op->od, ++ S_SWITCH_IN_ARP_ND_RSP, 110, ++ ds_cstr(&match), ds_cstr(&actions)); ++ } ++ } ++ } ++ ++ + /* Logical switch ingress table 12 and 13: DHCP options and response + * priority 100 flows. */ + HMAP_FOR_EACH (op, key_node, ports) { +@@ -9043,12 +9406,13 @@ static void + build_lflows(struct northd_context *ctx, struct hmap *datapaths, + struct hmap *ports, struct hmap *port_groups, + struct hmap *mcgroups, struct hmap *igmp_groups, +- struct shash *meter_groups) ++ struct shash *meter_groups, ++ struct hmap *lbs) + { + struct hmap lflows = HMAP_INITIALIZER(&lflows); + + build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups, +- igmp_groups, meter_groups); ++ igmp_groups, meter_groups, lbs); + build_lrouter_flows(datapaths, ports, &lflows, meter_groups); + + /* Push changes to the Logical_Flow table to database. */ +@@ -9765,9 +10129,11 @@ ovnnb_db_run(struct northd_context *ctx, + struct hmap mcast_groups; + struct hmap igmp_groups; + struct shash meter_groups = SHASH_INITIALIZER(&meter_groups); ++ struct hmap lbs; + + build_datapaths(ctx, datapaths, lr_list); + build_ports(ctx, sbrec_chassis_by_name, datapaths, ports); ++ build_ovn_lbs(ctx, ports, &lbs); + build_ipam(datapaths, ports); + build_port_group_lswitches(ctx, &port_groups, ports); + build_lrouter_groups(ports, lr_list); +@@ -9775,12 +10141,14 @@ ovnnb_db_run(struct northd_context *ctx, + build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups); + build_meter_groups(ctx, &meter_groups); + build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups, +- &igmp_groups, &meter_groups); ++ &igmp_groups, &meter_groups, &lbs); + + sync_address_sets(ctx); + sync_port_groups(ctx); + sync_meters(ctx); + sync_dns_entries(ctx, datapaths); ++ destroy_ovn_lbs(&lbs); ++ hmap_destroy(&lbs); + + struct ovn_igmp_group *igmp_group, *next_igmp_group; + +@@ -9829,16 +10197,43 @@ ovnnb_db_run(struct northd_context *ctx, + &addr.ea[0], &addr.ea[1], &addr.ea[2])) { + mac_prefix = addr; + } +- } else { +- struct smap options; ++ } + ++ const char *monitor_mac = smap_get(&nb->options, "svc_monitor_mac"); ++ if (monitor_mac) { ++ struct eth_addr addr; ++ ++ memset(&addr, 0, sizeof addr); ++ if (eth_addr_from_string(monitor_mac, &addr)) { ++ snprintf(svc_monitor_mac, sizeof svc_monitor_mac, ++ ETH_ADDR_FMT, ETH_ADDR_ARGS(addr)); ++ } else { ++ monitor_mac = NULL; ++ } ++ } ++ ++ if (!mac_addr_prefix || !monitor_mac) { ++ struct smap options; + smap_clone(&options, &nb->options); +- eth_addr_random(&mac_prefix); +- memset(&mac_prefix.ea[3], 0, 3); + +- smap_add_format(&options, "mac_prefix", +- "%02"PRIx8":%02"PRIx8":%02"PRIx8, +- mac_prefix.ea[0], mac_prefix.ea[1], mac_prefix.ea[2]); ++ if (!mac_addr_prefix) { ++ eth_addr_random(&mac_prefix); ++ memset(&mac_prefix.ea[3], 0, 3); ++ ++ smap_add_format(&options, "mac_prefix", ++ "%02"PRIx8":%02"PRIx8":%02"PRIx8, ++ mac_prefix.ea[0], mac_prefix.ea[1], ++ mac_prefix.ea[2]); ++ } ++ ++ if (!monitor_mac) { ++ struct eth_addr addr; ++ eth_addr_random(&addr); ++ snprintf(svc_monitor_mac, sizeof svc_monitor_mac, ++ ETH_ADDR_FMT, ETH_ADDR_ARGS(addr)); ++ smap_replace(&options, "svc_monitor_mac", svc_monitor_mac); ++ } ++ + nbrec_nb_global_verify_options(nb); + nbrec_nb_global_set_options(nb, &options); + +@@ -10631,6 +11026,25 @@ main(int argc, char *argv[]) + &sbrec_ip_multicast_col_query_interval); + add_column_noalert(ovnsb_idl_loop.idl, + &sbrec_ip_multicast_col_query_max_resp); ++ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_service_monitor); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_ip); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_logical_port); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_port); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_options); ++ ovsdb_idl_add_column(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_status); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_protocol); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_src_mac); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_src_ip); ++ add_column_noalert(ovnsb_idl_loop.idl, ++ &sbrec_service_monitor_col_external_ids); + + struct ovsdb_idl_index *sbrec_chassis_by_name + = chassis_index_create(ovnsb_idl_loop.idl); +diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema +index 084305b24..12999a466 100644 +--- a/ovn/ovn-nb.ovsschema ++++ b/ovn/ovn-nb.ovsschema +@@ -1,7 +1,7 @@ + { + "name": "OVN_Northbound", +- "version": "5.17.0", +- "cksum": "1128988054 23237", ++ "version": "5.18.0", ++ "cksum": "2806349485 24196", + "tables": { + "NB_Global": { + "columns": { +@@ -152,10 +152,31 @@ + "type": {"key": {"type": "string", + "enum": ["set", ["tcp", "udp"]]}, + "min": 0, "max": 1}}, ++ "health_check": {"type": { ++ "key": {"type": "uuid", ++ "refTable": "Load_Balancer_Health_Check", ++ "refType": "strong"}, ++ "min": 0, ++ "max": "unlimited"}}, ++ "ip_port_mappings": { ++ "type": {"key": "string", "value": "string", ++ "min": 0, "max": "unlimited"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "isRoot": true}, ++ "Load_Balancer_Health_Check": { ++ "columns": { ++ "vip": {"type": "string"}, ++ "options": { ++ "type": {"key": "string", ++ "value": "string", ++ "min": 0, ++ "max": "unlimited"}}, ++ "external_ids": { ++ "type": {"key": "string", "value": "string", ++ "min": 0, "max": "unlimited"}}}, ++ "isRoot": false}, + "ACL": { + "columns": { + "name": {"type": {"key": {"type": "string", +diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml +index cf2c5136a..0d22e3280 100644 +--- a/ovn/ovn-nb.xml ++++ b/ovn/ovn-nb.xml +@@ -1297,6 +1297,74 @@ +

        + + ++ ++ Load balancer health checks associated with this load balancer. ++ If health check is desired for a vip's endpoints defined in ++ the ++ column, then a row in the table ++ should ++ be created and referenced here and L4 port should be defined ++ for the vip and it's endpoints. Health checks are supported only ++ for IPv4 load balancers. ++ ++ ++ ++

        ++ This column is used if load balancer health checks are enabled. ++ This keeps a mapping of endpoint IP to the logical port name. ++ The source ip to be used for health checks is also expected to be ++ defined. The key of the mapping is the endpoint IP and the value ++ is in the format : port_name:SRC_IP ++

        ++ ++

        ++ Eg. If there is a VIP entry: ++ "10.0.0.10:80=10.0.0.4:8080,20.0.0.4:8080", ++ then the IP to port mappings should be defined as: ++ "10.0.0.4"="sw0-p1:10.0.0.2" and ++ "20.0.0.4"="sw1-p1:20.0.0.2". 10.0.0.2 ++ and 20.0.0.2 will be used by ovn-controller ++ as source ip when it sends out health check packets. ++

        ++
        ++ ++ ++ ++ See External IDs at the beginning of this document. ++ ++ ++ ++ ++ ++

        ++ Each row represents one load balancer health check. Health checks ++ are supported for IPv4 load balancers only. ++

        ++ ++ ++ vip whose endpoints should be monitored for health check. ++ ++ ++ ++ ++ The interval, in seconds, between health checks. ++ ++ ++ ++ The time, in seconds, after which a health check times out. ++ ++ ++ ++ The number of successful checks after which the endpoint is ++ considered online. ++ ++ ++ ++ The number of failure checks after which the endpoint is considered ++ offline. ++ ++ ++ + + + See External IDs at the beginning of this document. +diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema +index 5c013b17e..56af0ed3e 100644 +--- a/ovn/ovn-sb.ovsschema ++++ b/ovn/ovn-sb.ovsschema +@@ -1,7 +1,7 @@ + { + "name": "OVN_Southbound", +- "version": "2.5.0", +- "cksum": "1257419092 20387", ++ "version": "2.6.0", ++ "cksum": "4271405686 21646", + "tables": { + "SB_Global": { + "columns": { +@@ -403,4 +403,31 @@ + "refType": "weak"}, + "min": 0, "max": "unlimited"}}}, + "indexes": [["address", "datapath", "chassis"]], +- "isRoot": true}}} ++ "isRoot": true}, ++ "Service_Monitor": { ++ "columns": { ++ "ip": {"type": "string"}, ++ "protocol": { ++ "type": {"key": {"type": "string", ++ "enum": ["set", ["tcp", "udp"]]}, ++ "min": 0, "max": 1}}, ++ "port": {"type": {"key": {"type": "integer", ++ "minInteger": 0, ++ "maxInteger": 32767}}}, ++ "logical_port": {"type": "string"}, ++ "src_mac": {"type": "string"}, ++ "src_ip": {"type": "string"}, ++ "status": { ++ "type": {"key": {"type": "string", ++ "enum": ["set", ["online", "offline", "error"]]}, ++ "min": 0, "max": 1}}, ++ "options": { ++ "type": {"key": "string", "value": "string", ++ "min": 0, "max": "unlimited"}}, ++ "external_ids": { ++ "type": {"key": "string", "value": "string", ++ "min": 0, "max": "unlimited"}}}, ++ "indexes": [["logical_port", "ip", "port", "protocol"]], ++ "isRoot": true} ++ } ++} +diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml +index e5fb51a9d..335f9031b 100644 +--- a/ovn/ovn-sb.xml ++++ b/ovn/ovn-sb.xml +@@ -3743,4 +3743,89 @@ tcp.flags = RST; + The destination port bindings for this IGMP group. + +
        ++ ++ ++

        ++ This table montiors a service for its liveliness. The service ++ can be an IPv4 tcp or a udp service. ovn-controller ++ periodically sends out service monitor packets and updates the ++ status of the service. Service monitoring for IPv6 services is ++ not supported. ++

        ++ ++ ++ IP of the service to be monitored. Only IPv4 is supported. ++ ++ ++ ++ The protocol of the service. It can be either tcp or ++ udp. ++ ++ ++ ++ The tcp or udp port of the service. ++ ++ ++ ++ The VIF of logical port on which the service is running. The ++ ovn-controller which binds this logical_port ++ monitors the service by sending periodic monitor packets. ++ ++ ++ ++

        ++ The ovn-controller which binds the ++ logical_port updates the status to online ++ offline or error. ++

        ++ ++

        ++ For tcp service, ovn-controller sends a ++ TCP SYN packet to the service and expects a ++ TCP ACK response to consider the service to be ++ online. ++

        ++ ++

        ++ For udp service, ovn-controller sends a udp ++ packet to the service and doesn't expect any reply. If it receives ++ ICMP reply, then it considers the service to be offline. ++

        ++
        ++ ++ ++ Source Ethernet address to use in the service monitor packet. ++ ++ ++ ++ Source IPv4 address to use in the service monitor packet. ++ ++ ++ ++ ++ The interval, in seconds, between service monitor checks. ++ ++ ++ ++ The time, in seconds, after which the service monitor check times ++ out. ++ ++ ++ ++ The number of successful checks after which the service is ++ considered online. ++ ++ ++ ++ The number of failure checks after which the service is considered ++ offline. ++ ++ ++ ++ ++ ++ See External IDs at the beginning of this document. ++ ++ ++
        + +diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at +index 989ed4f47..da566f900 100644 +--- a/tests/ovn-northd.at ++++ b/tests/ovn-northd.at +@@ -1061,3 +1061,218 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [2 + ]) + + AT_CLEANUP ++ ++AT_SETUP([ovn -- check Load balancer health check and Service Monitor sync]) ++AT_SKIP_IF([test $HAVE_PYTHON = no]) ++ovn_start ++ ++ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 ++ ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1 ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1 ++ ++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor | wc -l`]) ++ ++ovn-nbctl --wait=sb -- --id=@hc create \ ++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \ ++health_check @hc ++ ++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor | wc -l`]) ++ ++# create logical switches and ports ++ovn-nbctl ls-add sw0 ++ovn-nbctl --wait=sb lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1 \ ++"00:00:00:00:00:03 10.0.0.3" ++ ++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | wc -l`]) ++ ++ovn-nbctl ls-add sw1 ++ovn-nbctl --wait=sb lsp-add sw1 sw1-p1 -- lsp-set-addresses sw1-p1 \ ++"02:00:00:00:00:03 20.0.0.3" ++ ++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l`]) ++ ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2 ++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | wc -l`]) ++ ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2 ++ ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l`]) ++ ++ovn-nbctl --wait=sb ls-lb-add sw0 lb1 ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++]) ++ ++# Delete the Load_Balancer_Health_Check ++ovn-nbctl --wait=sb clear load_balancer . health_check ++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor | wc -l`]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++]) ++ ++# Create the Load_Balancer_Health_Check again. ++ovn-nbctl --wait=sb -- --id=@hc create \ ++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \ ++health_check @hc ++ ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l`]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++]) ++ ++# Get the uuid of both the service_monitor ++sm_sw0_p1=`ovn-sbctl --bare --columns _uuid find service_monitor logical_port=sw0-p1` ++sm_sw1_p1=`ovn-sbctl --bare --columns _uuid find service_monitor logical_port=sw1-p1` ++ ++# Set the service monitor for sw1-p1 to offline ++ovn-sbctl set service_monitor $sm_sw1_p1 status=offline ++ ++OVS_WAIT_UNTIL([ ++ status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1` ++ test "$status" = "offline"]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);) ++]) ++ ++# Set the service monitor for sw0-p1 to offline ++ovn-sbctl set service_monitor $sm_sw0_p1 status=offline ++ ++OVS_WAIT_UNTIL([ ++ status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw0-p1` ++ test "$status" = "offline"]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++]) ++ ++ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \ ++| grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;) ++]) ++ ++# Set the service monitor for sw0-p1 and sw1-p1 to online ++ovn-sbctl set service_monitor $sm_sw0_p1 status=online ++ovn-sbctl set service_monitor $sm_sw1_p1 status=online ++ ++OVS_WAIT_UNTIL([ ++ status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1` ++ test "$status" = "online"]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++]) ++ ++# Set the service monitor for sw1-p1 to error ++ovn-sbctl set service_monitor $sm_sw1_p1 status=error ++OVS_WAIT_UNTIL([ ++ status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1` ++ test "$status" = "error"]) ++ ++ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \ ++| grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);) ++]) ++ ++# Add one more vip to lb1 ++ ++ovn-nbctl set load_balancer . vip:"10.0.0.40\:1000"="10.0.0.3:1000,20.0.0.3:80" ++ ++# create health_check for new vip - 10.0.0.40 ++ovn-nbctl --wait=sb -- --id=@hc create \ ++Load_Balancer_Health_Check vip="10.0.0.40\:1000" -- add Load_Balancer . \ ++health_check @hc ++ ++# There should be totally 3 rows in service_monitor for - ++# * 10.0.0.3:80 ++# * 10.0.0.3:1000 ++# * 20.0.0.3:80 ++ ++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l`]) ++ ++# There should be 2 rows with logical_port=sw0-p1 ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor logical_port=sw0-p1 | sed '/^$/d' | wc -l`]) ++ ++# There should be 1 row1 with port=1000 ++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor port=1000 | sed '/^$/d' | wc -l`]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);) ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000);) ++]) ++ ++# Set the service monitor for sw1-p1 to online ++ovn-sbctl set service_monitor $sm_sw1_p1 status=online ++ ++OVS_WAIT_UNTIL([ ++ status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1` ++ test "$status" = "online"]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);) ++]) ++ ++# Associate lb1 to sw1 ++ovn-nbctl --wait=sb ls-lb-add sw1 lb1 ++ovn-sbctl dump-flows sw1 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);) ++]) ++ ++# Now create lb2 same as lb1 but udp protocol. ++ovn-nbctl lb-add lb2 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 udp ++lb2_uuid=`ovn-nbctl lb-list | grep udp | awk '{print $1}'` ++ovn-nbctl --wait=sb set load_balancer $lb2_uuid ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2 ++ovn-nbctl --wait=sb set load_balancer $lb2_uuid ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2 ++ ++ovn-nbctl -- --id=@hc create Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer $lb2_uuid health_check @hc ++ ++ovn-nbctl ls-lb-add sw0 lb2 ++ovn-nbctl ls-lb-add sw1 lb2 ++ovn-nbctl lr-lb-add lr0 lb2 ++ ++OVS_WAIT_UNTIL([test 5 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l`]) ++ ++# Change the svc_monitor_mac. This should get reflected in service_monitor table rows. ++ovn-nbctl set NB_Global . options:svc_monitor_mac="fe:a0:65:a2:01:03" ++ ++OVS_WAIT_UNTIL([test 5 = `ovn-sbctl --bare --columns src_mac find \ ++service_monitor | grep "fe:a0:65:a2:01:03" | wc -l`]) ++ ++# Change the source ip for 10.0.0.3 backend ip in lb2 ++ovn-nbctl --wait=sb set load_balancer $lb2_uuid ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.100 ++ ++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns src_ip find \ ++service_monitor logical_port=sw0-p1 | grep "10.0.0.100" | wc -l`]) ++ ++ovn-nbctl --wait=sb lb-del lb1 ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find service_monitor | sed '/^$/d' | wc -l`]) ++ ++ovn-nbctl --wait=sb lb-del lb2 ++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor | wc -l`]) ++ ++AT_CLEANUP +-- +2.23.0 + diff --git a/SOURCES/0010-Add-a-new-action-handle_svc_check.patch b/SOURCES/0010-Add-a-new-action-handle_svc_check.patch new file mode 100644 index 0000000..0f7b0fb --- /dev/null +++ b/SOURCES/0010-Add-a-new-action-handle_svc_check.patch @@ -0,0 +1,192 @@ +From bb1755e8dcccfe3b4da44c36c8c1afa1b987f3d1 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Tue, 22 Oct 2019 12:07:46 +0530 +Subject: [PATCH 10/12] Add a new action - handle_svc_check + +This action will be used in an upcoming patch to handle the +service monitor replies from ovn-controller when it sends +out service monitor requests. + +This action gets translated to openflow controller action. + +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique +--- + include/ovn/actions.h | 17 +++++++++++++++- + ovn/lib/actions.c | 42 +++++++++++++++++++++++++++++++++++++++ + ovn/ovn-sb.xml | 17 ++++++++++++++++ + ovn/utilities/ovn-trace.c | 3 +++ + tests/ovn.at | 13 ++++++++++++ + 5 files changed, 91 insertions(+), 1 deletion(-) + +diff --git a/include/ovn/actions.h b/include/ovn/actions.h +index f4997e9c9..047a8d737 100644 +--- a/include/ovn/actions.h ++++ b/include/ovn/actions.h +@@ -88,7 +88,8 @@ struct ovn_extend_table; + OVNACT(OVNFIELD_LOAD, ovnact_load) \ + OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger) \ + OVNACT(TRIGGER_EVENT, ovnact_controller_event) \ +- OVNACT(BIND_VPORT, ovnact_bind_vport) ++ OVNACT(BIND_VPORT, ovnact_bind_vport) \ ++ OVNACT(HANDLE_SVC_CHECK, ovnact_handle_svc_check) + + /* enum ovnact_type, with a member OVNACT_ for each action. */ + enum OVS_PACKED_ENUM ovnact_type { +@@ -352,6 +353,12 @@ struct ovnact_bind_vport { + struct expr_field vport_parent; /* Logical virtual port's port name. */ + }; + ++/* OVNACT_HANDLE_SVC_CHECK. */ ++struct ovnact_handle_svc_check { ++ struct ovnact ovnact; ++ struct expr_field port; /* Logical port name. */ ++}; ++ + /* Internal use by the helpers below. */ + void ovnact_init(struct ovnact *, enum ovnact_type, size_t len); + void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len); +@@ -537,6 +544,14 @@ enum action_opcode { + * MFF_LOG_INPORT. + */ + ACTION_OPCODE_BIND_VPORT, ++ ++ /* "handle_svc_check(port)"." ++ * ++ * Arguments are passed through the packet metadata and data, as follows: ++ * ++ * MFF_LOG_INPORT = port ++ */ ++ ACTION_OPCODE_HANDLE_SVC_CHECK, + }; + + /* Header. */ +diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c +index 82a9334cb..5441922a7 100644 +--- a/ovn/lib/actions.c ++++ b/ovn/lib/actions.c +@@ -2814,6 +2814,46 @@ ovnact_bind_vport_free(struct ovnact_bind_vport *bp) + free(bp->vport); + } + ++static void ++parse_handle_svc_check(struct action_context *ctx OVS_UNUSED) ++{ ++ if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN)) { ++ return; ++ } ++ ++ struct ovnact_handle_svc_check *svc_chk = ++ ovnact_put_HANDLE_SVC_CHECK(ctx->ovnacts); ++ action_parse_field(ctx, 0, false, &svc_chk->port); ++ lexer_force_match(ctx->lexer, LEX_T_RPAREN); ++} ++ ++static void ++format_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk, ++ struct ds *s) ++{ ++ ds_put_cstr(s, "handle_svc_check("); ++ expr_field_format(&svc_chk->port, s); ++ ds_put_cstr(s, ");"); ++} ++ ++static void ++encode_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk, ++ const struct ovnact_encode_params *ep OVS_UNUSED, ++ struct ofpbuf *ofpacts) ++{ ++ const struct arg args[] = { ++ { expr_resolve_field(&svc_chk->port), MFF_LOG_INPORT }, ++ }; ++ encode_setup_args(args, ARRAY_SIZE(args), ofpacts); ++ encode_controller_op(ACTION_OPCODE_HANDLE_SVC_CHECK, ofpacts); ++ encode_restore_args(args, ARRAY_SIZE(args), ofpacts); ++} ++ ++static void ++ovnact_handle_svc_check_free(struct ovnact_handle_svc_check *sc OVS_UNUSED) ++{ ++} ++ + /* Parses an assignment or exchange or put_dhcp_opts action. */ + static void + parse_set_action(struct action_context *ctx) +@@ -2931,6 +2971,8 @@ parse_action(struct action_context *ctx) + parse_trigger_event(ctx, ovnact_put_TRIGGER_EVENT(ctx->ovnacts)); + } else if (lexer_match_id(ctx->lexer, "bind_vport")) { + parse_bind_vport(ctx); ++ } else if (lexer_match_id(ctx->lexer, "handle_svc_check")) { ++ parse_handle_svc_check(ctx); + } else { + lexer_syntax_error(ctx->lexer, "expecting action"); + } +diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml +index 335f9031b..82167c488 100644 +--- a/ovn/ovn-sb.xml ++++ b/ovn/ovn-sb.xml +@@ -2097,6 +2097,23 @@ tcp.flags = RST; + set to P. +

        +
      • ++ ++
        handle_svc_check(P);
        ++
        ++

        ++ Parameters: logical port string field P. ++

        ++ ++

        ++ Handles the service monitor reply received from the VIF of ++ the logical port P. ovn-controller ++ periodically sends out the service monitor packets for the ++ services configured in the ++ table and this action updates the status of those services. ++

        ++ ++

        Example: handle_svc_check(inport);

        ++
        +
        +
        + +diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c +index 05d711358..79257335b 100644 +--- a/ovn/utilities/ovn-trace.c ++++ b/ovn/utilities/ovn-trace.c +@@ -2221,6 +2221,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, + + case OVNACT_BIND_VPORT: + break; ++ ++ case OVNACT_HANDLE_SVC_CHECK: ++ break; + } + } + ds_destroy(&s); +diff --git a/tests/ovn.at b/tests/ovn.at +index 89bd3f174..f68ddcd33 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -1468,6 +1468,19 @@ bind_vport("xyzzy",; + bind_vport("xyzzy", inport; + Syntax error at `;' expecting `)'. + ++# handle_svc_check ++handle_svc_check(inport); ++ encodes as controller(userdata=00.00.00.12.00.00.00.00) ++ ++handle_svc_check(outport); ++ encodes as push:NXM_NX_REG14[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],controller(userdata=00.00.00.12.00.00.00.00),pop:NXM_NX_REG14[] ++ ++handle_svc_check(); ++ Syntax error at `)' expecting field name. ++ ++handle_svc_check(reg0); ++ Cannot use numeric field reg0 where string field is required. ++ + # Miscellaneous negative tests. + ; + Syntax error at `;'. +-- +2.23.0 + diff --git a/SOURCES/0011-Send-service-monitor-health-checks.patch b/SOURCES/0011-Send-service-monitor-health-checks.patch new file mode 100644 index 0000000..ba4776f --- /dev/null +++ b/SOURCES/0011-Send-service-monitor-health-checks.patch @@ -0,0 +1,1345 @@ +From b06fa16c0efeacc453f96e5640124acdf980bae7 Mon Sep 17 00:00:00 2001 +From: Numan Siddique +Date: Tue, 22 Oct 2019 12:28:36 +0530 +Subject: [PATCH 11/12] Send service monitor health checks + +ovn-controller will periodically sends out the service monitor packets +for the services configured in the SB DB Service_Monitor table. This +patch makes use of the action - handle_svc_check to handle the service +monitor reply packets from the service. + +This patch supports IPv4 TCP and UDP service monitoring. For TCP services, +it sends out a TCP SYN packet and expects TCP ACK packet in response. +If the response is received on time, the status of the service is set to +"online", otherwise it is set to "offline". + +For UDP services, it sends out a empty UDP packet and doesn't expect any +reply. In case the service is down, the host running the service, sends out +ICMP type 3 code 4 (destination unreachable) packet. If ovn-controller receives this +ICMP packet, it sets the status of the service to "offline". + +Right now only IPv4 service monitoring is supported. An upcoming patch will add +the support for IPv6. + +Change-Id: Ia8457c020b6d6cea15b1e5a73989a54430d8d9de +Acked-by: Mark Michelson +Signed-off-by: Numan Siddique +--- + ovn/controller/ovn-controller.c | 2 + + ovn/controller/pinctrl.c | 775 ++++++++++++++++++++++++++++++-- + ovn/controller/pinctrl.h | 2 + + ovn/northd/ovn-northd.8.xml | 10 + + ovn/northd/ovn-northd.c | 18 + + tests/ovn.at | 119 +++++ + tests/system-common-macros.at | 1 + + tests/system-ovn.at | 180 ++++++++ + 8 files changed, 1081 insertions(+), 26 deletions(-) + +diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c +index cb3d63cbb..f2378ab99 100644 +--- a/ovn/controller/ovn-controller.c ++++ b/ovn/controller/ovn-controller.c +@@ -2046,6 +2046,8 @@ main(int argc, char *argv[]) + sbrec_dns_table_get(ovnsb_idl_loop.idl), + sbrec_controller_event_table_get( + ovnsb_idl_loop.idl), ++ sbrec_service_monitor_table_get( ++ ovnsb_idl_loop.idl), + br_int, chassis, + &ed_runtime_data.local_datapaths, + &ed_runtime_data.active_tunnels); +diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c +index c6dd69fb3..ae1b8e1ca 100644 +--- a/ovn/controller/pinctrl.c ++++ b/ovn/controller/pinctrl.c +@@ -38,6 +38,7 @@ + #include "openvswitch/ofp-switch.h" + #include "openvswitch/ofp-util.h" + #include "openvswitch/vlog.h" ++#include "lib/random.h" + + #include "lib/dhcp.h" + #include "ovn-controller.h" +@@ -284,6 +285,22 @@ static void run_put_vport_bindings( + static void wait_put_vport_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn); + static void pinctrl_handle_bind_vport(const struct flow *md, + struct ofpbuf *userdata); ++static void pinctrl_handle_svc_check(struct rconn *swconn, ++ const struct flow *ip_flow, ++ struct dp_packet *pkt_in, ++ const struct match *md); ++static void init_svc_monitors(void); ++static void destroy_svc_monitors(void); ++static void sync_svc_monitors( ++ struct ovsdb_idl_txn *ovnsb_idl_txn, ++ const struct sbrec_service_monitor_table *svc_mon_table, ++ struct ovsdb_idl_index *sbrec_port_binding_by_name, ++ const struct sbrec_chassis *our_chassis) ++ OVS_REQUIRES(pinctrl_mutex); ++static void svc_monitors_run(struct rconn *swconn, ++ long long int *svc_monitors_next_run_time) ++ OVS_REQUIRES(pinctrl_mutex); ++static void svc_monitors_wait(long long int svc_monitors_next_run_time); + + COVERAGE_DEFINE(pinctrl_drop_put_mac_binding); + COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map); +@@ -446,6 +463,7 @@ pinctrl_init(void) + init_event_table(); + ip_mcast_snoop_init(); + init_put_vport_bindings(); ++ init_svc_monitors(); + pinctrl.br_int_name = NULL; + pinctrl_handler_seq = seq_create(); + pinctrl_main_seq = seq_create(); +@@ -1983,6 +2001,13 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg) + ovs_mutex_unlock(&pinctrl_mutex); + break; + ++ case ACTION_OPCODE_HANDLE_SVC_CHECK: ++ ovs_mutex_lock(&pinctrl_mutex); ++ pinctrl_handle_svc_check(swconn, &headers, &packet, ++ &pin.flow_metadata); ++ ovs_mutex_unlock(&pinctrl_mutex); ++ break; ++ + default: + VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32, + ntohl(ah->opcode)); +@@ -2052,6 +2077,7 @@ pinctrl_handler(void *arg_) + static long long int send_garp_time = LLONG_MAX; + /* Next multicast query (IGMP) in ms. */ + static long long int send_mcast_query_time = LLONG_MAX; ++ static long long int svc_monitors_next_run_time = LLONG_MAX; + + swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); + +@@ -2112,11 +2138,16 @@ pinctrl_handler(void *arg_) + } + } + ++ ovs_mutex_lock(&pinctrl_mutex); ++ svc_monitors_run(swconn, &svc_monitors_next_run_time); ++ ovs_mutex_unlock(&pinctrl_mutex); ++ + rconn_run_wait(swconn); + rconn_recv_wait(swconn); + send_garp_wait(send_garp_time); + ipv6_ra_wait(send_ipv6_ra_time); + ip_mcast_querier_wait(send_mcast_query_time); ++ svc_monitors_wait(svc_monitors_next_run_time); + + new_seq = seq_read(pinctrl_handler_seq); + seq_wait(pinctrl_handler_seq, new_seq); +@@ -2142,6 +2173,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct ovsdb_idl_index *sbrec_ip_multicast_opts, + const struct sbrec_dns_table *dns_table, + const struct sbrec_controller_event_table *ce_table, ++ const struct sbrec_service_monitor_table *svc_mon_table, + const struct ovsrec_bridge *br_int, + const struct sbrec_chassis *chassis, + const struct hmap *local_datapaths, +@@ -2178,6 +2210,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, + run_buffered_binding(sbrec_port_binding_by_datapath, + sbrec_mac_binding_by_lport_ip, + local_datapaths); ++ sync_svc_monitors(ovnsb_idl_txn, svc_mon_table, sbrec_port_binding_by_name, ++ chassis); + ovs_mutex_unlock(&pinctrl_mutex); + } + +@@ -2726,6 +2760,7 @@ pinctrl_destroy(void) + destroy_put_vport_bindings(); + destroy_dns_cache(); + ip_mcast_snoop_destroy(); ++ destroy_svc_monitors(); + seq_destroy(pinctrl_main_seq); + seq_destroy(pinctrl_handler_seq); + } +@@ -3158,6 +3193,36 @@ send_garp(struct rconn *swconn, struct garp_data *garp, + return garp->announce_time; + } + ++static void ++pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src, ++ struct eth_addr eth_dst, ovs_be32 ipv4_src, ++ ovs_be32 ipv4_dst, uint8_t ip_proto, uint8_t ttl, ++ uint16_t ip_payload_len) ++{ ++ dp_packet_clear(packet); ++ packet->packet_type = htonl(PT_ETH); ++ ++ struct eth_header *eh = dp_packet_put_zeros(packet, sizeof *eh); ++ eh->eth_dst = eth_dst; ++ eh->eth_src = eth_src; ++ ++ struct ip_header *nh = dp_packet_put_zeros(packet, sizeof *nh); ++ ++ eh->eth_type = htons(ETH_TYPE_IP); ++ dp_packet_set_l3(packet, nh); ++ nh->ip_ihl_ver = IP_IHL_VER(5, 4); ++ nh->ip_tot_len = htons(sizeof(struct ip_header) + ip_payload_len); ++ nh->ip_tos = IP_DSCP_CS6; ++ nh->ip_proto = ip_proto; ++ nh->ip_frag_off = htons(IP_DF); ++ ++ /* Setting tos and ttl to 0 and 1 respectively. */ ++ packet_set_ipv4(packet, ipv4_src, ipv4_dst, 0, ttl); ++ ++ nh->ip_csum = 0; ++ nh->ip_csum = csum(nh, sizeof *nh); ++} ++ + /* + * Multicast snooping configuration. + */ +@@ -3775,32 +3840,11 @@ ip_mcast_querier_send(struct rconn *swconn, struct ip_mcast_snoop *ip_ms, + struct dp_packet packet; + + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); +- +- uint8_t ip_tos = 0; +- uint8_t igmp_ttl = 1; +- +- dp_packet_clear(&packet); +- packet.packet_type = htonl(PT_ETH); +- +- struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh); +- eh->eth_dst = ip_ms->cfg.query_eth_dst; +- eh->eth_src = ip_ms->cfg.query_eth_src; +- +- struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh); +- +- eh->eth_type = htons(ETH_TYPE_IP); +- dp_packet_set_l3(&packet, nh); +- nh->ip_ihl_ver = IP_IHL_VER(5, 4); +- nh->ip_tot_len = htons(sizeof(struct ip_header) + +- sizeof(struct igmpv3_query_header)); +- nh->ip_tos = IP_DSCP_CS6; +- nh->ip_proto = IPPROTO_IGMP; +- nh->ip_frag_off = htons(IP_DF); +- packet_set_ipv4(&packet, ip_ms->cfg.query_ipv4_src, +- ip_ms->cfg.query_ipv4_dst, ip_tos, igmp_ttl); +- +- nh->ip_csum = 0; +- nh->ip_csum = csum(nh, sizeof *nh); ++ pinctrl_compose_ipv4(&packet, ip_ms->cfg.query_eth_src, ++ ip_ms->cfg.query_eth_dst, ++ ip_ms->cfg.query_ipv4_src, ++ ip_ms->cfg.query_ipv4_dst, ++ IPPROTO_IGMP, 1, sizeof(struct igmpv3_query_header)); + + struct igmpv3_query_header *igh = + dp_packet_put_zeros(&packet, sizeof *igh); +@@ -4693,3 +4737,682 @@ pinctrl_handle_bind_vport( + + notify_pinctrl_main(); + } ++ ++enum svc_monitor_state { ++ SVC_MON_S_INIT, ++ SVC_MON_S_WAITING, ++ SVC_MON_S_ONLINE, ++ SVC_MON_S_OFFLINE, ++}; ++ ++enum svc_monitor_status { ++ SVC_MON_ST_UNKNOWN, ++ SVC_MON_ST_OFFLINE, ++ SVC_MON_ST_ONLINE, ++}; ++ ++enum svc_monitor_protocol { ++ SVC_MON_PROTO_TCP, ++ SVC_MON_PROTO_UDP, ++}; ++ ++/* Service monitor health checks. */ ++struct svc_monitor { ++ struct hmap_node hmap_node; ++ struct ovs_list list_node; ++ ++ /* Should be accessed only with in the main ovn-controller ++ * thread. */ ++ const struct sbrec_service_monitor *sb_svc_mon; ++ ++ /* key */ ++ struct in6_addr ip; ++ uint32_t dp_key; ++ uint32_t port_key; ++ uint32_t proto_port; /* tcp/udp port */ ++ ++ struct eth_addr ea; ++ long long int timestamp; ++ bool is_ip6; ++ ++ long long int wait_time; ++ long long int next_send_time; ++ ++ struct smap options; ++ /* The interval, in milli seconds, between service monitor checks. */ ++ int interval; ++ ++ /* The time, in milli seconds, after which the service monitor check ++ * times out. */ ++ int svc_timeout; ++ ++ /* The number of successful checks after which the service is ++ * considered online. */ ++ int success_count; ++ int n_success; ++ ++ /* The number of failure checks after which the service is ++ * considered offline. */ ++ int failure_count; ++ int n_failures; ++ ++ enum svc_monitor_protocol protocol; ++ enum svc_monitor_state state; ++ enum svc_monitor_status status; ++ struct dp_packet pkt; ++ ++ uint32_t seq_no; ++ ovs_be16 tp_src; ++ ++ bool delete; ++}; ++ ++static struct hmap svc_monitors_map; ++static struct ovs_list svc_monitors; ++ ++static void ++init_svc_monitors(void) ++{ ++ hmap_init(&svc_monitors_map); ++ ovs_list_init(&svc_monitors); ++} ++ ++static void ++destroy_svc_monitors(void) ++{ ++ struct svc_monitor *svc; ++ HMAP_FOR_EACH_POP (svc, hmap_node, &svc_monitors_map) { ++ ++ } ++ ++ hmap_destroy(&svc_monitors_map); ++ ++ LIST_FOR_EACH_POP (svc, list_node, &svc_monitors) { ++ smap_destroy(&svc->options); ++ free(svc); ++ } ++} ++ ++ ++static struct svc_monitor * ++pinctrl_find_svc_monitor(uint32_t dp_key, uint32_t port_key, ++ const struct in6_addr *ip_key, uint32_t port, ++ enum svc_monitor_protocol protocol, ++ uint32_t hash) ++{ ++ struct svc_monitor *svc; ++ HMAP_FOR_EACH_WITH_HASH (svc, hmap_node, hash, &svc_monitors_map) { ++ if (svc->dp_key == dp_key ++ && svc->port_key == port_key ++ && svc->proto_port == port ++ && IN6_ARE_ADDR_EQUAL(&svc->ip, ip_key) ++ && svc->protocol == protocol) { ++ return svc; ++ } ++ } ++ return NULL; ++} ++ ++static void ++sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn, ++ const struct sbrec_service_monitor_table *svc_mon_table, ++ struct ovsdb_idl_index *sbrec_port_binding_by_name, ++ const struct sbrec_chassis *our_chassis) ++ OVS_REQUIRES(pinctrl_mutex) ++{ ++ bool changed = false; ++ struct svc_monitor *svc_mon; ++ ++ LIST_FOR_EACH (svc_mon, list_node, &svc_monitors) { ++ svc_mon->delete = true; ++ } ++ ++ const struct sbrec_service_monitor *sb_svc_mon; ++ SBREC_SERVICE_MONITOR_TABLE_FOR_EACH (sb_svc_mon, svc_mon_table) { ++ const struct sbrec_port_binding *pb ++ = lport_lookup_by_name(sbrec_port_binding_by_name, ++ sb_svc_mon->logical_port); ++ if (!pb) { ++ continue; ++ } ++ ++ if (pb->chassis != our_chassis) { ++ continue; ++ } ++ ++ struct in6_addr ip_addr; ++ ovs_be32 ip4; ++ if (ip_parse(sb_svc_mon->ip, &ip4)) { ++ ip_addr = in6_addr_mapped_ipv4(ip4); ++ } else { ++ continue; ++ } ++ ++ struct eth_addr ea; ++ bool mac_found = false; ++ for (size_t i = 0; i < pb->n_mac; i++) { ++ struct lport_addresses laddrs; ++ if (!extract_lsp_addresses(pb->mac[i], &laddrs)) { ++ continue; ++ } ++ ++ for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { ++ if (ip4 == laddrs.ipv4_addrs[j].addr) { ++ ea = laddrs.ea; ++ mac_found = true; ++ break; ++ } ++ } ++ ++ if (mac_found) { ++ break; ++ } ++ } ++ ++ if (!mac_found) { ++ continue; ++ } ++ ++ uint32_t dp_key = pb->datapath->tunnel_key; ++ uint32_t port_key = pb->tunnel_key; ++ uint32_t hash = ++ hash_bytes(&ip_addr, sizeof ip_addr, ++ hash_3words(dp_key, port_key, sb_svc_mon->port)); ++ ++ enum svc_monitor_protocol protocol; ++ if (!sb_svc_mon->protocol || strcmp(sb_svc_mon->protocol, "udp")) { ++ protocol = SVC_MON_PROTO_TCP; ++ } else { ++ protocol = SVC_MON_PROTO_UDP; ++ } ++ ++ svc_mon = pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr, ++ sb_svc_mon->port, protocol, hash); ++ ++ if (!svc_mon) { ++ svc_mon = xmalloc(sizeof *svc_mon); ++ svc_mon->dp_key = dp_key; ++ svc_mon->port_key = port_key; ++ svc_mon->proto_port = sb_svc_mon->port; ++ svc_mon->ip = ip_addr; ++ svc_mon->is_ip6 = false; ++ svc_mon->state = SVC_MON_S_INIT; ++ svc_mon->status = SVC_MON_ST_UNKNOWN; ++ svc_mon->protocol = protocol; ++ ++ smap_init(&svc_mon->options); ++ svc_mon->interval = ++ smap_get_int(&svc_mon->options, "interval", 5) * 1000; ++ svc_mon->svc_timeout = ++ smap_get_int(&svc_mon->options, "timeout", 3) * 1000; ++ svc_mon->success_count = ++ smap_get_int(&svc_mon->options, "success_count", 1); ++ svc_mon->failure_count = ++ smap_get_int(&svc_mon->options, "failure_count", 1); ++ svc_mon->n_success = 0; ++ svc_mon->n_failures = 0; ++ ++ hmap_insert(&svc_monitors_map, &svc_mon->hmap_node, hash); ++ ovs_list_push_back(&svc_monitors, &svc_mon->list_node); ++ changed = true; ++ } ++ ++ svc_mon->sb_svc_mon = sb_svc_mon; ++ svc_mon->ea = ea; ++ if (!smap_equal(&svc_mon->options, &sb_svc_mon->options)) { ++ smap_destroy(&svc_mon->options); ++ smap_clone(&svc_mon->options, &sb_svc_mon->options); ++ svc_mon->interval = ++ smap_get_int(&svc_mon->options, "interval", 5) * 1000; ++ svc_mon->svc_timeout = ++ smap_get_int(&svc_mon->options, "timeout", 3) * 1000; ++ svc_mon->success_count = ++ smap_get_int(&svc_mon->options, "success_count", 1); ++ svc_mon->failure_count = ++ smap_get_int(&svc_mon->options, "failure_count", 1); ++ changed = true; ++ } ++ ++ svc_mon->delete = false; ++ } ++ ++ struct svc_monitor *next; ++ LIST_FOR_EACH_SAFE (svc_mon, next, list_node, &svc_monitors) { ++ if (svc_mon->delete) { ++ hmap_remove(&svc_monitors_map, &svc_mon->hmap_node); ++ ovs_list_remove(&svc_mon->list_node); ++ smap_destroy(&svc_mon->options); ++ free(svc_mon); ++ changed = true; ++ } else if (ovnsb_idl_txn) { ++ /* Update the status of the service monitor. */ ++ if (svc_mon->status != SVC_MON_ST_UNKNOWN) { ++ if (svc_mon->status == SVC_MON_ST_ONLINE) { ++ sbrec_service_monitor_set_status(svc_mon->sb_svc_mon, ++ "online"); ++ } else { ++ sbrec_service_monitor_set_status(svc_mon->sb_svc_mon, ++ "offline"); ++ } ++ } ++ } ++ } ++ ++ if (changed) { ++ notify_pinctrl_handler(); ++ } ++ ++} ++ ++static uint16_t ++get_random_src_port(void) ++{ ++ uint16_t random_src_port = random_uint16(); ++ while (random_src_port < 1024) { ++ random_src_port = random_uint16(); ++ } ++ ++ return random_src_port; ++} ++ ++static void ++svc_monitor_send_tcp_health_check__(struct rconn *swconn, ++ struct svc_monitor *svc_mon, ++ uint16_t ctl_flags, ++ ovs_be32 tcp_seq, ++ ovs_be32 tcp_ack, ++ ovs_be16 tcp_src) ++{ ++ if (svc_mon->is_ip6) { ++ return; ++ } ++ ++ /* Compose a TCP-SYN packet. */ ++ uint64_t packet_stub[128 / 8]; ++ struct dp_packet packet; ++ ++ struct eth_addr eth_src; ++ eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, ð_src); ++ ovs_be32 ip4_src; ++ ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src); ++ ++ dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); ++ pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea, ++ ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip), ++ IPPROTO_TCP, 63, TCP_HEADER_LEN); ++ ++ struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th); ++ dp_packet_set_l4(&packet, th); ++ th->tcp_dst = htons(svc_mon->proto_port); ++ th->tcp_src = tcp_src; ++ ++ th->tcp_ctl = htons((5 << 12) | ctl_flags); ++ put_16aligned_be32(&th->tcp_seq, tcp_seq); ++ put_16aligned_be32(&th->tcp_ack, tcp_ack); ++ ++ th->tcp_winsz = htons(65160); ++ ++ uint32_t csum; ++ csum = packet_csum_pseudoheader(dp_packet_l3(&packet)); ++ csum = csum_continue(csum, th, dp_packet_size(&packet) - ++ ((const unsigned char *)th - ++ (const unsigned char *)dp_packet_eth(&packet))); ++ th->tcp_csum = csum_finish(csum); ++ ++ uint64_t ofpacts_stub[4096 / 8]; ++ struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); ++ enum ofp_version version = rconn_get_version(swconn); ++ put_load(svc_mon->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); ++ put_load(svc_mon->port_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); ++ put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts); ++ struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts); ++ resubmit->in_port = OFPP_CONTROLLER; ++ resubmit->table_id = OFTABLE_LOCAL_OUTPUT; ++ ++ struct ofputil_packet_out po = { ++ .packet = dp_packet_data(&packet), ++ .packet_len = dp_packet_size(&packet), ++ .buffer_id = UINT32_MAX, ++ .ofpacts = ofpacts.data, ++ .ofpacts_len = ofpacts.size, ++ }; ++ match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); ++ enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); ++ queue_msg(swconn, ofputil_encode_packet_out(&po, proto)); ++ dp_packet_uninit(&packet); ++ ofpbuf_uninit(&ofpacts); ++} ++ ++static void ++svc_monitor_send_udp_health_check(struct rconn *swconn, ++ struct svc_monitor *svc_mon, ++ ovs_be16 udp_src) ++{ ++ if (svc_mon->is_ip6) { ++ return; ++ } ++ ++ struct eth_addr eth_src; ++ eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, ð_src); ++ ovs_be32 ip4_src; ++ ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src); ++ ++ uint64_t packet_stub[128 / 8]; ++ struct dp_packet packet; ++ dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); ++ pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea, ++ ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip), ++ IPPROTO_UDP, 63, UDP_HEADER_LEN + 8); ++ ++ struct udp_header *uh = dp_packet_put_zeros(&packet, sizeof *uh); ++ dp_packet_set_l4(&packet, uh); ++ uh->udp_dst = htons(svc_mon->proto_port); ++ uh->udp_src = udp_src; ++ uh->udp_len = htons(UDP_HEADER_LEN + 8); ++ uh->udp_csum = 0; ++ dp_packet_put_zeros(&packet, 8); ++ ++ uint64_t ofpacts_stub[4096 / 8]; ++ struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); ++ enum ofp_version version = rconn_get_version(swconn); ++ put_load(svc_mon->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); ++ put_load(svc_mon->port_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); ++ put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts); ++ struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts); ++ resubmit->in_port = OFPP_CONTROLLER; ++ resubmit->table_id = OFTABLE_LOCAL_OUTPUT; ++ ++ struct ofputil_packet_out po = { ++ .packet = dp_packet_data(&packet), ++ .packet_len = dp_packet_size(&packet), ++ .buffer_id = UINT32_MAX, ++ .ofpacts = ofpacts.data, ++ .ofpacts_len = ofpacts.size, ++ }; ++ match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); ++ enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); ++ queue_msg(swconn, ofputil_encode_packet_out(&po, proto)); ++ dp_packet_uninit(&packet); ++ ofpbuf_uninit(&ofpacts); ++} ++ ++static void ++svc_monitor_send_health_check(struct rconn *swconn, ++ struct svc_monitor *svc_mon) ++{ ++ if (svc_mon->protocol == SVC_MON_PROTO_TCP) { ++ svc_mon->seq_no = random_uint32(); ++ svc_mon->tp_src = htons(get_random_src_port()); ++ svc_monitor_send_tcp_health_check__(swconn, svc_mon, ++ TCP_SYN, ++ htonl(svc_mon->seq_no), htonl(0), ++ svc_mon->tp_src); ++ } else { ++ if (!svc_mon->tp_src) { ++ svc_mon->tp_src = htons(get_random_src_port()); ++ } ++ svc_monitor_send_udp_health_check(swconn, svc_mon, svc_mon->tp_src); ++ } ++ ++ svc_mon->wait_time = time_msec() + svc_mon->svc_timeout; ++ svc_mon->state = SVC_MON_S_WAITING; ++} ++ ++static void ++svc_monitors_run(struct rconn *swconn, ++ long long int *svc_monitors_next_run_time) ++ OVS_REQUIRES(pinctrl_mutex) ++{ ++ *svc_monitors_next_run_time = LLONG_MAX; ++ struct svc_monitor *svc_mon; ++ LIST_FOR_EACH (svc_mon, list_node, &svc_monitors) { ++ char ip_[INET6_ADDRSTRLEN + 1]; ++ memset(ip_, 0, INET6_ADDRSTRLEN + 1); ++ ipv6_string_mapped(ip_, &svc_mon->ip); ++ ++ long long int current_time = time_msec(); ++ long long int next_run_time = LLONG_MAX; ++ enum svc_monitor_status old_status = svc_mon->status; ++ switch (svc_mon->state) { ++ case SVC_MON_S_INIT: ++ svc_monitor_send_health_check(swconn, svc_mon); ++ next_run_time = svc_mon->wait_time; ++ break; ++ ++ case SVC_MON_S_WAITING: ++ if (current_time > svc_mon->wait_time) { ++ if (svc_mon->protocol == SVC_MON_PROTO_TCP) { ++ svc_mon->n_failures++; ++ svc_mon->state = SVC_MON_S_OFFLINE; ++ } else { ++ svc_mon->n_success++; ++ svc_mon->state = SVC_MON_S_ONLINE; ++ } ++ svc_mon->next_send_time = current_time + svc_mon->interval; ++ next_run_time = svc_mon->next_send_time; ++ } else { ++ next_run_time = svc_mon->wait_time - current_time; ++ next_run_time = svc_mon->wait_time; ++ } ++ break; ++ ++ case SVC_MON_S_ONLINE: ++ if (svc_mon->n_success >= svc_mon->success_count) { ++ svc_mon->status = SVC_MON_ST_ONLINE; ++ svc_mon->n_success = 0; ++ } ++ if (current_time >= svc_mon->next_send_time) { ++ svc_monitor_send_health_check(swconn, svc_mon); ++ next_run_time = svc_mon->wait_time; ++ } else { ++ next_run_time = svc_mon->next_send_time; ++ } ++ break; ++ ++ case SVC_MON_S_OFFLINE: ++ if (svc_mon->n_failures >= svc_mon->failure_count) { ++ svc_mon->status = SVC_MON_ST_OFFLINE; ++ svc_mon->n_failures = 0; ++ } ++ ++ if (current_time >= svc_mon->next_send_time) { ++ svc_monitor_send_health_check(swconn, svc_mon); ++ next_run_time = svc_mon->wait_time; ++ } else { ++ next_run_time = svc_mon->next_send_time; ++ } ++ break; ++ ++ default: ++ OVS_NOT_REACHED(); ++ } ++ ++ if (*svc_monitors_next_run_time > next_run_time) { ++ *svc_monitors_next_run_time = next_run_time; ++ } ++ ++ if (old_status != svc_mon->status) { ++ /* Notify the main thread to update the status in the SB DB. */ ++ notify_pinctrl_main(); ++ } ++ } ++} ++ ++static void ++svc_monitors_wait(long long int svc_monitors_next_run_time) ++{ ++ if (!ovs_list_is_empty(&svc_monitors)) { ++ poll_timer_wait_until(svc_monitors_next_run_time); ++ } ++} ++ ++static bool ++pinctrl_handle_tcp_svc_check(struct rconn *swconn, ++ struct dp_packet *pkt_in, ++ struct svc_monitor *svc_mon) ++{ ++ struct tcp_header *th = dp_packet_l4(pkt_in); ++ ++ if (!th) { ++ return false; ++ } ++ ++ uint32_t tcp_seq = ntohl(get_16aligned_be32(&th->tcp_seq)); ++ uint32_t tcp_ack = ntohl(get_16aligned_be32(&th->tcp_ack)); ++ ++ if (th->tcp_dst != svc_mon->tp_src) { ++ return false; ++ } ++ ++ if (tcp_ack != (svc_mon->seq_no + 1)) { ++ return false; ++ } ++ ++ /* Check for SYN flag and Ack flag. */ ++ if ((TCP_FLAGS(th->tcp_ctl) & (TCP_SYN | TCP_ACK)) ++ == (TCP_SYN | TCP_ACK)) { ++ svc_mon->n_success++; ++ svc_mon->state = SVC_MON_S_ONLINE; ++ ++ /* Send RST-ACK packet. */ ++ svc_monitor_send_tcp_health_check__(swconn, svc_mon, TCP_RST | TCP_ACK, ++ htonl(tcp_ack + 1), ++ htonl(tcp_seq + 1), th->tcp_dst); ++ /* Calculate next_send_time. */ ++ svc_mon->next_send_time = time_msec() + svc_mon->interval; ++ return true; ++ } ++ ++ /* Check if RST flag is set. */ ++ if (TCP_FLAGS(th->tcp_ctl) & TCP_RST) { ++ svc_mon->n_failures++; ++ svc_mon->state = SVC_MON_S_OFFLINE; ++ ++ /* Calculate next_send_time. */ ++ svc_mon->next_send_time = time_msec() + svc_mon->interval; ++ return false; ++ } ++ ++ return false; ++} ++ ++static void ++pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow, ++ struct dp_packet *pkt_in, const struct match *md) ++{ ++ uint32_t dp_key = ntohll(md->flow.metadata); ++ uint32_t port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0]; ++ struct in6_addr ip_addr; ++ struct eth_header *in_eth = dp_packet_data(pkt_in); ++ struct ip_header *in_ip = dp_packet_l3(pkt_in); ++ ++ if (in_ip->ip_proto != IPPROTO_TCP && in_ip->ip_proto != IPPROTO_ICMP) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, ++ "handle service check: Unsupported protocol - [%x]", ++ in_ip->ip_proto); ++ return; ++ } ++ ++ uint16_t in_ip_len = ntohs(in_ip->ip_tot_len); ++ if (in_ip_len < IP_HEADER_LEN) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, ++ "IP packet with invalid length (%u)", ++ in_ip_len); ++ return; ++ } ++ ++ if (in_eth->eth_type == htons(ETH_TYPE_IP)) { ++ ip_addr = in6_addr_mapped_ipv4(ip_flow->nw_src); ++ } else { ++ ip_addr = ip_flow->ipv6_dst; ++ } ++ ++ if (in_ip->ip_proto == IPPROTO_TCP) { ++ uint32_t hash = ++ hash_bytes(&ip_addr, sizeof ip_addr, ++ hash_3words(dp_key, port_key, ntohs(ip_flow->tp_src))); ++ ++ struct svc_monitor *svc_mon = ++ pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr, ++ ntohs(ip_flow->tp_src), ++ SVC_MON_PROTO_TCP, hash); ++ if (!svc_mon) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "handle service check: Service monitor " ++ "not found"); ++ return; ++ } ++ pinctrl_handle_tcp_svc_check(swconn, pkt_in, svc_mon); ++ } else { ++ /* It's ICMP packet. */ ++ struct icmp_header *ih = dp_packet_l4(pkt_in); ++ if (!ih) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "ICMPv4 packet with invalid header"); ++ return; ++ } ++ ++ if (ih->icmp_type != ICMP4_DST_UNREACH || ih->icmp_code != 3) { ++ return; ++ } ++ ++ const char *end = ++ (char *)dp_packet_l4(pkt_in) + dp_packet_l4_size(pkt_in); ++ ++ const struct ip_header *orig_ip_hr = ++ dp_packet_get_icmp_payload(pkt_in); ++ if (!orig_ip_hr) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "Original IP datagram not present in " ++ "ICMP packet"); ++ return; ++ } ++ ++ if (ntohs(orig_ip_hr->ip_tot_len) != ++ (IP_HEADER_LEN + UDP_HEADER_LEN + 8)) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "Invalid original IP datagram length present " ++ "in ICMP packet"); ++ return; ++ } ++ ++ struct udp_header *orig_uh = (struct udp_header *) (orig_ip_hr + 1); ++ if ((char *)orig_uh >= end) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "Invalid UDP header in the original " ++ "IP datagram"); ++ return; ++ } ++ ++ uint32_t hash = ++ hash_bytes(&ip_addr, sizeof ip_addr, ++ hash_3words(dp_key, port_key, ntohs(orig_uh->udp_dst))); ++ ++ struct svc_monitor *svc_mon = ++ pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr, ++ ntohs(orig_uh->udp_dst), ++ SVC_MON_PROTO_UDP, hash); ++ if (!svc_mon) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "handle service check: Service monitor not " ++ "found for ICMP packet"); ++ return; ++ } ++ ++ if (orig_uh->udp_src != svc_mon->tp_src) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ VLOG_WARN_RL(&rl, "handle service check: UDP src port doesn't " ++ "match in the Original IP datagram of ICMP packet"); ++ return; ++ } ++ ++ /* The UDP service monitor is down. */ ++ svc_mon->n_failures++; ++ svc_mon->state = SVC_MON_S_OFFLINE; ++ ++ /* Calculate next_send_time. */ ++ svc_mon->next_send_time = time_msec() + svc_mon->interval; ++ } ++} +diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h +index fcfce6bcf..208272442 100644 +--- a/ovn/controller/pinctrl.h ++++ b/ovn/controller/pinctrl.h +@@ -30,6 +30,7 @@ struct ovsrec_bridge; + struct sbrec_chassis; + struct sbrec_dns_table; + struct sbrec_controller_event_table; ++struct sbrec_service_monitor_table; + + void pinctrl_init(void); + void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, +@@ -42,6 +43,7 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn, + struct ovsdb_idl_index *sbrec_ip_multicast_opts, + const struct sbrec_dns_table *, + const struct sbrec_controller_event_table *, ++ const struct sbrec_service_monitor_table *, + const struct ovsrec_bridge *, const struct sbrec_chassis *, + const struct hmap *local_datapaths, + const struct sset *active_tunnels); +diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml +index 9515c7790..956a10362 100644 +--- a/ovn/northd/ovn-northd.8.xml ++++ b/ovn/northd/ovn-northd.8.xml +@@ -1009,6 +1009,16 @@ output; +

        + +
          ++
        • ++ A priorirty-110 flow with the match ++ eth.src == E for all logical switch ++ datapaths and applies the action handle_svc_check(inport). ++ Where E is the service monitor mac defined in the ++ colum of table. ++
        • ++ +
        • + A priority-100 flow that punts all IGMP packets to + ovn-controller if IGMP snooping is enabled on the +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index 1ded5dd9b..ec8f9a70f 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -6182,6 +6182,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + } + } + ++ char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac); + /* Ingress table 17: Destination lookup, broadcast and multicast handling + * (priority 70 - 100). */ + HMAP_FOR_EACH (od, key_node, datapaths) { +@@ -6189,6 +6190,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + continue; + } + ++ ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 110, svc_check_match, ++ "handle_svc_check(inport);"); ++ + struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw; + + if (mcast_sw_info->enabled) { +@@ -6248,6 +6252,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 70, "eth.mcast", + "outport = \""MC_FLOOD"\"; output;"); + } ++ free(svc_check_match); + + /* Ingress table 17: Add IP multicast flows learnt from IGMP + * (priority 90). */ +@@ -10521,6 +10526,11 @@ static const char *rbac_mac_binding_auth[] = + static const char *rbac_mac_binding_update[] = + {"logical_port", "ip", "mac", "datapath"}; + ++static const char *rbac_svc_monitor_auth[] = ++ {""}; ++static const char *rbac_svc_monitor_auth_update[] = ++ {"status"}; ++ + static struct rbac_perm_cfg { + const char *table; + const char **auth; +@@ -10562,6 +10572,14 @@ static struct rbac_perm_cfg { + .update = rbac_mac_binding_update, + .n_update = ARRAY_SIZE(rbac_mac_binding_update), + .row = NULL ++ },{ ++ .table = "Service_Monitor", ++ .auth = rbac_svc_monitor_auth, ++ .n_auth = ARRAY_SIZE(rbac_svc_monitor_auth), ++ .insdel = false, ++ .update = rbac_svc_monitor_auth_update, ++ .n_update = ARRAY_SIZE(rbac_svc_monitor_auth_update), ++ .row = NULL + },{ + .table = NULL, + .auth = NULL, +diff --git a/tests/ovn.at b/tests/ovn.at +index f68ddcd33..e2565f274 100644 +--- a/tests/ovn.at ++++ b/tests/ovn.at +@@ -16932,5 +16932,124 @@ as hv4 ovs-ofctl show br-phys + as hv4 ovs-appctl fdb/show br-phys + + OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) ++AT_CLEANUP ++ ++AT_SETUP([ovn -- Load balancer health checks]) ++AT_KEYWORDS([lb]) ++ovn_start ++ ++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 -- add-port br-int hv1-vif1 -- \ ++ set interface hv1-vif1 external-ids:iface-id=sw0-p1 \ ++ options:tx_pcap=hv1/vif1-tx.pcap \ ++ options:rxq_pcap=hv1/vif1-rx.pcap \ ++ ofport-request=1 ++ovs-vsctl -- add-port br-int hv1-vif2 -- \ ++ set interface hv1-vif2 external-ids:iface-id=sw0-p2 \ ++ options:tx_pcap=hv1/vif2-tx.pcap \ ++ options:rxq_pcap=hv1/vif2-rx.pcap \ ++ ofport-request=2 ++ ++sim_add hv2 ++as hv2 ++ovs-vsctl add-br br-phys ++ovn_attach n1 br-phys 192.168.0.2 ++ovs-vsctl -- add-port br-int hv2-vif1 -- \ ++ set interface hv2-vif1 external-ids:iface-id=sw1-p1 \ ++ options:tx_pcap=hv2/vif1-tx.pcap \ ++ options:rxq_pcap=hv2/vif1-rx.pcap \ ++ ofport-request=1 ++ ++ovn-nbctl ls-add sw0 ++ ++ovn-nbctl lsp-add sw0 sw0-p1 ++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3" ++ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3" ++ ++ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4" ++ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4" ++ ++# Create the second logical switch with one port ++ovn-nbctl ls-add sw1 ++ovn-nbctl lsp-add sw1 sw1-p1 ++ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3" ++ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3" + ++# Create a logical router and attach both logical switches ++ovn-nbctl lr-add lr0 ++ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 ++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 lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 ++ovn-nbctl lsp-add sw1 sw1-lr0 ++ovn-nbctl lsp-set-type sw1-lr0 router ++ovn-nbctl lsp-set-addresses sw1-lr0 router ++ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 ++ ++ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 ++ ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2 ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2 ++ ++ovn-nbctl --wait=sb -- --id=@hc create \ ++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \ ++health_check @hc ++ ++ovn-nbctl --wait=sb ls-lb-add sw0 lb1 ++ovn-nbctl --wait=sb ls-lb-add sw1 lb1 ++ovn-nbctl --wait=sb lr-lb-add lr0 lb1 ++ ++OVN_POPULATE_ARP ++ovn-nbctl --wait=hv sync ++ ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \ ++service_monitor | sed '/^$/d' | wc -l`]) ++ ++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);) ++]) ++ ++# get the svc monitor mac. ++svc_mon_src_mac=`ovn-nbctl get NB_Global . options:svc_monitor_mac | \ ++sed s/":"//g | sed s/\"//g` ++ ++OVS_WAIT_UNTIL( ++ [test 1 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \ ++grep "505400000003${svc_mon_src_mac}" | wc -l`] ++) ++ ++OVS_WAIT_UNTIL( ++ [test 1 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \ ++grep "405400000003${svc_mon_src_mac}" | wc -l`] ++) ++ ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \ ++service_monitor | grep offline | wc -l`]) ++ ++OVS_WAIT_UNTIL( ++ [test 2 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \ ++grep "505400000003${svc_mon_src_mac}" | wc -l`] ++) ++ ++OVS_WAIT_UNTIL( ++ [test 2 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \ ++grep "405400000003${svc_mon_src_mac}" | wc -l`] ++) ++ ++ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \ ++| grep priority=120 > lflows.txt ++AT_CHECK([cat lflows.txt], [0], [dnl ++ table=10(ls_in_stateful ), priority=120 , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;) ++]) ++ ++OVN_CLEANUP([hv1], [hv2]) + AT_CLEANUP +diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at +index 64bf5ec63..c8fa6f03f 100644 +--- a/tests/system-common-macros.at ++++ b/tests/system-common-macros.at +@@ -267,6 +267,7 @@ m4_define([OVS_CHECK_FIREWALL], + # + m4_define([OVS_START_L7], + [PIDFILE=$(mktemp $2XXX.pid) ++ echo $PIDFILE > l7_pid_file + NETNS_DAEMONIZE([$1], [[$PYTHON $srcdir/test-l7.py $2]], [$PIDFILE]) + + dnl netstat doesn't print http over IPv6 as "http6"; drop the number. +diff --git a/tests/system-ovn.at b/tests/system-ovn.at +index b3f90aae2..7d1c65d85 100644 +--- a/tests/system-ovn.at ++++ b/tests/system-ovn.at +@@ -2523,3 +2523,183 @@ as + OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d + /connection dropped.*/d"]) + AT_CLEANUP ++ ++AT_SETUP([ovn -- Load balancer health checks]) ++AT_KEYWORDS([lb]) ++ovn_start ++ ++OVS_TRAFFIC_VSWITCHD_START() ++ADD_BR([br-int]) ++ ++# Set external-ids in br-int needed for ovn-controller ++ovs-vsctl \ ++ -- set Open_vSwitch . external-ids:system-id=hv1 \ ++ -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ ++ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ ++ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ ++ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true ++ ++# Start ovn-controller ++start_daemon ovn-controller ++ ++ovn-nbctl ls-add sw0 ++ ++ovn-nbctl lsp-add sw0 sw0-p1 ++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3" ++ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3" ++ ++ovn-nbctl lsp-add sw0 sw0-p2 ++ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4" ++ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4" ++ ++# Create the second logical switch with one port ++ovn-nbctl ls-add sw1 ++ovn-nbctl lsp-add sw1 sw1-p1 ++ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3" ++ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3" ++ ++# Create a logical router and attach both logical switches ++ovn-nbctl lr-add lr0 ++ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 ++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 lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 ++ovn-nbctl lsp-add sw1 sw1-lr0 ++ovn-nbctl lsp-set-type sw1-lr0 router ++ovn-nbctl lsp-set-addresses sw1-lr0 router ++ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 ++ ++ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 ++ ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2 ++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2 ++ ++ovn-nbctl --wait=sb -- --id=@hc create \ ++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \ ++health_check @hc ++ ++ovn-nbctl --wait=sb ls-lb-add sw0 lb1 ++ovn-nbctl --wait=sb ls-lb-add sw1 lb1 ++ovn-nbctl --wait=sb lr-lb-add lr0 lb1 ++ ++OVN_POPULATE_ARP ++ovn-nbctl --wait=hv sync ++ ++ADD_NAMESPACES(sw0-p1) ++ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \ ++ "10.0.0.1") ++ ++ADD_NAMESPACES(sw1-p1) ++ADD_VETH(sw1-p1, sw1-p1, br-int, "20.0.0.3/24", "40:54:00:00:00:03", \ ++ "20.0.0.1") ++ ++ADD_NAMESPACES(sw0-p2) ++ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \ ++ "10.0.0.1") ++ ++# Wait until all the services are set to offline. ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \ ++service_monitor | sed '/^$/d' | grep offline | wc -l`]) ++ ++# Start webservers in 'sw0-p1' and 'sw1-p1'. ++OVS_START_L7([sw0-p1], [http]) ++sw0_p1_pid_file=`cat l7_pid_file` ++OVS_START_L7([sw1-p1], [http]) ++ ++# Wait until the services are set to online. ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \ ++service_monitor | sed '/^$/d' | grep online | wc -l`]) ++ ++OVS_WAIT_UNTIL( ++ [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt ++ test 1 = `cat lflows.txt | grep "ct_lb(10.0.0.3:80,20.0.0.3:80)" | wc -l`] ++) ++ ++# From sw0-p2 send traffic to vip - 10.0.0.10 ++for i in `seq 1 20`; do ++ echo Request $i ++ ovn-sbctl list service_monitor ++ NS_CHECK_EXEC([sw0-p2], [wget 10.0.0.10 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) ++done ++ ++dnl Each server should have at least one connection. ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=10.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,protoinfo=(state=) ++tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=20.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,protoinfo=(state=) ++]) ++ ++# Stop webserer in sw0-p1 ++kill `cat $sw0_p1_pid_file` ++ ++# Wait until service_monitor for sw0-p1 is set to offline ++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns status find \ ++service_monitor logical_port=sw0-p1 | sed '/^$/d' | grep offline | wc -l`]) ++ ++OVS_WAIT_UNTIL( ++ [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt ++ test 1 = `cat lflows.txt | grep "ct_lb(20.0.0.3:80)" | wc -l`] ++) ++ ++ovs-appctl dpctl/flush-conntrack ++# From sw0-p2 send traffic to vip - 10.0.0.10 ++for i in `seq 1 20`; do ++ echo Request $i ++ NS_CHECK_EXEC([sw0-p2], [wget 10.0.0.10 -t 5 -T 1 --retry-connrefused -v -o wget$i.log]) ++done ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \ ++sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl ++tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=,dport=),reply=(src=20.0.0.3,dst=10.0.0.4,sport=,dport=),zone=,protoinfo=(state=) ++]) ++ ++# Create udp load balancer. ++ovn-nbctl lb-add lb2 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 udp ++lb_udp=`ovn-nbctl lb-list | grep udp | awk '{print $1}'` ++ ++echo "lb udp uuid = $lb_udp" ++ ++ovn-nbctl list load_balancer ++ ++ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2 ++ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2 ++ ++ovn-nbctl --wait=sb -- --id=@hc create \ ++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer $lb_udp \ ++health_check @hc ++ ++ovn-nbctl --wait=sb ls-lb-add sw0 lb2 ++ovn-nbctl --wait=sb ls-lb-add sw1 lb2 ++ovn-nbctl --wait=sb lr-lb-add lr0 lb2 ++ ++sleep 10 ++ ++ovn-nbctl list load_balancer ++echo "*******Next is health check*******" ++ovn-nbctl list Load_Balancer_Health_Check ++echo "********************" ++ovn-sbctl list service_monitor ++ ++# Wait until udp service_monitor are set to offline ++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \ ++service_monitor protocol=udp | sed '/^$/d' | grep offline | wc -l`]) ++ ++OVS_APP_EXIT_AND_WAIT([ovn-controller]) ++ ++as ovn-sb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as ovn-nb ++OVS_APP_EXIT_AND_WAIT([ovsdb-server]) ++ ++as northd ++OVS_APP_EXIT_AND_WAIT([ovn-northd]) ++ ++as ++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d ++/connection dropped.*/d"]) ++ ++AT_CLEANUP +-- +2.23.0 + diff --git a/SOURCES/0012-northd-Remove-misleading-warning-log-message.patch b/SOURCES/0012-northd-Remove-misleading-warning-log-message.patch new file mode 100644 index 0000000..c94e4d3 --- /dev/null +++ b/SOURCES/0012-northd-Remove-misleading-warning-log-message.patch @@ -0,0 +1,33 @@ +From 061eee986e8343df199c35d813ac14def610a92b Mon Sep 17 00:00:00 2001 +From: Russell Bryant +Date: Sun, 1 Dec 2019 22:19:04 -0500 +Subject: [PATCH 12/12] northd: Remove misleading warning log message + +While debugging an ovn-kubernetes cluster, I spotted several +"Duplicate MAC set" warning messages in the ovn-northd log. It looks +like this message was emitted from this code path by mistake, where +it correctly avoided assigning a duplicate MAC address. This patch +turns off the warning for that case. + +Signed-off-by: Russell Bryant +Acked-by: Numan Siddique +--- + ovn/northd/ovn-northd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c +index ec8f9a70f..55734b090 100644 +--- a/ovn/northd/ovn-northd.c ++++ b/ovn/northd/ovn-northd.c +@@ -1394,7 +1394,7 @@ ipam_get_unused_mac(ovs_be32 ip) + mac_addr_suffix = ((base_addr + i) % (MAC_ADDR_SPACE - 1)) + 1; + mac64 = eth_addr_to_uint64(mac_prefix) | mac_addr_suffix; + eth_addr_from_uint64(mac64, &mac); +- if (!ipam_is_duplicate_mac(&mac, mac64, true)) { ++ if (!ipam_is_duplicate_mac(&mac, mac64, false)) { + break; + } + } +-- +2.23.0 + diff --git a/SOURCES/arm64-armv8a-linuxapp-gcc-config b/SOURCES/arm64-armv8a-linuxapp-gcc-config new file mode 100644 index 0000000..5813d7a --- /dev/null +++ b/SOURCES/arm64-armv8a-linuxapp-gcc-config @@ -0,0 +1,540 @@ +# -*- cfg-sha: 9fc8b53ccd53cc8b64391f6252e1dba558ae660a73a72f10dcadff2ca5462243 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2015 Cavium, Inc +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Cavium, Inc +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2016 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2017 Intel Corporation +# RTE_EXEC_ENV values are the directories in mk/exec-env/ +CONFIG_RTE_EXEC_ENV="linuxapp" +# RTE_ARCH values are architecture we compile for. directories in mk/arch/ +CONFIG_RTE_ARCH="arm64" +# machine can define specific variables or action for a specific board +# RTE_MACHINE values are architecture we compile for. directories in mk/machine/ +CONFIG_RTE_MACHINE="armv8a" +# The compiler we use. +# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/ +CONFIG_RTE_TOOLCHAIN="gcc" +# Use intrinsics or assembly code for key routines +CONFIG_RTE_FORCE_INTRINSICS=y +# Machine forces strict alignment constraints. +CONFIG_RTE_ARCH_STRICT_ALIGN=n +# Compile to share library +CONFIG_RTE_BUILD_SHARED_LIB=n +# Use newest code breaking previous ABI +CONFIG_RTE_NEXT_ABI=n +# Major ABI to overwrite library specific LIBABIVER +CONFIG_RTE_MAJOR_ABI= +# Machine's cache line size +CONFIG_RTE_CACHE_LINE_SIZE=128 +# Memory model +CONFIG_RTE_USE_C11_MEM_MODEL=y +# Compile Environment Abstraction Layer +CONFIG_RTE_LIBRTE_EAL=y +CONFIG_RTE_MAX_LCORE=256 +CONFIG_RTE_MAX_NUMA_NODES=8 +CONFIG_RTE_MAX_HEAPS=32 +CONFIG_RTE_MAX_MEMSEG_LISTS=64 +# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages +# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller +CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192 +CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768 +# a "type" is a combination of page size and NUMA node. total number of memseg +# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split +# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or +# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of +# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller +CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768 +CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072 +# global maximum usable amount of VA, in megabytes +CONFIG_RTE_MAX_MEM_MB=524288 +CONFIG_RTE_MAX_MEMZONE=2560 +CONFIG_RTE_MAX_TAILQ=32 +CONFIG_RTE_ENABLE_ASSERT=n +CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO +CONFIG_RTE_LOG_HISTORY=256 +CONFIG_RTE_BACKTRACE=y +CONFIG_RTE_LIBEAL_USE_HPET=n +CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n +CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n +CONFIG_RTE_EAL_IGB_UIO=n +CONFIG_RTE_EAL_VFIO=y +CONFIG_RTE_MAX_VFIO_GROUPS=64 +CONFIG_RTE_MAX_VFIO_CONTAINERS=64 +CONFIG_RTE_MALLOC_DEBUG=n +CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y +CONFIG_RTE_USE_LIBBSD=n +# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing. +# AVX512 is marked as experimental for now, will enable it after enough +# field test and possible optimization. +CONFIG_RTE_ENABLE_AVX=y +CONFIG_RTE_ENABLE_AVX512=n +# Default driver path (or "" to disable) +CONFIG_RTE_EAL_PMD_PATH="" +# Compile Environment Abstraction Layer to support Vmware TSC map +CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=y +# Compile architecture we compile for. PCI library +CONFIG_RTE_LIBRTE_PCI=y +# Compile architecture we compile for. argument parser library +CONFIG_RTE_LIBRTE_KVARGS=y +# Compile generic ethernet library +CONFIG_RTE_LIBRTE_ETHER=y +CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n +CONFIG_RTE_MAX_ETHPORTS=32 +CONFIG_RTE_MAX_QUEUES_PER_PORT=1024 +CONFIG_RTE_LIBRTE_IEEE1588=n +CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16 +CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y +CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n +# Turn off Tx preparation stage +# Warning: rte_eth_tx_prepare() can be safely disabled only if using a +# driver which do not implement any Tx preparation. +CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n +# Common libraries, before Bus/PMDs +CONFIG_RTE_LIBRTE_COMMON_DPAAX=n +# Compile architecture we compile for. Intel FPGA bus +CONFIG_RTE_LIBRTE_IFPGA_BUS=n +# Compile PCI bus driver +CONFIG_RTE_LIBRTE_PCI_BUS=y +# Compile architecture we compile for. vdev bus +CONFIG_RTE_LIBRTE_VDEV_BUS=y +# Compile ARK PMD +CONFIG_RTE_LIBRTE_ARK_PMD=n +CONFIG_RTE_LIBRTE_ARK_PAD_TX=y +CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n +# Compile Aquantia Atlantic PMD driver +CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n +# Compile AMD PMD +CONFIG_RTE_LIBRTE_AXGBE_PMD=n +CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n +# Compile burst-oriented Broadcom PMD driver +CONFIG_RTE_LIBRTE_BNX2X_PMD=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n +CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n +# Compile burst-oriented Broadcom BNXT PMD driver +CONFIG_RTE_LIBRTE_BNXT_PMD=n +# Compile burst-oriented Chelsio Terminator (CXGBE) PMD +CONFIG_RTE_LIBRTE_CXGBE_PMD=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n +CONFIG_RTE_LIBRTE_CXGBE_TPUT=y +# NXP DPAA Bus +CONFIG_RTE_LIBRTE_DPAA_BUS=n +CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n +CONFIG_RTE_LIBRTE_DPAA_PMD=n +CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n +# Compile NXP DPAA2 FSL-MC Bus +CONFIG_RTE_LIBRTE_FSLMC_BUS=n +# Compile Support Libraries for NXP DPAA2 +CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n +CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y +# Compile burst-oriented NXP DPAA2 PMD driver +CONFIG_RTE_LIBRTE_DPAA2_PMD=n +CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n +# Compile NXP ENETC PMD Driver +CONFIG_RTE_LIBRTE_ENETC_PMD=n +# Compile burst-oriented Amazon ENA PMD driver +CONFIG_RTE_LIBRTE_ENA_PMD=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n +# Compile burst-oriented Cisco ENIC PMD driver +CONFIG_RTE_LIBRTE_ENIC_PMD=n +# Compile burst-oriented IGB & EM PMD drivers +CONFIG_RTE_LIBRTE_EM_PMD=n +CONFIG_RTE_LIBRTE_IGB_PMD=y +CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n +CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n +CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n +# Compile burst-oriented IXGBE PMD driver +CONFIG_RTE_LIBRTE_IXGBE_PMD=y +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n +CONFIG_RTE_IXGBE_INC_VECTOR=y +CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n +# Compile burst-oriented I40E PMD driver +CONFIG_RTE_LIBRTE_I40E_PMD=y +CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n +CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n +CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y +CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y +CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n +CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64 +CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4 +# Compile burst-oriented FM10K PMD +CONFIG_RTE_LIBRTE_FM10K_PMD=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y +CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y +# Compile burst-oriented AVF PMD driver +CONFIG_RTE_LIBRTE_AVF_PMD=n +CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y +CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n +CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n +CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n +# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD +CONFIG_RTE_LIBRTE_MLX4_PMD=n +CONFIG_RTE_LIBRTE_MLX4_DEBUG=n +CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=n +# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield +# (MLX5) PMD +CONFIG_RTE_LIBRTE_MLX5_PMD=n +CONFIG_RTE_LIBRTE_MLX5_DEBUG=n +CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=n +# Compile burst-oriented Netronome NFP PMD driver +CONFIG_RTE_LIBRTE_NFP_PMD=n +CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n +CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n +# QLogic 10G/25G/40G/50G/100G PMD +CONFIG_RTE_LIBRTE_QEDE_PMD=n +CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n +#Provides abs path/name of architecture we compile for. firmware file. +#Empty string denotes driver will use default firmware +CONFIG_RTE_LIBRTE_QEDE_FW="" +# Compile burst-oriented Solarflare libefx-based PMD +CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n +CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n +# Compile software PMD backed by SZEDATA2 device +CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n +# Compile burst-oriented Cavium Thunderx NICVF PMD driver +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n +# Compile burst-oriented Cavium LiquidIO PMD driver +CONFIG_RTE_LIBRTE_LIO_PMD=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n +# Compile burst-oriented Cavium OCTEONTX network PMD driver +CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n +# Compile WRS accelerated virtual port (AVP) guest PMD driver +CONFIG_RTE_LIBRTE_AVP_PMD=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n +# Compile burst-oriented VIRTIO PMD driver +CONFIG_RTE_LIBRTE_VIRTIO_PMD=y +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n +# Compile virtio device emulation inside virtio PMD driver +CONFIG_RTE_VIRTIO_USER=n +# Compile burst-oriented VMXNET3 PMD driver +CONFIG_RTE_LIBRTE_VMXNET3_PMD=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n +# Compile software PMD backed by AF_PACKET sockets (Linux only) +CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n +# Compile link bonding PMD library +CONFIG_RTE_LIBRTE_PMD_BOND=n +CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n +CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n +# Compile fail-safe PMD +CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y +# Compile Marvell PMD driver +CONFIG_RTE_LIBRTE_MVPP2_PMD=n +# Compile Marvell MVNETA PMD driver +CONFIG_RTE_LIBRTE_MVNETA_PMD=n +# Compile support for VMBus library +CONFIG_RTE_LIBRTE_VMBUS=n +# Compile native PMD for Hyper-V/Azure +CONFIG_RTE_LIBRTE_NETVSC_PMD=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n +# Compile virtual device driver for NetVSC on Hyper-V/Azure +CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=n +# Compile null PMD +CONFIG_RTE_LIBRTE_PMD_NULL=n +# Compile software PMD backed by PCAP files +CONFIG_RTE_LIBRTE_PMD_PCAP=n +# Compile example software rings based PMD +CONFIG_RTE_LIBRTE_PMD_RING=y +CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16 +CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 +# Compile SOFTNIC PMD +CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n +# Compile architecture we compile for. TAP PMD +# It is enabled by default for Linux only. +CONFIG_RTE_LIBRTE_PMD_TAP=y +# Do prefetch of packet data within PMD driver receive function +CONFIG_RTE_PMD_PACKET_PREFETCH=y +# Compile generic wireless base band device library +# EXPERIMENTAL: API may change without prior notice +CONFIG_RTE_LIBRTE_BBDEV=n +CONFIG_RTE_BBDEV_MAX_DEVS=128 +CONFIG_RTE_BBDEV_OFFLOAD_COST=n +# Compile PMD for NULL bbdev device +CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y +# Compile PMD for turbo software bbdev device +CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n +# Compile generic crypto device library +CONFIG_RTE_LIBRTE_CRYPTODEV=n +CONFIG_RTE_CRYPTO_MAX_DEVS=64 +# Compile PMD for ARMv8 Crypto device +CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n +CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n +# Compile NXP CAAM JR crypto Driver +CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n +CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n +# Compile NXP DPAA2 crypto sec driver for CAAM HW +CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n +# NXP DPAA caam - crypto driver +CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n +CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4 +# Compile PMD for Cavium OCTEON TX crypto device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y +# Compile PMD for QuickAssist based devices - see docs for details +CONFIG_RTE_LIBRTE_PMD_QAT=n +CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n +# Max. number of QuickAssist devices, which can be detected and attached +CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48 +CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16 +CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536 +# Compile PMD for virtio crypto devices +CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n +# Number of maximum virtio crypto devices +CONFIG_RTE_MAX_VIRTIO_CRYPTO=32 +# Compile PMD for AESNI backed device +CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n +# Compile PMD for Software backed device +CONFIG_RTE_LIBRTE_PMD_OPENSSL=n +# Compile PMD for AESNI GCM device +CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n +# Compile PMD for SNOW 3G device +CONFIG_RTE_LIBRTE_PMD_SNOW3G=n +CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n +# Compile PMD for KASUMI device +CONFIG_RTE_LIBRTE_PMD_KASUMI=n +# Compile PMD for ZUC device +CONFIG_RTE_LIBRTE_PMD_ZUC=n +# Compile PMD for Crypto Scheduler device +CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n +# Compile PMD for NULL Crypto device +CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n +# Compile PMD for AMD CCP crypto device +CONFIG_RTE_LIBRTE_PMD_CCP=n +# Compile PMD for Marvell Crypto device +CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n +# Compile generic security library +CONFIG_RTE_LIBRTE_SECURITY=n +# Compile generic compression device library +CONFIG_RTE_LIBRTE_COMPRESSDEV=n +CONFIG_RTE_COMPRESS_MAX_DEVS=64 +# Compile compressdev unit test +CONFIG_RTE_COMPRESSDEV_TEST=n +# Compile PMD for Octeontx ZIPVF compression device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n +# Compile PMD for ISA-L compression device +CONFIG_RTE_LIBRTE_PMD_ISAL=n +# Compile PMD for ZLIB compression device +CONFIG_RTE_LIBRTE_PMD_ZLIB=n +# Compile generic event device library +CONFIG_RTE_LIBRTE_EVENTDEV=n +CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n +CONFIG_RTE_EVENT_MAX_DEVS=16 +CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64 +CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32 +CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024 +CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32 +CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32 +# Compile PMD for skeleton event device +CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n +CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n +# Compile PMD for software event device +CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n +# Compile PMD for distributed software event device +CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n +# Compile PMD for octeontx sso event device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n +# Compile PMD for OPDL event device +CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n +# Compile PMD for NXP DPAA event device +CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n +# Compile PMD for NXP DPAA2 event device +CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n +# Compile raw device support +# EXPERIMENTAL: API may change without prior notice +CONFIG_RTE_LIBRTE_RAWDEV=n +CONFIG_RTE_RAWDEV_MAX_DEVS=10 +CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n +# Compile PMD for NXP DPAA2 CMDIF raw device +CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n +# Compile PMD for NXP DPAA2 QDMA raw device +CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n +# Compile PMD for Intel FPGA raw device +CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n +# Compile librte_ring +CONFIG_RTE_LIBRTE_RING=y +# Compile librte_mempool +CONFIG_RTE_LIBRTE_MEMPOOL=y +CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512 +CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n +# Compile Mempool drivers +CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y +CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64 +CONFIG_RTE_DRIVER_MEMPOOL_RING=y +CONFIG_RTE_DRIVER_MEMPOOL_STACK=y +# Compile PMD for octeontx fpa mempool device +CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n +# Compile librte_mbuf +CONFIG_RTE_LIBRTE_MBUF=y +CONFIG_RTE_LIBRTE_MBUF_DEBUG=n +CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc" +CONFIG_RTE_MBUF_REFCNT_ATOMIC=y +CONFIG_RTE_PKTMBUF_HEADROOM=128 +# Compile librte_timer +CONFIG_RTE_LIBRTE_TIMER=n +CONFIG_RTE_LIBRTE_TIMER_DEBUG=n +# Compile librte_cfgfile +CONFIG_RTE_LIBRTE_CFGFILE=n +# Compile librte_cmdline +CONFIG_RTE_LIBRTE_CMDLINE=y +CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n +# Compile librte_hash +CONFIG_RTE_LIBRTE_HASH=y +CONFIG_RTE_LIBRTE_HASH_DEBUG=n +# Compile librte_efd +CONFIG_RTE_LIBRTE_EFD=n +# Compile librte_member +CONFIG_RTE_LIBRTE_MEMBER=y +# Compile librte_jobstats +CONFIG_RTE_LIBRTE_JOBSTATS=n +# Compile architecture we compile for. device metrics library +CONFIG_RTE_LIBRTE_METRICS=y +# Compile architecture we compile for. bitrate statistics library +CONFIG_RTE_LIBRTE_BITRATE=y +# Compile architecture we compile for. latency statistics library +CONFIG_RTE_LIBRTE_LATENCY_STATS=y +# Compile librte_telemetry +CONFIG_RTE_LIBRTE_TELEMETRY=n +# Compile librte_lpm +CONFIG_RTE_LIBRTE_LPM=n +CONFIG_RTE_LIBRTE_LPM_DEBUG=n +# Compile librte_acl +CONFIG_RTE_LIBRTE_ACL=n +CONFIG_RTE_LIBRTE_ACL_DEBUG=n +# Compile librte_power +CONFIG_RTE_LIBRTE_POWER=n +CONFIG_RTE_LIBRTE_POWER_DEBUG=n +CONFIG_RTE_MAX_LCORE_FREQS=64 +# Compile librte_net +CONFIG_RTE_LIBRTE_NET=y +# Compile librte_ip_frag +CONFIG_RTE_LIBRTE_IP_FRAG=y +CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n +CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4 +CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n +# Compile GRO library +CONFIG_RTE_LIBRTE_GRO=y +# Compile GSO library +CONFIG_RTE_LIBRTE_GSO=y +# Compile librte_meter +CONFIG_RTE_LIBRTE_METER=y +# Compile librte_classify +CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n +# Compile librte_sched +CONFIG_RTE_LIBRTE_SCHED=n +CONFIG_RTE_SCHED_DEBUG=n +CONFIG_RTE_SCHED_RED=n +CONFIG_RTE_SCHED_COLLECT_STATS=n +CONFIG_RTE_SCHED_SUBPORT_TC_OV=n +CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 +CONFIG_RTE_SCHED_VECTOR=n +# Compile architecture we compile for. distributor library +CONFIG_RTE_LIBRTE_DISTRIBUTOR=n +# Compile architecture we compile for. reorder library +CONFIG_RTE_LIBRTE_REORDER=n +# Compile librte_port +CONFIG_RTE_LIBRTE_PORT=n +CONFIG_RTE_PORT_STATS_COLLECT=n +CONFIG_RTE_PORT_PCAP=n +# Compile librte_table +CONFIG_RTE_LIBRTE_TABLE=n +CONFIG_RTE_TABLE_STATS_COLLECT=n +# Compile librte_pipeline +CONFIG_RTE_LIBRTE_PIPELINE=n +CONFIG_RTE_PIPELINE_STATS_COLLECT=n +# Compile librte_kni +CONFIG_RTE_LIBRTE_KNI=n +CONFIG_RTE_LIBRTE_PMD_KNI=n +CONFIG_RTE_KNI_KMOD=n +CONFIG_RTE_KNI_KMOD_ETHTOOL=n +CONFIG_RTE_KNI_PREEMPT_DEFAULT=y +# Compile architecture we compile for. pdump library +CONFIG_RTE_LIBRTE_PDUMP=y +# Compile vhost user library +CONFIG_RTE_LIBRTE_VHOST=y +CONFIG_RTE_LIBRTE_VHOST_NUMA=y +CONFIG_RTE_LIBRTE_VHOST_DEBUG=n +# Compile vhost PMD +# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled. +CONFIG_RTE_LIBRTE_PMD_VHOST=y +# Compile IFC driver +# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO +# should be enabled. +CONFIG_RTE_LIBRTE_IFC_PMD=n +# Compile librte_bpf +CONFIG_RTE_LIBRTE_BPF=n +# allow load BPF from ELF files (requires libelf) +CONFIG_RTE_LIBRTE_BPF_ELF=n +# Compile architecture we compile for. test application +CONFIG_RTE_APP_TEST=y +CONFIG_RTE_APP_TEST_RESOURCE_TAR=n +# Compile architecture we compile for. procinfo application +CONFIG_RTE_PROC_INFO=y +# Compile architecture we compile for. PMD test application +CONFIG_RTE_TEST_PMD=n +CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n +CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n +# Compile architecture we compile for. bbdev test application +CONFIG_RTE_TEST_BBDEV=n +# Compile architecture we compile for. crypto performance application +CONFIG_RTE_APP_CRYPTO_PERF=n +# Compile architecture we compile for. eventdev application +CONFIG_RTE_APP_EVENTDEV=n +CONFIG_RTE_EXEC_ENV_LINUXAPP=y +CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n +# Common libraries, before Bus/PMDs +# NXP DPAA BUS and drivers +# NXP FSLMC BUS and DPAA2 drivers +# NXP ENETC PMD Driver +CONFIG_RTE_ARCH_ARM64=y +CONFIG_RTE_ARCH_64=y +# Maximum available cache line size in arm64 implementations. +# Setting to maximum available cache line size in generic config +# to address minimum DMA alignment across all arm64 implementations. +# Accelarate rte_memcpy. Be sure to run unit test (memcpy_perf_autotest) +# to determine architecture we compile for. best threshold in code. Refer to notes in source file +# (lib/librte_eal/common/include/arch/arm/rte_memcpy_64.h) for more info. +CONFIG_RTE_ARCH_ARM64_MEMCPY=n +#CONFIG_RTE_ARM64_MEMCPY_ALIGNED_THRESHOLD=2048 +#CONFIG_RTE_ARM64_MEMCPY_UNALIGNED_THRESHOLD=512 +# Leave below RTE_ARM64_MEMCPY_xxx options commented out, unless there're +# strong reasons. +#CONFIG_RTE_ARM64_MEMCPY_SKIP_GCC_VER_CHECK=n +#CONFIG_RTE_ARM64_MEMCPY_ALIGN_MASK=0xF +#CONFIG_RTE_ARM64_MEMCPY_STRICT_ALIGN=n +CONFIG_RTE_TOOLCHAIN_GCC=y +CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/SOURCES/configlib.sh b/SOURCES/configlib.sh new file mode 100644 index 0000000..a1049b3 --- /dev/null +++ b/SOURCES/configlib.sh @@ -0,0 +1,105 @@ +# Copyright (C) 2017, Red Hat, Inc. +# +# Core configuration file library. + +# Configurations are determined by sha values. The way to determine is by +# the special text: +# $FILE_COMMENT_TYPE -*- cfg-sha: $SHA256 -*- + +export LC_ALL=C + +# check required binaries +__check_reqd_binaries() { + local BIN __binaries=("egrep" "sort" "sha256sum" "sed") + for BIN in $__binaries; do + if ! type -P $BIN >/dev/null 2>&1; then + echo "Binary $BIN not found. Please install." + exit 1 + fi + done +} + +# Calculates a sha from a file +# The algorithm for generating a sha from a config is thus: +# +# 1. Remove all comment lines and blank lines +# 2. Sort the content +# 3. generate the sha-256 sum +# +# From a script perspective, this means: +# egrep -v ^\# %file% | egrep -v ^$ | sort -u | sha256sum +# +# Params: +# $1 = output variable +# $2 = file to use to calculate the shasum +# $3 = file comment type (defaults to # if unspecified) +calc_sha() { + __check_reqd_binaries + + if [ "$1" == "" ]; then + echo "Please pass in a storage variable." + return 1 + fi + + local __resultvar=$1 + __retval=1 + shift + + local __file=$1 + local cmnt=${2:-#} + + if [ -f "$__file" ]; then + local __shasum=$(egrep -v ^"$cmnt" "$__file" | egrep -v ^$ | sort -u | sha256sum -t | cut -d" " -f1) + eval $__resultvar="'$__shasum'" + __retval=0 + fi + return $__retval +} + +# Retrieves a sha stored in a file +# Param: +# $1 = output variable +# $2 = file to use to calculate the shasum +# $3 = file comment type (defaults to # if unspecified) +retr_sha() { + __check_reqd_binaries + + if [ "$1" == "" ]; then + echo "Please pass in a storage variable." + return 1 + fi + + local __resultvar=$1 + __retval=1 + shift + + local __file=$1 + local cmnt=${2:-#} + + if [ -f "$__file" ]; then + if grep -q "$cmnt -\*- cfg-sha:" "$__file"; then + local __shasum=$(grep "$cmnt -\*- cfg-sha:" "$__file" | sed -e "s@$cmnt -\*- cfg-sha: @@" | cut -d" " -f1) + eval $__resultvar="'$__shasum'" + __retval=0 + fi + fi + return $__retval +} + + +# Set a config value +# set_conf dpdk_build_tree parameter value +# dpdk_build_tree is the directory where the .config lives +# parameter is the config parameter +# value is the value to set for the config parameter +set_conf() { + c="$1/.config" + shift + + if grep -q "$1" "$c"; then + sed -i "s:^$1=.*$:$1=$2:g" $c + else + echo $1=$2 >> "$c" + fi +} + diff --git a/SOURCES/gen_config_group.sh b/SOURCES/gen_config_group.sh new file mode 100755 index 0000000..651a0c5 --- /dev/null +++ b/SOURCES/gen_config_group.sh @@ -0,0 +1,216 @@ +#!/bin/bash + +source configlib.sh + +# Generates arch configurations in the current directory based on +# 1. an openvswitch.spec file +# 2. an expanded dpdk tree + +if (( $# != 2 )); then + echo "$0: openvswitch.spec dpdk_tree" >&2 + exit 1 +fi + +OVSSPEC="$1" +DPDKDIR="$2" + +# accumulate all arch + name triples +OVS_DPDK_CONF_MACH_ARCH=() +for arch in $(grep %define\ dpdk_mach_arch "$OVSSPEC" | sed 's@%define dpdk_mach_arch @@') +do + OVS_DPDK_CONF_MACH_ARCH+=($arch) +done + +OVS_DPDK_CONF_MACH_TMPL=() +for tmpl in $(grep %define\ dpdk_mach_tmpl "$OVSSPEC" | sed 's@%define dpdk_mach_tmpl @@') +do + OVS_DPDK_CONF_MACH_TMPL+=($tmpl) +done + +OVS_DPDK_CONF_MACH=() +for mach in $(grep %define\ dpdk_mach\ "$OVSSPEC" | sed 's@%define dpdk_mach @@') +do + OVS_DPDK_CONF_MACH+=($mach) +done + +OVS_DPDK_TARGETS=() +for ((i=0; i < ${#OVS_DPDK_CONF_MACH[@]}; i++)); +do + OVS_DPDK_TARGETS+=("${OVS_DPDK_CONF_MACH_ARCH[$i]}-${OVS_DPDK_CONF_MACH_TMPL[$i]}-linuxapp-gcc") + echo "DPDK-target: ${OVS_DPDK_TARGETS[$i]}" +done + +OUTPUT_DIR=$(pwd) +pushd "$DPDKDIR" +for ((i=0; i < ${#OVS_DPDK_TARGETS[@]}; i++)); +do + echo "For ${OVS_DPDK_TARGETS[$i]}:" + + echo " a. Generating initial config" + echo " make V=1 T=${OVS_DPDK_TARGETS[$i]} O=${OVS_DPDK_TARGETS[$i]}" + make V=1 T=${OVS_DPDK_TARGETS[$i]} O=${OVS_DPDK_TARGETS[$i]} -j8 config + ORIG_SHA="" + OUTDIR="${OVS_DPDK_TARGETS[$i]}" + + echo " b. calculating and applying sha" + calc_sha ORIG_SHA "${OUTDIR}/.config" + if [ "$ORIG_SHA" == "" ]; then + echo "ERROR: Unable to get sha for arch ${OVS_DPDK_TARGETS[$i]}" + exit 1 + fi + echo "# -*- cfg-sha: ${ORIG_SHA}" > ${OUTDIR}/.config.new + cat "${OUTDIR}/.config" >> "${OUTDIR}/.config.new" + cp "${OUTDIR}/.config" "${OUTDIR}/.config.orig" + mv -f "${OUTDIR}/.config.new" "${OUTDIR}/.config" + + echo " c. setting initial configurations" + # these are the original setconf values from openvswitch.spec + set_conf "${OUTDIR}" CONFIG_RTE_MACHINE "\\\"${OVS_DPDK_CONF_MACH[$i]}\\\"" + + # Disable DPDK libraries not needed + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_TIMER n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CFGFILE n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_JOBSTATS n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_LPM n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_ACL n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_POWER n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_SCHED n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DISTRIBUTOR n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_REORDER n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PORT n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_TABLE n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PIPELINE n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_KNI n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CRYPTODEV n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_SECURITY n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_FLOW_CLASSIFY n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BBDEV n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_COMPRESSDEV n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BPF n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DPAA_MEMPOOL n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CFGFILE n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_EFD n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_FLOW_CLASSIFY n + + # Disable all eventdevs + for eventdev in $(grep _EVENTDEV= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g') + do + set_conf "${OUTDIR}" $eventdev n + done + + # Disable all rawdevs + for rawdev in $(grep _RAWDEV= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g') + do + set_conf "${OUTDIR}" $rawdev n + done + + # Disable virtio user + set_conf "${OUTDIR}" CONFIG_RTE_VIRTIO_USER n + + # Enable vhost numa as libnuma dep is ok + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VHOST_NUMA y + + # start by disabling ALL PMDs + for pmd in $(grep _PMD= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g') + do + set_conf "${OUTDIR}" $pmd n + done + + # PMDs which have their own naming scheme + # the default for this was 'n' at one point. Make sure we keep it + # as such + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_QAT n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VHOST n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_KNI n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_XENVIRT n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_NULL n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_PCAP n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_BOND n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_AF_PACKET n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SOFTNIC n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_DPAA_SEC n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_COMMON_DPAAX n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CAAM_JR n + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE n + + # whitelist of enabled PMDs + # Soft PMDs to enable + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_RING y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VHOST y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VIRTIO_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_TAP y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_FAILSAFE y + + + # start by disabling all buses + for bus in $(grep _BUS= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g') + do + set_conf "${OUTDIR}" $bus n + done + + # blacklist buses that don't conform to std naming + # May override VMBUS later in arch specific section + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VMBUS n + + # whitelist buses + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PCI_BUS y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VDEV_BUS y + + + # Disable some other miscellanous items related to test apps + set_conf "${OUTDIR}" CONFIG_RTE_TEST_BBDEV n + set_conf "${OUTDIR}" CONFIG_RTE_APP_CRYPTO_PERF n + + # Disable kernel modules + set_conf "${OUTDIR}" CONFIG_RTE_EAL_IGB_UIO n + set_conf "${OUTDIR}" CONFIG_RTE_KNI_KMOD n + + # Disable experimental stuff + set_conf "${OUTDIR}" CONFIG_RTE_NEXT_ABI n + + # Arch specific + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_I40E_PMD y + case "${OVS_DPDK_CONF_MACH_ARCH[i]}" in + x86_64) + # Hw PMD + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BNXT_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_ENIC_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX4_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX5_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_NFP_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_QEDE_PMD y + # Sw PMD + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_NETVSC_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD y + # Bus + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VMBUS y + ;& + arm64) + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_IXGBE_PMD y + set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_IGB_PMD y + ;; + esac + + cp "${OUTDIR}/.config" "${OUTPUT_DIR}/${OVS_DPDK_TARGETS[$i]}-config" +done +popd >/dev/null + +echo -n "For each arch ( " +for ((i=0; i < ${#OVS_DPDK_CONF_MACH_ARCH[@]}; i++)); +do + echo -n "${OVS_DPDK_CONF_MACH_ARCH[i]} " +done +echo "):" +echo "1. ensure you enable the requisite hw" diff --git a/SOURCES/ppc_64-power8-linuxapp-gcc-config b/SOURCES/ppc_64-power8-linuxapp-gcc-config new file mode 100644 index 0000000..2319b68 --- /dev/null +++ b/SOURCES/ppc_64-power8-linuxapp-gcc-config @@ -0,0 +1,550 @@ +# -*- cfg-sha: ac783e64ca20c977a7c1c42e72e6dce151b31aa9aecfbfa121b45e49e938f418 +# BSD LICENSE +# Copyright (C) IBM Corporation 2014. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of IBM Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2016 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2017 Intel Corporation +# RTE_EXEC_ENV values are the directories in mk/exec-env/ +CONFIG_RTE_EXEC_ENV="linuxapp" +# RTE_ARCH values are architecture we compile for. directories in mk/arch/ +CONFIG_RTE_ARCH="ppc_64" +# machine can define specific variables or action for a specific board +# RTE_MACHINE values are architecture we compile for. directories in mk/machine/ +CONFIG_RTE_MACHINE="power8" +# The compiler we use. +# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/ +CONFIG_RTE_TOOLCHAIN="gcc" +# Use intrinsics or assembly code for key routines +CONFIG_RTE_FORCE_INTRINSICS=n +# Machine forces strict alignment constraints. +CONFIG_RTE_ARCH_STRICT_ALIGN=n +# Compile to share library +CONFIG_RTE_BUILD_SHARED_LIB=n +# Use newest code breaking previous ABI +CONFIG_RTE_NEXT_ABI=n +# Major ABI to overwrite library specific LIBABIVER +CONFIG_RTE_MAJOR_ABI= +# Machine's cache line size +CONFIG_RTE_CACHE_LINE_SIZE=128 +# Memory model +CONFIG_RTE_USE_C11_MEM_MODEL=n +# Compile Environment Abstraction Layer +CONFIG_RTE_LIBRTE_EAL=y +CONFIG_RTE_MAX_LCORE=256 +CONFIG_RTE_MAX_NUMA_NODES=32 +CONFIG_RTE_MAX_HEAPS=32 +CONFIG_RTE_MAX_MEMSEG_LISTS=64 +# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages +# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller +CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192 +CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768 +# a "type" is a combination of page size and NUMA node. total number of memseg +# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split +# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or +# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of +# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller +CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768 +CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072 +# global maximum usable amount of VA, in megabytes +CONFIG_RTE_MAX_MEM_MB=524288 +CONFIG_RTE_MAX_MEMZONE=2560 +CONFIG_RTE_MAX_TAILQ=32 +CONFIG_RTE_ENABLE_ASSERT=n +CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO +CONFIG_RTE_LOG_HISTORY=256 +CONFIG_RTE_BACKTRACE=y +CONFIG_RTE_LIBEAL_USE_HPET=n +CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n +CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n +CONFIG_RTE_EAL_IGB_UIO=n +CONFIG_RTE_EAL_VFIO=y +CONFIG_RTE_MAX_VFIO_GROUPS=64 +CONFIG_RTE_MAX_VFIO_CONTAINERS=64 +CONFIG_RTE_MALLOC_DEBUG=n +CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y +CONFIG_RTE_USE_LIBBSD=n +# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing. +# AVX512 is marked as experimental for now, will enable it after enough +# field test and possible optimization. +CONFIG_RTE_ENABLE_AVX=y +CONFIG_RTE_ENABLE_AVX512=n +# Default driver path (or "" to disable) +CONFIG_RTE_EAL_PMD_PATH="" +# Compile Environment Abstraction Layer to support Vmware TSC map +CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=n +# Compile architecture we compile for. PCI library +CONFIG_RTE_LIBRTE_PCI=y +# Compile architecture we compile for. argument parser library +CONFIG_RTE_LIBRTE_KVARGS=y +# Compile generic ethernet library +CONFIG_RTE_LIBRTE_ETHER=y +CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n +CONFIG_RTE_MAX_ETHPORTS=32 +CONFIG_RTE_MAX_QUEUES_PER_PORT=1024 +CONFIG_RTE_LIBRTE_IEEE1588=n +CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16 +CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y +CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n +# Turn off Tx preparation stage +# Warning: rte_eth_tx_prepare() can be safely disabled only if using a +# driver which do not implement any Tx preparation. +CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n +# Common libraries, before Bus/PMDs +CONFIG_RTE_LIBRTE_COMMON_DPAAX=n +# Compile architecture we compile for. Intel FPGA bus +CONFIG_RTE_LIBRTE_IFPGA_BUS=n +# Compile PCI bus driver +CONFIG_RTE_LIBRTE_PCI_BUS=y +# Compile architecture we compile for. vdev bus +CONFIG_RTE_LIBRTE_VDEV_BUS=y +# Compile ARK PMD +CONFIG_RTE_LIBRTE_ARK_PMD=n +CONFIG_RTE_LIBRTE_ARK_PAD_TX=y +CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n +# Compile Aquantia Atlantic PMD driver +CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n +# Compile AMD PMD +CONFIG_RTE_LIBRTE_AXGBE_PMD=n +CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n +# Compile burst-oriented Broadcom PMD driver +CONFIG_RTE_LIBRTE_BNX2X_PMD=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n +CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n +# Compile burst-oriented Broadcom BNXT PMD driver +CONFIG_RTE_LIBRTE_BNXT_PMD=n +# Compile burst-oriented Chelsio Terminator (CXGBE) PMD +CONFIG_RTE_LIBRTE_CXGBE_PMD=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n +CONFIG_RTE_LIBRTE_CXGBE_TPUT=y +# NXP DPAA Bus +CONFIG_RTE_LIBRTE_DPAA_BUS=n +CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n +CONFIG_RTE_LIBRTE_DPAA_PMD=n +CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n +# Compile NXP DPAA2 FSL-MC Bus +CONFIG_RTE_LIBRTE_FSLMC_BUS=n +# Compile Support Libraries for NXP DPAA2 +CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n +CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y +# Compile burst-oriented NXP DPAA2 PMD driver +CONFIG_RTE_LIBRTE_DPAA2_PMD=n +CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n +# Compile NXP ENETC PMD Driver +CONFIG_RTE_LIBRTE_ENETC_PMD=n +# Compile burst-oriented Amazon ENA PMD driver +CONFIG_RTE_LIBRTE_ENA_PMD=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n +# Compile burst-oriented Cisco ENIC PMD driver +CONFIG_RTE_LIBRTE_ENIC_PMD=n +# Compile burst-oriented IGB & EM PMD drivers +CONFIG_RTE_LIBRTE_EM_PMD=n +CONFIG_RTE_LIBRTE_IGB_PMD=n +CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n +CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n +CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n +# Compile burst-oriented IXGBE PMD driver +CONFIG_RTE_LIBRTE_IXGBE_PMD=n +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n +CONFIG_RTE_IXGBE_INC_VECTOR=y +CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n +# Compile burst-oriented I40E PMD driver +CONFIG_RTE_LIBRTE_I40E_PMD=y +CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n +CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n +CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y +CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y +CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n +CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64 +CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4 +# Compile burst-oriented FM10K PMD +CONFIG_RTE_LIBRTE_FM10K_PMD=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y +CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y +# Compile burst-oriented AVF PMD driver +CONFIG_RTE_LIBRTE_AVF_PMD=n +CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y +CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n +CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n +CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n +# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD +CONFIG_RTE_LIBRTE_MLX4_PMD=n +CONFIG_RTE_LIBRTE_MLX4_DEBUG=n +CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=n +# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield +# (MLX5) PMD +CONFIG_RTE_LIBRTE_MLX5_PMD=n +CONFIG_RTE_LIBRTE_MLX5_DEBUG=n +CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=n +# Compile burst-oriented Netronome NFP PMD driver +CONFIG_RTE_LIBRTE_NFP_PMD=n +CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n +CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n +# QLogic 10G/25G/40G/50G/100G PMD +CONFIG_RTE_LIBRTE_QEDE_PMD=n +CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n +#Provides abs path/name of architecture we compile for. firmware file. +#Empty string denotes driver will use default firmware +CONFIG_RTE_LIBRTE_QEDE_FW="" +# Compile burst-oriented Solarflare libefx-based PMD +CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n +CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n +# Compile software PMD backed by SZEDATA2 device +CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n +# Compile burst-oriented Cavium Thunderx NICVF PMD driver +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n +# Compile burst-oriented Cavium LiquidIO PMD driver +CONFIG_RTE_LIBRTE_LIO_PMD=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n +# Compile burst-oriented Cavium OCTEONTX network PMD driver +CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n +# Compile WRS accelerated virtual port (AVP) guest PMD driver +CONFIG_RTE_LIBRTE_AVP_PMD=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n +# Compile burst-oriented VIRTIO PMD driver +CONFIG_RTE_LIBRTE_VIRTIO_PMD=y +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n +# Compile virtio device emulation inside virtio PMD driver +CONFIG_RTE_VIRTIO_USER=n +# Compile burst-oriented VMXNET3 PMD driver +CONFIG_RTE_LIBRTE_VMXNET3_PMD=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n +# Compile software PMD backed by AF_PACKET sockets (Linux only) +CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n +# Compile link bonding PMD library +CONFIG_RTE_LIBRTE_PMD_BOND=n +CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n +CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n +# Compile fail-safe PMD +CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y +# Compile Marvell PMD driver +CONFIG_RTE_LIBRTE_MVPP2_PMD=n +# Compile Marvell MVNETA PMD driver +CONFIG_RTE_LIBRTE_MVNETA_PMD=n +# Compile support for VMBus library +CONFIG_RTE_LIBRTE_VMBUS=n +# Compile native PMD for Hyper-V/Azure +CONFIG_RTE_LIBRTE_NETVSC_PMD=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n +# Compile virtual device driver for NetVSC on Hyper-V/Azure +CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=n +# Compile null PMD +CONFIG_RTE_LIBRTE_PMD_NULL=n +# Compile software PMD backed by PCAP files +CONFIG_RTE_LIBRTE_PMD_PCAP=n +# Compile example software rings based PMD +CONFIG_RTE_LIBRTE_PMD_RING=y +CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16 +CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 +# Compile SOFTNIC PMD +CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n +# Compile architecture we compile for. TAP PMD +# It is enabled by default for Linux only. +CONFIG_RTE_LIBRTE_PMD_TAP=y +# Do prefetch of packet data within PMD driver receive function +CONFIG_RTE_PMD_PACKET_PREFETCH=y +# Compile generic wireless base band device library +# EXPERIMENTAL: API may change without prior notice +CONFIG_RTE_LIBRTE_BBDEV=n +CONFIG_RTE_BBDEV_MAX_DEVS=128 +CONFIG_RTE_BBDEV_OFFLOAD_COST=n +# Compile PMD for NULL bbdev device +CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y +# Compile PMD for turbo software bbdev device +CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n +# Compile generic crypto device library +CONFIG_RTE_LIBRTE_CRYPTODEV=n +CONFIG_RTE_CRYPTO_MAX_DEVS=64 +# Compile PMD for ARMv8 Crypto device +CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n +CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n +# Compile NXP CAAM JR crypto Driver +CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n +CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n +# Compile NXP DPAA2 crypto sec driver for CAAM HW +CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n +# NXP DPAA caam - crypto driver +CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n +CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4 +# Compile PMD for Cavium OCTEON TX crypto device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y +# Compile PMD for QuickAssist based devices - see docs for details +CONFIG_RTE_LIBRTE_PMD_QAT=n +CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n +# Max. number of QuickAssist devices, which can be detected and attached +CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48 +CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16 +CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536 +# Compile PMD for virtio crypto devices +CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n +# Number of maximum virtio crypto devices +CONFIG_RTE_MAX_VIRTIO_CRYPTO=32 +# Compile PMD for AESNI backed device +CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n +# Compile PMD for Software backed device +CONFIG_RTE_LIBRTE_PMD_OPENSSL=n +# Compile PMD for AESNI GCM device +CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n +# Compile PMD for SNOW 3G device +CONFIG_RTE_LIBRTE_PMD_SNOW3G=n +CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n +# Compile PMD for KASUMI device +CONFIG_RTE_LIBRTE_PMD_KASUMI=n +# Compile PMD for ZUC device +CONFIG_RTE_LIBRTE_PMD_ZUC=n +# Compile PMD for Crypto Scheduler device +CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n +# Compile PMD for NULL Crypto device +CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n +# Compile PMD for AMD CCP crypto device +CONFIG_RTE_LIBRTE_PMD_CCP=n +# Compile PMD for Marvell Crypto device +CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n +# Compile generic security library +CONFIG_RTE_LIBRTE_SECURITY=n +# Compile generic compression device library +CONFIG_RTE_LIBRTE_COMPRESSDEV=n +CONFIG_RTE_COMPRESS_MAX_DEVS=64 +# Compile compressdev unit test +CONFIG_RTE_COMPRESSDEV_TEST=n +# Compile PMD for Octeontx ZIPVF compression device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n +# Compile PMD for ISA-L compression device +CONFIG_RTE_LIBRTE_PMD_ISAL=n +# Compile PMD for ZLIB compression device +CONFIG_RTE_LIBRTE_PMD_ZLIB=n +# Compile generic event device library +CONFIG_RTE_LIBRTE_EVENTDEV=n +CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n +CONFIG_RTE_EVENT_MAX_DEVS=16 +CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64 +CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32 +CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024 +CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32 +CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32 +# Compile PMD for skeleton event device +CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n +CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n +# Compile PMD for software event device +CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n +# Compile PMD for distributed software event device +CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n +# Compile PMD for octeontx sso event device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n +# Compile PMD for OPDL event device +CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n +# Compile PMD for NXP DPAA event device +CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n +# Compile PMD for NXP DPAA2 event device +CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n +# Compile raw device support +# EXPERIMENTAL: API may change without prior notice +CONFIG_RTE_LIBRTE_RAWDEV=n +CONFIG_RTE_RAWDEV_MAX_DEVS=10 +CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n +# Compile PMD for NXP DPAA2 CMDIF raw device +CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n +# Compile PMD for NXP DPAA2 QDMA raw device +CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n +# Compile PMD for Intel FPGA raw device +CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n +# Compile librte_ring +CONFIG_RTE_LIBRTE_RING=y +# Compile librte_mempool +CONFIG_RTE_LIBRTE_MEMPOOL=y +CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512 +CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n +# Compile Mempool drivers +CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y +CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64 +CONFIG_RTE_DRIVER_MEMPOOL_RING=y +CONFIG_RTE_DRIVER_MEMPOOL_STACK=y +# Compile PMD for octeontx fpa mempool device +CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n +# Compile librte_mbuf +CONFIG_RTE_LIBRTE_MBUF=y +CONFIG_RTE_LIBRTE_MBUF_DEBUG=n +CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc" +CONFIG_RTE_MBUF_REFCNT_ATOMIC=y +CONFIG_RTE_PKTMBUF_HEADROOM=128 +# Compile librte_timer +CONFIG_RTE_LIBRTE_TIMER=n +CONFIG_RTE_LIBRTE_TIMER_DEBUG=n +# Compile librte_cfgfile +CONFIG_RTE_LIBRTE_CFGFILE=n +# Compile librte_cmdline +CONFIG_RTE_LIBRTE_CMDLINE=y +CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n +# Compile librte_hash +CONFIG_RTE_LIBRTE_HASH=y +CONFIG_RTE_LIBRTE_HASH_DEBUG=n +# Compile librte_efd +CONFIG_RTE_LIBRTE_EFD=n +# Compile librte_member +CONFIG_RTE_LIBRTE_MEMBER=y +# Compile librte_jobstats +CONFIG_RTE_LIBRTE_JOBSTATS=n +# Compile architecture we compile for. device metrics library +CONFIG_RTE_LIBRTE_METRICS=y +# Compile architecture we compile for. bitrate statistics library +CONFIG_RTE_LIBRTE_BITRATE=y +# Compile architecture we compile for. latency statistics library +CONFIG_RTE_LIBRTE_LATENCY_STATS=y +# Compile librte_telemetry +CONFIG_RTE_LIBRTE_TELEMETRY=n +# Compile librte_lpm +CONFIG_RTE_LIBRTE_LPM=n +CONFIG_RTE_LIBRTE_LPM_DEBUG=n +# Compile librte_acl +CONFIG_RTE_LIBRTE_ACL=n +CONFIG_RTE_LIBRTE_ACL_DEBUG=n +# Compile librte_power +CONFIG_RTE_LIBRTE_POWER=n +CONFIG_RTE_LIBRTE_POWER_DEBUG=n +CONFIG_RTE_MAX_LCORE_FREQS=64 +# Compile librte_net +CONFIG_RTE_LIBRTE_NET=y +# Compile librte_ip_frag +CONFIG_RTE_LIBRTE_IP_FRAG=y +CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n +CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4 +CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n +# Compile GRO library +CONFIG_RTE_LIBRTE_GRO=y +# Compile GSO library +CONFIG_RTE_LIBRTE_GSO=y +# Compile librte_meter +CONFIG_RTE_LIBRTE_METER=y +# Compile librte_classify +CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n +# Compile librte_sched +CONFIG_RTE_LIBRTE_SCHED=n +CONFIG_RTE_SCHED_DEBUG=n +CONFIG_RTE_SCHED_RED=n +CONFIG_RTE_SCHED_COLLECT_STATS=n +CONFIG_RTE_SCHED_SUBPORT_TC_OV=n +CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 +CONFIG_RTE_SCHED_VECTOR=n +# Compile architecture we compile for. distributor library +CONFIG_RTE_LIBRTE_DISTRIBUTOR=n +# Compile architecture we compile for. reorder library +CONFIG_RTE_LIBRTE_REORDER=n +# Compile librte_port +CONFIG_RTE_LIBRTE_PORT=n +CONFIG_RTE_PORT_STATS_COLLECT=n +CONFIG_RTE_PORT_PCAP=n +# Compile librte_table +CONFIG_RTE_LIBRTE_TABLE=n +CONFIG_RTE_TABLE_STATS_COLLECT=n +# Compile librte_pipeline +CONFIG_RTE_LIBRTE_PIPELINE=n +CONFIG_RTE_PIPELINE_STATS_COLLECT=n +# Compile librte_kni +CONFIG_RTE_LIBRTE_KNI=n +CONFIG_RTE_LIBRTE_PMD_KNI=n +CONFIG_RTE_KNI_KMOD=n +CONFIG_RTE_KNI_KMOD_ETHTOOL=n +CONFIG_RTE_KNI_PREEMPT_DEFAULT=y +# Compile architecture we compile for. pdump library +CONFIG_RTE_LIBRTE_PDUMP=y +# Compile vhost user library +CONFIG_RTE_LIBRTE_VHOST=y +CONFIG_RTE_LIBRTE_VHOST_NUMA=y +CONFIG_RTE_LIBRTE_VHOST_DEBUG=n +# Compile vhost PMD +# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled. +CONFIG_RTE_LIBRTE_PMD_VHOST=y +# Compile IFC driver +# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO +# should be enabled. +CONFIG_RTE_LIBRTE_IFC_PMD=n +# Compile librte_bpf +CONFIG_RTE_LIBRTE_BPF=n +# allow load BPF from ELF files (requires libelf) +CONFIG_RTE_LIBRTE_BPF_ELF=n +# Compile architecture we compile for. test application +CONFIG_RTE_APP_TEST=y +CONFIG_RTE_APP_TEST_RESOURCE_TAR=n +# Compile architecture we compile for. procinfo application +CONFIG_RTE_PROC_INFO=y +# Compile architecture we compile for. PMD test application +CONFIG_RTE_TEST_PMD=n +CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n +CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n +# Compile architecture we compile for. bbdev test application +CONFIG_RTE_TEST_BBDEV=n +# Compile architecture we compile for. crypto performance application +CONFIG_RTE_APP_CRYPTO_PERF=n +# Compile architecture we compile for. eventdev application +CONFIG_RTE_APP_EVENTDEV=n +CONFIG_RTE_EXEC_ENV_LINUXAPP=y +CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n +# Common libraries, before Bus/PMDs +# NXP DPAA BUS and drivers +# NXP FSLMC BUS and DPAA2 drivers +# NXP ENETC PMD Driver +CONFIG_RTE_ARCH_PPC_64=y +CONFIG_RTE_ARCH_64=y +CONFIG_RTE_TOOLCHAIN_GCC=y +# Note: Power doesn't have this support +# Note: Initially, all of architecture we compile for. PMD drivers compilation are turned off on Power +# Will turn on them only after architecture we compile for. successful testing on Power +CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/SOURCES/set_config.sh b/SOURCES/set_config.sh new file mode 100755 index 0000000..002386b --- /dev/null +++ b/SOURCES/set_config.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Copyright (C) 2017, Red Hat, Inc. +# +# set_config.sh will copy a configuration from $1 to $2, in the process +# checking that the sha header for $1 matches the header in $2 + +source configlib.sh + +if (( $# < 2 )); then + echo "$0: source dest [comment-marker]" + exit 1 +fi + +if [ ! -f "$1" ]; then + echo "Source file $1 must exist." + exit 1 +fi +src_file=$1 +shift + +if [ ! -f "$1" ]; then + echo "Dest file $1 must exist." + exit 1 +fi +dst_file=$1 +shift + +comment_sep=${1:-#} + +export LANG=en_US.utf8 + +DEST_FILE_SHA="" +SRC_FILE_SHA="" + +calc_sha DEST_FILE_SHA "$dst_file" "$comment_sep" || echo "Failed to calc sha" +retr_sha SRC_FILE_SHA "$src_file" "$comment_sep" || echo "Failed to retrieve sha" + +if [ "$DEST_FILE_SHA" != "$SRC_FILE_SHA" ]; then + echo "ERROR: The requisite starting sha from $dst_file does not match the" + echo " specified sha in $src_file." + echo "[ $DEST_FILE_SHA ] vs [ $SRC_FILE_SHA ]" + exit 1 +fi + +mv "$dst_file" "$dst_file".OLD +cp "$src_file" "$dst_file" +echo "copied 1 config file." +exit 0 diff --git a/SOURCES/x86_64-native-linuxapp-gcc-config b/SOURCES/x86_64-native-linuxapp-gcc-config new file mode 100644 index 0000000..4b7a7ea --- /dev/null +++ b/SOURCES/x86_64-native-linuxapp-gcc-config @@ -0,0 +1,525 @@ +# -*- cfg-sha: 2ba93102021dc5d38494cf5090c3ecaca37db13153dd558b1511a56f2a3d9b10 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2016 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2017 Intel Corporation +# RTE_EXEC_ENV values are the directories in mk/exec-env/ +CONFIG_RTE_EXEC_ENV="linuxapp" +# RTE_ARCH values are architecture we compile for. directories in mk/arch/ +CONFIG_RTE_ARCH="x86_64" +# machine can define specific variables or action for a specific board +# RTE_MACHINE values are architecture we compile for. directories in mk/machine/ +CONFIG_RTE_MACHINE="default" +# The compiler we use. +# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/ +CONFIG_RTE_TOOLCHAIN="gcc" +# Use intrinsics or assembly code for key routines +CONFIG_RTE_FORCE_INTRINSICS=n +# Machine forces strict alignment constraints. +CONFIG_RTE_ARCH_STRICT_ALIGN=n +# Compile to share library +CONFIG_RTE_BUILD_SHARED_LIB=n +# Use newest code breaking previous ABI +CONFIG_RTE_NEXT_ABI=n +# Major ABI to overwrite library specific LIBABIVER +CONFIG_RTE_MAJOR_ABI= +# Machine's cache line size +CONFIG_RTE_CACHE_LINE_SIZE=64 +# Memory model +CONFIG_RTE_USE_C11_MEM_MODEL=n +# Compile Environment Abstraction Layer +CONFIG_RTE_LIBRTE_EAL=y +CONFIG_RTE_MAX_LCORE=128 +CONFIG_RTE_MAX_NUMA_NODES=8 +CONFIG_RTE_MAX_HEAPS=32 +CONFIG_RTE_MAX_MEMSEG_LISTS=64 +# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages +# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller +CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192 +CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768 +# a "type" is a combination of page size and NUMA node. total number of memseg +# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split +# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or +# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of +# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller +CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768 +CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072 +# global maximum usable amount of VA, in megabytes +CONFIG_RTE_MAX_MEM_MB=524288 +CONFIG_RTE_MAX_MEMZONE=2560 +CONFIG_RTE_MAX_TAILQ=32 +CONFIG_RTE_ENABLE_ASSERT=n +CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO +CONFIG_RTE_LOG_HISTORY=256 +CONFIG_RTE_BACKTRACE=y +CONFIG_RTE_LIBEAL_USE_HPET=n +CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n +CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n +CONFIG_RTE_EAL_IGB_UIO=n +CONFIG_RTE_EAL_VFIO=y +CONFIG_RTE_MAX_VFIO_GROUPS=64 +CONFIG_RTE_MAX_VFIO_CONTAINERS=64 +CONFIG_RTE_MALLOC_DEBUG=n +CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y +CONFIG_RTE_USE_LIBBSD=n +# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing. +# AVX512 is marked as experimental for now, will enable it after enough +# field test and possible optimization. +CONFIG_RTE_ENABLE_AVX=y +CONFIG_RTE_ENABLE_AVX512=n +# Default driver path (or "" to disable) +CONFIG_RTE_EAL_PMD_PATH="" +# Compile Environment Abstraction Layer to support Vmware TSC map +CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=y +# Compile architecture we compile for. PCI library +CONFIG_RTE_LIBRTE_PCI=y +# Compile architecture we compile for. argument parser library +CONFIG_RTE_LIBRTE_KVARGS=y +# Compile generic ethernet library +CONFIG_RTE_LIBRTE_ETHER=y +CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n +CONFIG_RTE_MAX_ETHPORTS=32 +CONFIG_RTE_MAX_QUEUES_PER_PORT=1024 +CONFIG_RTE_LIBRTE_IEEE1588=n +CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16 +CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y +CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n +# Turn off Tx preparation stage +# Warning: rte_eth_tx_prepare() can be safely disabled only if using a +# driver which do not implement any Tx preparation. +CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n +# Common libraries, before Bus/PMDs +CONFIG_RTE_LIBRTE_COMMON_DPAAX=n +# Compile architecture we compile for. Intel FPGA bus +CONFIG_RTE_LIBRTE_IFPGA_BUS=n +# Compile PCI bus driver +CONFIG_RTE_LIBRTE_PCI_BUS=y +# Compile architecture we compile for. vdev bus +CONFIG_RTE_LIBRTE_VDEV_BUS=y +# Compile ARK PMD +CONFIG_RTE_LIBRTE_ARK_PMD=n +CONFIG_RTE_LIBRTE_ARK_PAD_TX=y +CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n +CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n +# Compile Aquantia Atlantic PMD driver +CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n +# Compile AMD PMD +CONFIG_RTE_LIBRTE_AXGBE_PMD=n +CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n +# Compile burst-oriented Broadcom PMD driver +CONFIG_RTE_LIBRTE_BNX2X_PMD=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n +CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n +CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n +# Compile burst-oriented Broadcom BNXT PMD driver +CONFIG_RTE_LIBRTE_BNXT_PMD=y +# Compile burst-oriented Chelsio Terminator (CXGBE) PMD +CONFIG_RTE_LIBRTE_CXGBE_PMD=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n +CONFIG_RTE_LIBRTE_CXGBE_TPUT=y +# NXP DPAA Bus +CONFIG_RTE_LIBRTE_DPAA_BUS=n +CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n +CONFIG_RTE_LIBRTE_DPAA_PMD=n +CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n +# Compile NXP DPAA2 FSL-MC Bus +CONFIG_RTE_LIBRTE_FSLMC_BUS=n +# Compile Support Libraries for NXP DPAA2 +CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n +CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y +# Compile burst-oriented NXP DPAA2 PMD driver +CONFIG_RTE_LIBRTE_DPAA2_PMD=n +CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n +# Compile NXP ENETC PMD Driver +CONFIG_RTE_LIBRTE_ENETC_PMD=n +# Compile burst-oriented Amazon ENA PMD driver +CONFIG_RTE_LIBRTE_ENA_PMD=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n +CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n +# Compile burst-oriented Cisco ENIC PMD driver +CONFIG_RTE_LIBRTE_ENIC_PMD=y +# Compile burst-oriented IGB & EM PMD drivers +CONFIG_RTE_LIBRTE_EM_PMD=n +CONFIG_RTE_LIBRTE_IGB_PMD=y +CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n +CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n +CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n +# Compile burst-oriented IXGBE PMD driver +CONFIG_RTE_LIBRTE_IXGBE_PMD=y +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n +CONFIG_RTE_IXGBE_INC_VECTOR=y +CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n +# Compile burst-oriented I40E PMD driver +CONFIG_RTE_LIBRTE_I40E_PMD=y +CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n +CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n +CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y +CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y +CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n +CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64 +CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4 +# Compile burst-oriented FM10K PMD +CONFIG_RTE_LIBRTE_FM10K_PMD=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n +CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y +CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y +# Compile burst-oriented AVF PMD driver +CONFIG_RTE_LIBRTE_AVF_PMD=n +CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y +CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n +CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n +CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n +CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n +# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD +CONFIG_RTE_LIBRTE_MLX4_PMD=y +CONFIG_RTE_LIBRTE_MLX4_DEBUG=n +CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=y +# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield +# (MLX5) PMD +CONFIG_RTE_LIBRTE_MLX5_PMD=y +CONFIG_RTE_LIBRTE_MLX5_DEBUG=n +CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=y +# Compile burst-oriented Netronome NFP PMD driver +CONFIG_RTE_LIBRTE_NFP_PMD=y +CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n +CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n +# QLogic 10G/25G/40G/50G/100G PMD +CONFIG_RTE_LIBRTE_QEDE_PMD=y +CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n +CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n +#Provides abs path/name of architecture we compile for. firmware file. +#Empty string denotes driver will use default firmware +CONFIG_RTE_LIBRTE_QEDE_FW="" +# Compile burst-oriented Solarflare libefx-based PMD +CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n +CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n +# Compile software PMD backed by SZEDATA2 device +CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n +# Compile burst-oriented Cavium Thunderx NICVF PMD driver +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n +CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n +# Compile burst-oriented Cavium LiquidIO PMD driver +CONFIG_RTE_LIBRTE_LIO_PMD=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n +CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n +# Compile burst-oriented Cavium OCTEONTX network PMD driver +CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n +# Compile WRS accelerated virtual port (AVP) guest PMD driver +CONFIG_RTE_LIBRTE_AVP_PMD=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n +CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n +# Compile burst-oriented VIRTIO PMD driver +CONFIG_RTE_LIBRTE_VIRTIO_PMD=y +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n +CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n +# Compile virtio device emulation inside virtio PMD driver +CONFIG_RTE_VIRTIO_USER=n +# Compile burst-oriented VMXNET3 PMD driver +CONFIG_RTE_LIBRTE_VMXNET3_PMD=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n +CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n +# Compile software PMD backed by AF_PACKET sockets (Linux only) +CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n +# Compile link bonding PMD library +CONFIG_RTE_LIBRTE_PMD_BOND=n +CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n +CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n +# Compile fail-safe PMD +CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y +# Compile Marvell PMD driver +CONFIG_RTE_LIBRTE_MVPP2_PMD=n +# Compile Marvell MVNETA PMD driver +CONFIG_RTE_LIBRTE_MVNETA_PMD=n +# Compile support for VMBus library +CONFIG_RTE_LIBRTE_VMBUS=y +# Compile native PMD for Hyper-V/Azure +CONFIG_RTE_LIBRTE_NETVSC_PMD=y +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n +CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n +# Compile virtual device driver for NetVSC on Hyper-V/Azure +CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=y +# Compile null PMD +CONFIG_RTE_LIBRTE_PMD_NULL=n +# Compile software PMD backed by PCAP files +CONFIG_RTE_LIBRTE_PMD_PCAP=n +# Compile example software rings based PMD +CONFIG_RTE_LIBRTE_PMD_RING=y +CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16 +CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 +# Compile SOFTNIC PMD +CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n +# Compile architecture we compile for. TAP PMD +# It is enabled by default for Linux only. +CONFIG_RTE_LIBRTE_PMD_TAP=y +# Do prefetch of packet data within PMD driver receive function +CONFIG_RTE_PMD_PACKET_PREFETCH=y +# Compile generic wireless base band device library +# EXPERIMENTAL: API may change without prior notice +CONFIG_RTE_LIBRTE_BBDEV=n +CONFIG_RTE_BBDEV_MAX_DEVS=128 +CONFIG_RTE_BBDEV_OFFLOAD_COST=n +# Compile PMD for NULL bbdev device +CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y +# Compile PMD for turbo software bbdev device +CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n +# Compile generic crypto device library +CONFIG_RTE_LIBRTE_CRYPTODEV=n +CONFIG_RTE_CRYPTO_MAX_DEVS=64 +# Compile PMD for ARMv8 Crypto device +CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n +CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n +# Compile NXP CAAM JR crypto Driver +CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n +CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n +# Compile NXP DPAA2 crypto sec driver for CAAM HW +CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n +# NXP DPAA caam - crypto driver +CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n +CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4 +# Compile PMD for Cavium OCTEON TX crypto device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y +# Compile PMD for QuickAssist based devices - see docs for details +CONFIG_RTE_LIBRTE_PMD_QAT=n +CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n +# Max. number of QuickAssist devices, which can be detected and attached +CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48 +CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16 +CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536 +# Compile PMD for virtio crypto devices +CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n +# Number of maximum virtio crypto devices +CONFIG_RTE_MAX_VIRTIO_CRYPTO=32 +# Compile PMD for AESNI backed device +CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n +# Compile PMD for Software backed device +CONFIG_RTE_LIBRTE_PMD_OPENSSL=n +# Compile PMD for AESNI GCM device +CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n +# Compile PMD for SNOW 3G device +CONFIG_RTE_LIBRTE_PMD_SNOW3G=n +CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n +# Compile PMD for KASUMI device +CONFIG_RTE_LIBRTE_PMD_KASUMI=n +# Compile PMD for ZUC device +CONFIG_RTE_LIBRTE_PMD_ZUC=n +# Compile PMD for Crypto Scheduler device +CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n +# Compile PMD for NULL Crypto device +CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n +# Compile PMD for AMD CCP crypto device +CONFIG_RTE_LIBRTE_PMD_CCP=n +# Compile PMD for Marvell Crypto device +CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n +# Compile generic security library +CONFIG_RTE_LIBRTE_SECURITY=n +# Compile generic compression device library +CONFIG_RTE_LIBRTE_COMPRESSDEV=n +CONFIG_RTE_COMPRESS_MAX_DEVS=64 +# Compile compressdev unit test +CONFIG_RTE_COMPRESSDEV_TEST=n +# Compile PMD for Octeontx ZIPVF compression device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n +# Compile PMD for ISA-L compression device +CONFIG_RTE_LIBRTE_PMD_ISAL=n +# Compile PMD for ZLIB compression device +CONFIG_RTE_LIBRTE_PMD_ZLIB=n +# Compile generic event device library +CONFIG_RTE_LIBRTE_EVENTDEV=n +CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n +CONFIG_RTE_EVENT_MAX_DEVS=16 +CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64 +CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32 +CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024 +CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32 +CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32 +# Compile PMD for skeleton event device +CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n +CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n +# Compile PMD for software event device +CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n +# Compile PMD for distributed software event device +CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n +# Compile PMD for octeontx sso event device +CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n +# Compile PMD for OPDL event device +CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n +# Compile PMD for NXP DPAA event device +CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n +# Compile PMD for NXP DPAA2 event device +CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n +# Compile raw device support +# EXPERIMENTAL: API may change without prior notice +CONFIG_RTE_LIBRTE_RAWDEV=n +CONFIG_RTE_RAWDEV_MAX_DEVS=10 +CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n +# Compile PMD for NXP DPAA2 CMDIF raw device +CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n +# Compile PMD for NXP DPAA2 QDMA raw device +CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n +# Compile PMD for Intel FPGA raw device +CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n +# Compile librte_ring +CONFIG_RTE_LIBRTE_RING=y +# Compile librte_mempool +CONFIG_RTE_LIBRTE_MEMPOOL=y +CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512 +CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n +# Compile Mempool drivers +CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y +CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64 +CONFIG_RTE_DRIVER_MEMPOOL_RING=y +CONFIG_RTE_DRIVER_MEMPOOL_STACK=y +# Compile PMD for octeontx fpa mempool device +CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n +# Compile librte_mbuf +CONFIG_RTE_LIBRTE_MBUF=y +CONFIG_RTE_LIBRTE_MBUF_DEBUG=n +CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc" +CONFIG_RTE_MBUF_REFCNT_ATOMIC=y +CONFIG_RTE_PKTMBUF_HEADROOM=128 +# Compile librte_timer +CONFIG_RTE_LIBRTE_TIMER=n +CONFIG_RTE_LIBRTE_TIMER_DEBUG=n +# Compile librte_cfgfile +CONFIG_RTE_LIBRTE_CFGFILE=n +# Compile librte_cmdline +CONFIG_RTE_LIBRTE_CMDLINE=y +CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n +# Compile librte_hash +CONFIG_RTE_LIBRTE_HASH=y +CONFIG_RTE_LIBRTE_HASH_DEBUG=n +# Compile librte_efd +CONFIG_RTE_LIBRTE_EFD=n +# Compile librte_member +CONFIG_RTE_LIBRTE_MEMBER=y +# Compile librte_jobstats +CONFIG_RTE_LIBRTE_JOBSTATS=n +# Compile architecture we compile for. device metrics library +CONFIG_RTE_LIBRTE_METRICS=y +# Compile architecture we compile for. bitrate statistics library +CONFIG_RTE_LIBRTE_BITRATE=y +# Compile architecture we compile for. latency statistics library +CONFIG_RTE_LIBRTE_LATENCY_STATS=y +# Compile librte_telemetry +CONFIG_RTE_LIBRTE_TELEMETRY=n +# Compile librte_lpm +CONFIG_RTE_LIBRTE_LPM=n +CONFIG_RTE_LIBRTE_LPM_DEBUG=n +# Compile librte_acl +CONFIG_RTE_LIBRTE_ACL=n +CONFIG_RTE_LIBRTE_ACL_DEBUG=n +# Compile librte_power +CONFIG_RTE_LIBRTE_POWER=n +CONFIG_RTE_LIBRTE_POWER_DEBUG=n +CONFIG_RTE_MAX_LCORE_FREQS=64 +# Compile librte_net +CONFIG_RTE_LIBRTE_NET=y +# Compile librte_ip_frag +CONFIG_RTE_LIBRTE_IP_FRAG=y +CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n +CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4 +CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n +# Compile GRO library +CONFIG_RTE_LIBRTE_GRO=y +# Compile GSO library +CONFIG_RTE_LIBRTE_GSO=y +# Compile librte_meter +CONFIG_RTE_LIBRTE_METER=y +# Compile librte_classify +CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n +# Compile librte_sched +CONFIG_RTE_LIBRTE_SCHED=n +CONFIG_RTE_SCHED_DEBUG=n +CONFIG_RTE_SCHED_RED=n +CONFIG_RTE_SCHED_COLLECT_STATS=n +CONFIG_RTE_SCHED_SUBPORT_TC_OV=n +CONFIG_RTE_SCHED_PORT_N_GRINDERS=8 +CONFIG_RTE_SCHED_VECTOR=n +# Compile architecture we compile for. distributor library +CONFIG_RTE_LIBRTE_DISTRIBUTOR=n +# Compile architecture we compile for. reorder library +CONFIG_RTE_LIBRTE_REORDER=n +# Compile librte_port +CONFIG_RTE_LIBRTE_PORT=n +CONFIG_RTE_PORT_STATS_COLLECT=n +CONFIG_RTE_PORT_PCAP=n +# Compile librte_table +CONFIG_RTE_LIBRTE_TABLE=n +CONFIG_RTE_TABLE_STATS_COLLECT=n +# Compile librte_pipeline +CONFIG_RTE_LIBRTE_PIPELINE=n +CONFIG_RTE_PIPELINE_STATS_COLLECT=n +# Compile librte_kni +CONFIG_RTE_LIBRTE_KNI=n +CONFIG_RTE_LIBRTE_PMD_KNI=n +CONFIG_RTE_KNI_KMOD=n +CONFIG_RTE_KNI_KMOD_ETHTOOL=n +CONFIG_RTE_KNI_PREEMPT_DEFAULT=y +# Compile architecture we compile for. pdump library +CONFIG_RTE_LIBRTE_PDUMP=y +# Compile vhost user library +CONFIG_RTE_LIBRTE_VHOST=y +CONFIG_RTE_LIBRTE_VHOST_NUMA=y +CONFIG_RTE_LIBRTE_VHOST_DEBUG=n +# Compile vhost PMD +# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled. +CONFIG_RTE_LIBRTE_PMD_VHOST=y +# Compile IFC driver +# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO +# should be enabled. +CONFIG_RTE_LIBRTE_IFC_PMD=n +# Compile librte_bpf +CONFIG_RTE_LIBRTE_BPF=n +# allow load BPF from ELF files (requires libelf) +CONFIG_RTE_LIBRTE_BPF_ELF=n +# Compile architecture we compile for. test application +CONFIG_RTE_APP_TEST=y +CONFIG_RTE_APP_TEST_RESOURCE_TAR=n +# Compile architecture we compile for. procinfo application +CONFIG_RTE_PROC_INFO=y +# Compile architecture we compile for. PMD test application +CONFIG_RTE_TEST_PMD=n +CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n +CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n +# Compile architecture we compile for. bbdev test application +CONFIG_RTE_TEST_BBDEV=n +# Compile architecture we compile for. crypto performance application +CONFIG_RTE_APP_CRYPTO_PERF=n +# Compile architecture we compile for. eventdev application +CONFIG_RTE_APP_EVENTDEV=n +CONFIG_RTE_EXEC_ENV_LINUXAPP=y +CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n +# Common libraries, before Bus/PMDs +# NXP DPAA BUS and drivers +# NXP FSLMC BUS and DPAA2 drivers +# NXP ENETC PMD Driver +CONFIG_RTE_ARCH_X86_64=y +CONFIG_RTE_ARCH_X86=y +CONFIG_RTE_ARCH_64=y +CONFIG_RTE_TOOLCHAIN_GCC=y +CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/SPECS/ovn2.11.spec b/SPECS/ovn2.11.spec new file mode 100644 index 0000000..5397655 --- /dev/null +++ b/SPECS/ovn2.11.spec @@ -0,0 +1,1057 @@ +# Spec file for Open Virtual Network (OVN). + +# Copyright (C) 2018 Red Hat, Inc. +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without warranty of any kind. +# +# If tests have to be skipped while building, specify the '--without check' +# option. For example: +# rpmbuild -bb --without check rhel/ovn-fedora.spec +# + +# This defines the base package name's version. +%define pkgver 2.11 +%define pkgname ovn%{pkgver} + +#%%global commit0 f11b99776c46831184ac30065c6cdf911061bb5a +#%%global date 20190223 +#%%global shortcommit0 %(c=%{commit0}; echo ${c:0:7}) + +# If libcap-ng isn't available and there is no need for running OVS +# as regular user, specify the '--without libcapng' +%bcond_without libcapng + +# option to build ovn-docker package +%bcond_with ovn_docker + +# Enable PIE, bz#955181 +%global _hardened_build 1 + +# some distros (e.g: RHEL-7) don't define _rundir macro yet +# Fedora 15 onwards uses /run as _rundir +%if 0%{!?_rundir:1} +%define _rundir /run +%endif + +# Build python2 (that provides python) and python3 subpackages on Fedora +# Build only python3 (that provides python) subpackage on RHEL8 +# Build only python subpackage on RHEL7 +%if 0%{?rhel} > 7 || 0%{?fedora} +# Use Python3 +%global _py python3 +%global _py2 python2 +%global with_python3 1 +%if 0%{?fedora} +%global with_python2 1 +%else +%global with_python2 0 +%endif +# On RHEL8 Sphinx is included in buildroot +%global external_sphinx 1 +%else +# Use Python2 +%global _py python +%global _py2 python +%global with_python2 1 +%global with_python3 0 +# Don't use external sphinx (RHV doesn't have optional repositories enabled) +%global external_sphinx 0 +%endif + +Name: %{pkgname} +Summary: Open Virtual Network support +Group: System Environment/Daemons +URL: http://www.openvswitch.org/ +Version: %{pkgver}.1 +Release: 53%{?commit0:.%{date}git%{shortcommit0}}%{?dist} +Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1 + +# Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the +# lib/sflow*.[ch] files are SISSL +License: ASL 2.0 and LGPLv2+ and SISSL + +%if 0%{?commit0:1} +Source: https://github.com/openvswitch/ovs/archive/%{commit0}.tar.gz#/openvswitch-%{shortcommit0}.tar.gz +%else +Source: https://www.openvswitch.org/releases/openvswitch-%{version}.tar.gz +%endif + +%define docutilsver 0.12 +%define pygmentsver 1.4 +%define sphinxver 1.1.3 +Source100: https://pypi.io/packages/source/d/docutils/docutils-%{docutilsver}.tar.gz +Source101: https://pypi.io/packages/source/P/Pygments/Pygments-%{pygmentsver}.tar.gz +Source102: https://pypi.io/packages/source/S/Sphinx/Sphinx-%{sphinxver}.tar.gz + +Source500: configlib.sh +Source501: gen_config_group.sh +Source502: set_config.sh + +# Important: source503 is used as the actual copy file +# @TODO: this causes a warning - fix it? +Source504: arm64-armv8a-linuxapp-gcc-config +Source505: ppc_64-power8-linuxapp-gcc-config +Source506: x86_64-native-linuxapp-gcc-config + +# ovn-patches + +# OVN (including OVS if required) backports (0 - 399) +# Bug 1741057 +Patch2: 0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch + +# Bug 1749739 +Patch10: 0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch + +# Bug 1751942 +Patch20: 0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch + +# Bug 1743577 +Patch30: 0001-northd-introduce-build_empty_lb_event_flow-routine.patch +Patch31: 0002-add-meter-support-to-trigger_event-action.patch +Patch32: 0003-northd-add-rate-limiting-support-for-SB-controller-e.patch +Patch33: 0004-northd-add-empty_lb-controller_event-for-logical-rou.patch + +# Bug 1756466 +Patch40: 0001-Disable-conjunction-by-force-cross-product-for-all-t.patch + +# Bug 1580542 +Patch50: 0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch +Patch51: 0002-northd-add-the-possibility-to-define-localnet-as-qos.patch +Patch52: 0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch + +# Bug 1729846, 1757714 & 1757715 +Patch60: 0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch +Patch61: 0002-ovn-northd-Add-IGMP-Relay-support.patch +Patch62: 0003-Learn-the-mac-binding-only-if-required.patch +Patch63: 0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch +Patch64: 0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch + +# Bug 1699332 +Patch70: 0001-Add-RDNSS-support-to-OVN.patch + +# Bug 1764718 +Patch80: 0001-Add-DNSSL-support-to-OVN.patch + +# Bug 1764032 +Patch90: 0001-Revert-conjunctive-match-removal-patches.patch +Patch91: 0002-Combine-conjunctions-with-identical-matches-into-one.patch +Patch92: 0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch + +# Bug 1768347 +Patch100: 0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch +Patch101: 0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch +Patch102: 0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch +Patch103: 0004-northd-Add-lflows-for-IPv6-NAT.patch +Patch104: 0005-system-ovn-Add-IPv6-NAT-test-cases.patch +Patch105: 0006-controller-Downgrade-a-warning-log-message.patch + +#Bug 1762341 +Patch110: 0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch + +#Bug 1769043 +Patch120: 0001-Prevent-erroneous-duplicate-IP-address-messages.patch + +# Bug 1770953 +Patch130: 0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch + +# Bug 1769709 +Patch140: 0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch + +# Bug 1762777 +Patch150: 0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch + +# Bug 1773605 +Patch160: 0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch +Patch161: 0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch +Patch162: 0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch + +# Bug 1756945 +Patch170: 0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch +Patch171: 0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch +Patch172: 0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch + +# Bug 1779112 +# Bug 1778164 +# Bug 1779124 +Patch180: 0001-ovn-controller-Consider-non-virtual-ports-first-when.patch +Patch181: 0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch +Patch182: 0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch + +# Bug 1779212 +Patch190: 0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch +Patch191: 0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch + +# Bug 1703162 +Patch200: 0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch +Patch201: 0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch +Patch202: 0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch +Patch203: 0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch +Patch204: 0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch +Patch205: 0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch +Patch206: 0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch +Patch207: 0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch +Patch208: 0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch +Patch209: 0010-Add-a-new-action-handle_svc_check.patch +Patch210: 0011-Send-service-monitor-health-checks.patch +Patch211: 0012-northd-Remove-misleading-warning-log-message.patch + +# Bug 1787319 +Patch215: 0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch +Patch216: 0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch +Patch217: 0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch +Patch218: 0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch + +# Bug 1787361 +Patch220: 0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch + +# Bug 1787000 +Patch230: 0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch +Patch231: 0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch +Patch232: 0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch + +# Bug 1788456 +Patch240: 0001-ovn-controller-Don-t-monitor-connection-table-column.patch +Patch241: 0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch + +# Bug 1791388 +Patch250: 0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch + +# Bug 1781223 +Patch260: 0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch +Patch261: 0002-northd-Log-all-dynamic-address-assignments.patch +Patch262: 0003-northd-Load-config-before-processing-nbdb-contents.patch +Patch263: 0004-tests-Updated-expected-log-message.patch + +# Bug 1703162 +Patch270: 0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch +Patch271: 0002-ovn-northd-Consider-load-balancer-active-backends-in.patch + +# Bug 1795697 +Patch280: 0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch + +# Bug 1797873 +Patch290: 0001-controller-grant-cap_net_admin-to-ovn-controller.patch + +# Bug 1788193 +Patch300: 0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch +Patch301: 0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch + +# Bug 1811119 +Patch310: 0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch + +# Bug 1813050 +Patch320: 0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch + +# Bug 1805709 +Patch330: 0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch + +# Bug 1816620 +Patch340: 0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch +Patch341: 0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch + +# Bug 1815316 +Patch350: 0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch + +# Bug 1818844 +Patch360: 0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch +Patch361: 0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch + +# Bug 1803005 +Patch370: 0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch + +# Bug 1814127 +Patch380: 0001-ovn-northd-Fix-IP-local-multicast-flooding.patch + +# Bug 1825483 +Patch390: 0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch + +# Bug 1827526 +Patch400: 0001-northd-Allow-64-after-ipv6_prefix.patch + +# Bug 1835750 +Patch410: 0001-Make-is_switch-in-lflow.c-a-util-function.patch +Patch411: 0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch +Patch412: 0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch +Patch413: 0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch + +# Bug 1834433 +Patch420: 0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch +Patch421: 0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch +Patch422: 0003-controller-fix-ip-buffering-with-static-routes.patch +Patch423: 0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch + +# Bug 1848401 +Patch430: 0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch +Patch431: 0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch + +# Bug 1801734 +Patch440: 0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch + +# Bug 1804595 +patch450: 0001-Honour-router_preference-for-solicited-RA.patch + +# Bug 1848818 +Patch460: 0001-Rely-on-unique-name-for-ovn-qos-meters.patch + +# Bug 1846849 +Patch470: 0001-DNS-Make-DNS-lookups-case-insensitive.patch + +# Bug 1825701 +Patch480: 0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch + +# FIXME Sphinx is used to generate some manpages, unfortunately, on RHEL, it's +# in the -optional repository and so we can't require it directly since RHV +# doesn't have the -optional repository enabled and so TPS fails +%if %{external_sphinx} +BuildRequires: %{_py}-sphinx +%else +# Sphinx dependencies +BuildRequires: %{_py}-devel +BuildRequires: %{_py}-setuptools +#BuildRequires: %{_py}-docutils +BuildRequires: %{_py}-jinja2 +BuildRequires: %{_py}-nose +#BuildRequires: %{_py}-pygments +# docutils dependencies +BuildRequires: %{_py}-imaging +# pygments dependencies +BuildRequires: %{_py}-nose +%endif + +BuildRequires: gcc gcc-c++ make +BuildRequires: autoconf automake libtool +BuildRequires: systemd-units openssl openssl-devel +%if %{with_python3} +BuildRequires: python3-devel python3-six python3-setuptools +%endif +%if %{with_python2} +BuildRequires: %{_py2}-devel %{_py2}-six %{_py2}-setuptools +%endif +BuildRequires: desktop-file-utils +BuildRequires: groff-base graphviz +BuildRequires: unbound-devel + +# make check dependencies +BuildRequires: procps-ng +%if %{with_python2} +BuildRequires: pyOpenSSL +%else +BuildRequires: python3-pyOpenSSL +%endif + +%if %{with libcapng} +BuildRequires: libcap-ng libcap-ng-devel +%endif + +Requires: hostname openssl iproute module-init-tools + +Requires(post): systemd-units +Requires(preun): systemd-units +Requires(postun): systemd-units + +# to skip running checks, pass --without check +%bcond_without check + +%description +OVN, the Open Virtual Network, is a system to support virtual network +abstraction. OVN complements the existing capabilities of OVS to add +native support for virtual network abstractions, such as virtual L2 and L3 +overlays and security groups. + +%package central +Summary: Open Virtual Network support +License: ASL 2.0 +Requires: %{pkgname} +Requires: firewalld-filesystem +Provides: openvswitch%{pkgver}-ovn-central = %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: openvswitch%{pkgver}-ovn-central < 2.11.0-1 + +%description central +OVN DB servers and ovn-northd running on a central node. + +%package host +Summary: Open Virtual Network support +License: ASL 2.0 +Requires: %{pkgname} +Requires: firewalld-filesystem +Provides: openvswitch%{pkgver}-ovn-host = %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: openvswitch%{pkgver}-ovn-host < 2.11.0-1 + +%description host +OVN controller running on each host. + +%package vtep +Summary: Open Virtual Network support +License: ASL 2.0 +Requires: %{pkgname} +Provides: openvswitch%{pkgver}-ovn-vtep = %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: openvswitch%{pkgver}-ovn-vtep < 2.11.0-1 + +%description vtep +OVN vtep controller + +%if %{with ovn_docker} +%package docker +Summary: Open Virtual Network support +License: ASL 2.0 +Requires: %{pkgname} %{_py2}-openvswitch +Provides: openvswitch%{pkgver}-ovn-docker = %{?epoch:%{epoch}:}%{version}-%{release} +Obsoletes: openvswitch%{pkgver}-ovn-docker < 2.11.0-1 + +%description docker +Docker network plugins for OVN. +%endif + +%prep +%autosetup -N -c +%if 0%{?commit0:1} +find ovs-%{commit0}/ -maxdepth 1 -mindepth 1 -exec mv -t . {} + +rmdir ovs-%{commit0}/ +%else +find openvswitch-%{version}/ -maxdepth 1 -mindepth 1 -exec mv -t . {} + +rmdir openvswitch-%{version}/ +%endif +%autopatch -p 1 +%if ! %{external_sphinx} +%setup -q -D -T -a 100 -a 101 -a 102 +%endif + +%build +# Build Sphinx on RHEL +%if ! %{external_sphinx} +export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}%{_builddir}/pytmp/lib/python" +for x in docutils-%{docutilsver} Pygments-%{pygmentsver} Sphinx-%{sphinxver}; do + pushd "$x" + %{_py} setup.py install --home %{_builddir}/pytmp + popd +done + +export PATH="$PATH:%{_builddir}/pytmp/bin" +%endif + +%if 0%{?commit0:1} +# fix the snapshot unreleased version to be the released one. +sed -i.old -e "s/^AC_INIT(openvswitch,.*,/AC_INIT(openvswitch, %{version},/" configure.ac +%endif +./boot.sh + +%configure \ +%if %{with libcapng} + --enable-libcapng \ +%else + --disable-libcapng \ +%endif + --enable-ssl \ + --with-pkidir=%{_sharedstatedir}/openvswitch/pki +make %{?_smp_mflags} + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT + +for service in ovn-controller ovn-controller-vtep ovn-northd; do + install -p -D -m 0644 \ + rhel/usr_lib_systemd_system_${service}.service \ + $RPM_BUILD_ROOT%{_unitdir}/${service}.service +done + +rm -rf $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/ + +install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/openvswitch + +install -d $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ +install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \ + $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ovn-central-firewall-service.xml +install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \ + $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ovn-host-firewall-service.xml + +install -d -m 0755 $RPM_BUILD_ROOT%{_prefix}/lib/ocf/resource.d/ovn +ln -s %{_datadir}/openvswitch/scripts/ovndb-servers.ocf \ + $RPM_BUILD_ROOT%{_prefix}/lib/ocf/resource.d/ovn/ovndb-servers + +# remove OVS unpackages files +rm -f $RPM_BUILD_ROOT%{_bindir}/ovs* +rm -f $RPM_BUILD_ROOT%{_bindir}/vtep-ctl +rm -f $RPM_BUILD_ROOT%{_sbindir}/ovs* +rm -f $RPM_BUILD_ROOT%{_mandir}/man1/ovs* +rm -f $RPM_BUILD_ROOT%{_mandir}/man5/ovs* +rm -f $RPM_BUILD_ROOT%{_mandir}/man5/vtep* +rm -f $RPM_BUILD_ROOT%{_mandir}/man7/ovs* +rm -f $RPM_BUILD_ROOT%{_mandir}/man8/ovs* +rm -f $RPM_BUILD_ROOT%{_mandir}/man8/vtep* +rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/ovs* +rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/vswitch.ovsschema +rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/vtep.ovsschema +rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs* +rm -rf $RPM_BUILD_ROOT%{_datadir}/openvswitch/bugtool-plugins +rm -f $RPM_BUILD_ROOT%{_includedir}/openvswitch/* +rm -f $RPM_BUILD_ROOT%{_includedir}/openflow/* +rm -f $RPM_BUILD_ROOT%{_libdir}/*.a +rm -f $RPM_BUILD_ROOT%{_libdir}/*.la +rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc +rm -f $RPM_BUILD_ROOT%{_includedir}/openvswitch/* +rm -f $RPM_BUILD_ROOT%{_includedir}/openflow/* +rm -f $RPM_BUILD_ROOT%{_includedir}/ovn/* +rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash +rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash +rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/openvswitch + +%if %{without ovn_docker} +rm -f $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-overlay-driver \ + $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-underlay-driver +%endif + +%check +%if %{with check} + touch resolv.conf + export OVS_RESOLV_CONF=$(pwd)/resolv.conf + if make check TESTSUITEFLAGS='%{_smp_mflags} -k ovn' || + make check TESTSUITEFLAGS='--recheck -k ovn'; then :; + else + cat tests/testsuite.log + # Presently a test case - "2796: ovn -- ovn-controller incremental processing" + # is failing on aarch64 arch. Let's not exit for this arch + # until we figure out why it is failing. + %ifnarch aarch64 + exit 1 + %endif + fi +%endif + +%clean +rm -rf $RPM_BUILD_ROOT + +%pre central +if [ $1 -eq 1 ] ; then + # Package install. + /bin/systemctl status ovn-northd.service &>/dev/null + ovn_status=$? + rpm -ql openvswitch-ovn-central > /dev/null + if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then + # ovn-northd service is running which means old openvswitch-ovn-central + # is already installed and it will be cleaned up. So start ovn-northd + # service when posttrans central is called. + touch %{_localstatedir}/lib/rpm-state/ovn-northd + fi +fi + +%pre host +if [ $1 -eq 1 ] ; then + # Package install. + /bin/systemctl status ovn-controller.service &>/dev/null + ovn_status=$? + rpm -ql openvswitch-ovn-host > /dev/null + if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then + # ovn-controller service is running which means old + # openvswitch-ovn-host is installed and it will be cleaned up. So + # start ovn-controller service when posttrans host is called. + touch %{_localstatedir}/lib/rpm-state/ovn-controller + fi +fi + +%pre vtep +if [ $1 -eq 1 ] ; then + # Package install. + /bin/systemctl status ovn-controller-vtep.service >/dev/null + ovn_status=$? + rpm -ql openvswitch-ovn-vtep > /dev/null + if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then + # ovn-controller-vtep service is running which means old + # openvswitch-ovn-vtep is installed and it will be cleaned up. So + # start ovn-controller-vtep service when posttrans host is called. + touch %{_localstatedir}/lib/rpm-state/ovn-controller-vtep + fi +fi + +%preun central +%if 0%{?systemd_preun:1} + %systemd_preun ovn-northd.service +%else + if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || : + /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || : + fi +%endif + +%preun host +%if 0%{?systemd_preun:1} + %systemd_preun ovn-controller.service +%else + if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || : + /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || : + fi +%endif + +%preun vtep +%if 0%{?systemd_preun:1} + %systemd_preun ovn-controller-vtep.service +%else + if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || : + /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || : + fi +%endif + +%post central +%if 0%{?systemd_post:1} + %systemd_post ovn-northd.service +%else + # Package install, not upgrade + if [ $1 -eq 1 ]; then + /bin/systemctl daemon-reload >dev/null || : + fi +%endif + +%post host +%if 0%{?systemd_post:1} + %systemd_post ovn-controller.service +%else + # Package install, not upgrade + if [ $1 -eq 1 ]; then + /bin/systemctl daemon-reload >dev/null || : + fi +%endif + +%post vtep +%if 0%{?systemd_post:1} + %systemd_post ovn-controller-vtep.service +%else + # Package install, not upgrade + if [ $1 -eq 1 ]; then + /bin/systemctl daemon-reload >dev/null || : + fi +%endif + +%postun + +%postun central +%if 0%{?systemd_postun_with_restart:1} + %systemd_postun_with_restart ovn-northd.service +%else + /bin/systemctl daemon-reload >/dev/null 2>&1 || : + if [ "$1" -ge "1" ] ; then + # Package upgrade, not uninstall + /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || : + fi +%endif + +%postun host +%if 0%{?systemd_postun_with_restart:1} + %systemd_postun_with_restart ovn-controller.service +%else + /bin/systemctl daemon-reload >/dev/null 2>&1 || : + if [ "$1" -ge "1" ] ; then + # Package upgrade, not uninstall + /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || : + fi +%endif + +%postun vtep +%if 0%{?systemd_postun_with_restart:1} + %systemd_postun_with_restart ovn-controller-vtep.service +%else + /bin/systemctl daemon-reload >/dev/null 2>&1 || : + if [ "$1" -ge "1" ] ; then + # Package upgrade, not uninstall + /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || : + fi +%endif + +%posttrans central +if [ $1 -eq 1 ]; then + # Package install, not upgrade + if [ -e %{_localstatedir}/lib/rpm-state/ovn-northd ]; then + rm %{_localstatedir}/lib/rpm-state/ovn-northd + /bin/systemctl start ovn-northd.service >/dev/null 2>&1 || : + fi +fi + + +%posttrans host +if [ $1 -eq 1 ]; then + # Package install, not upgrade + if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller ]; then + rm %{_localstatedir}/lib/rpm-state/ovn-controller + /bin/systemctl start ovn-controller.service >/dev/null 2>&1 || : + fi +fi + +%posttrans vtep +if [ $1 -eq 1 ]; then + # Package install, not upgrade + if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller-vtep ]; then + rm %{_localstatedir}/lib/rpm-state/ovn-controller-vtep + /bin/systemctl start ovn-controller-vtep.service >/dev/null 2>&1 || : + fi +fi + +%files +%{_bindir}/ovn-nbctl +%{_bindir}/ovn-sbctl +%{_bindir}/ovn-trace +%{_bindir}/ovn-detrace +%{_datadir}/openvswitch/scripts/ovn-ctl +%{_datadir}/openvswitch/scripts/ovndb-servers.ocf +%{_mandir}/man8/ovn-ctl.8* +%{_mandir}/man8/ovn-nbctl.8* +%{_mandir}/man8/ovn-trace.8* +%{_mandir}/man1/ovn-detrace.1* +%{_mandir}/man7/ovn-architecture.7* +%{_mandir}/man8/ovn-sbctl.8* +%{_mandir}/man5/ovn-nb.5* +%{_mandir}/man5/ovn-sb.5* +%{_prefix}/lib/ocf/resource.d/ovn/ovndb-servers +%if %{with_python2} +%{_datadir}/openvswitch/scripts/ovn-bugtool-nbctl-show +%{_datadir}/openvswitch/scripts/ovn-bugtool-sbctl-lflow-list +%{_datadir}/openvswitch/scripts/ovn-bugtool-sbctl-show +%endif + +%if %{with ovn_docker} +%files docker +%{_bindir}/ovn-docker-overlay-driver +%{_bindir}/ovn-docker-underlay-driver +%endif + +%files central +%{_bindir}/ovn-northd +%{_mandir}/man8/ovn-northd.8* +%config %{_datadir}/openvswitch/ovn-nb.ovsschema +%config %{_datadir}/openvswitch/ovn-sb.ovsschema +%{_unitdir}/ovn-northd.service +%{_prefix}/lib/firewalld/services/ovn-central-firewall-service.xml + +%files host +%{_bindir}/ovn-controller +%{_mandir}/man8/ovn-controller.8* +%{_unitdir}/ovn-controller.service +%{_prefix}/lib/firewalld/services/ovn-host-firewall-service.xml + +%files vtep +%{_bindir}/ovn-controller-vtep +%{_mandir}/man8/ovn-controller-vtep.8* +%{_unitdir}/ovn-controller-vtep.service + +%changelog +* Fri Jul 10 2020 Lorenzo Bianconi - 2.11.1-53 +- Backport "northd: Fix IPAM IPv4 start address calculation" (#1825701) + +* Thu Jul 9 2020 Lorenzo Bianconi - 2.11.1-52 +- Backport "DNS: Make DNS lookups case insensitive" (#1846849) + +* Thu Jul 9 2020 Lorenzo Bianconi - 2.11.1-51 +- Backport "Rely on unique name for ovn qos meters" (#1848818) + +* Thu Jun 25 2020 Lorenzo Bianconi - 2.11.1-50 +- Backport "Honour router_preference for solicited RA" (#1804595) + +* Thu Jun 25 2020 Numan Siddique - 2.11.1-48 +- Backport "pinctrl: Support DHCPRELEASE and DHCPINFORM in native OVN dhcp responder." (#1801734) + +* Thu Jun 18 2020 Numan Siddique - 2.11.1-47 +- Backport "northd: By pass IPv6 Router Adv and Router Solicitation packets from ACL stages." (#1848401) +- Backport "ovsdb idl: Try committing the pending txn in ovsdb_idl_loop_run." + +* Wed May 27 2020 Lorenzo Bianconi - 2.11.1-47 +- Backport "ovn-northd: Remove useless flow for GW_REDIRECT" (#1834433) +- Backport "Revert "Manage ARP process locally in a DVR scenario" (#1834433) +- Backport "controller: fix ip buffering with static routes" (#1834433) +- Backport "northd: manage ARP request locally for FIP traffic" (#1834433) + +* Thu May 14 2020 Numan Siddique - 2.11.1-46 +- Backport "Make is_switch() in lflow.c a util function" (#1835750) +- Backport "ovn-controller: Minimize SB DB port_binding lookups." (#1835750) +- Backport "ovn-controller.c: Fix memory leak of local_datapath->ports." (#1835750) +- Backport "ovn-controller: Remove ports from struct local_datapaths." (#1835750) + +* Sat May 09 2020 Numan Siddique - 2.11.1-45 +- Backport "northd: Allow /64 after ipv6_prefix" (#1827526) + +* Sat Apr 18 2020 Numan Siddique - 2.11.1-44 +- Backport "pinctrl: Handle service monitors even if the lport doesn't have IPv4 addresses set." (#1825483) + +* Wed Apr 15 2020 Dumitru Ceara - 2.11.1-43 +- Backport "ovn-northd: Fix IP local multicast flooding.". (#1814127) + +* Fri Apr 10 2020 Numan Siddique - 2.11.1-42 +- Backport "ovn-ctl: Provide the option to configure inactive probe from standby to active". (#1803005) + +* Thu Apr 09 2020 Dumitru Ceara - 2.11.1-41 +- Backport "ovn-controller: Fix potential segfault with "virtual" port bindings." (#1818844) +- Backport "ovn-controller: Skip vport bindings done through OVS external_ids:iface-id." (#1818844) + +* Thu Apr 02 2020 Lorenzo Bianconi - 2.11.1-40 +- Backport "controller: use LLA IPv6 address as NS source address" (#1815316) + +* Tue Mar 24 2020 Dumitru Ceara - 2.11.1-39 +- Backport "ovn-northd: Forward ARP requests on localnet ports." (#1816620) +- Backport "ovn.at: Fix ARP test that fails due to timing." (#1816620) + +* Mon Mar 23 2020 Numan Siddique - 2.11.1-38 +- Backport "ovn-northd: Don't add arp responder flows for lports with 'unknown' address." (#1805709) + +* Fri Mar 13 2020 Numan Siddique - 2.11.1-37 +- Backport "ovn-northd: Add lflows to by pass the svc monitor packets from conntrack". (#1813050) + +* Fri Mar 6 2020 Ihar Hrachyshka - 2.11.1-36 +- Backport "Broadcast DHCPREPLY when BROADCAST flag is set" (#1811119) + +* Mon Mar 2 2020 Lorenzo Bianconi - 2.11.1-35 +- Backport "pinctrl: fix IP buffering with connection-tracking" (#1788193) +- Backport "Manage ARP process locally in a DVR scenario" (#1788193) + +* Fri Feb 28 2020 Lorenzo Bianconi - 2.11.1-34 +- Backport "controller: grant cap_net_admin to ovn-controller" (#1797873) + +* Tue Feb 4 2020 Numan Siddique - 2.11.1-33 +* Backport "ovn-northd: Address scale issues with DNAT flows." (1795697) + +* Thu Jan 30 2020 Numan Siddique - 2.11.1-32 +* Backport "[RFE] LB health check : ovn-northd: Consider load balancer active backends in router pipeline" (#1703162) + +* Fri Jan 24 2020 Dumitru Ceara - 2.11.1-31 +- Backport "tests: Updated expected log message" (#1781223) +- Backport "nbctl: Log the source of duplicate IP addresses" (#1781223) +- Backport "northd: Log all dynamic address assignments" (#1781223) +- Backport "northd: Load config before processing nbdb contents" (#1781223) + +* Thu Jan 16 2020 Timothy Redaelli - 2.11.1-30 +- Add a RHEL-only patch to read the OVS_USER_ID from + /etc/openvswitch/default.conf and /etc/sysconfig/openvswitch. + This is needed for openvswitch2.11 < 2.11.0-43 compatibility (#1791388) + +* Fri Jan 10 2020 Numan Siddique - 2.11.1-29 +- Backport "ovn-controller: Don't monitor connection table columns" +- Backport "Restrict ARP/IPv6 ND replies for LB VIP only on chassis redirect port" (#1788456) + +* Thu Jan 09 2020 Lorenzo Bianconi - 2.11.1-28 +- Backport "pinctrl.c: Fix maybe-uninitialized warnings with old GCC versions" (#1787000) +- Backport "DNSSL: copy dnssl string in order to avoid truncated value" (#1787000) +- Backport "RA Route Info Option: copy route info string in order to avoid truncated value" (#1787000) + +* Thu Jan 02 2020 Dumitru Ceara - 2.11.1-27 +- Backport "ovn-controller: Run I-P engine even when no SB txn is available." (#1787361) + +* Thu Jan 02 2020 Dumitru Ceara - 2.11.1-26 +- Backport "ovn-controller: Refactor I-P engine_run() tracking." (#1787318) +- Backport "ovn-controller: Add per node states to I-P engine." (#1787318) +- Backport "ovn-controller: Add separate I-P engine node for processing ct-zones." (#1787318) +- Backport "ovn-controller: Fix use of dangling pointers in I-P runtime_data." (#1787318) + +* Thu Dec 19 2019 Mark Michelson - 2.11.1-25 +- Do not force crossproducts on string expressions (#1764032) + +* Thu Dec 05 2019 Numan Siddique - 2.11.1-24 +- Backport "[RFE] Support for load balancing health checks in OVN" (#1703162) +- Backport "Stateless NAT support (backported due to conflicts)" +- Backport "Ankur's Vlan backed DVR N-S patches (backported due to conflicts)" + +* Tue Dec 03 2019 Lorenzo Bianconi - 2.11.1-23 +- Backport "Add support to Default Router Preference (PRF) - RFC4191" (#1779212) +- Backport "Add support for Route Info Option in RA - RFC4191" + +* Tue Dec 3 2019 Dumitru Ceara - 2.11.1-22 +- Backport "ovn-controller: Consider non-virtual ports first when updating bindings." (#1779112) +- Backport "ovn-controller: Add missing port group lflow references." (#1778164) +- Backport "ovn-controller: Add command to trigger an I-P full recompute." (#1779124) + +* Tue Nov 26 2019 Dumitru Ceara - 2.11.1-21 +- Backport "ovn-northd: Fix get_router_load_balancer_ips() for mixed address families." +- Backport "ovn-northd: Limit ARP/ND broadcast domain whenever possible." (#1756945) +- Backport "ovn-northd: Avoid empty address list when limiting ARP/ND broadcast." (#1756945) + +* Tue Nov 19 2019 Numan Siddique - 2.11.1-20 +- Backport "Skip IPv6 NS packets in router egress SNAT pipeline" (#1773605) +- Backport "northd: Match IPv4 or IPv6 for MAC resolution" +- Removed - Requires: openvswitch2.11 + +* Tue Nov 12 2019 Dumitru Ceara - 2.11.1-19 +- Backport "Fix ha chassis failover issues for stale ha chassis entries" (#1762777) + +* Tue Nov 12 2019 Dumitru Ceara - 2.11.1-18 +- Backport "ovn-northd: Validate dnat_and_snat external_mac/logical_ip." (#1769709) + +* Mon Nov 11 2019 Mark Michelson - 2.11.1-17 +- Revert support for openvswitch2.12 + +* Mon Nov 11 2019 Mark Michelson - 2.11.1-16 +- Update "Requires" to allow for openvswitch2.12 + +* Mon Nov 11 2019 Dumitru Ceara - 2.11.1-15 +- Backport "lflow.c: Fix memory leak of lflow_ref_list_node->ref_name." (#1770953) + +* Tue Nov 05 2019 Mark Michelson - 2.11.1-14 +- Backport "Prevent erroneous duplicate IP address messages" (#1769043) + +* Mon Nov 04 2019 Numan Siddique - 2.11.1-13 +- Backport "Fix virtual port binding when the parents are scheduled in same chassis" (#1762341) + +* Mon Nov 04 2019 Numan Siddique - 2.11.1-12 +- Backport "Support IPv6 NAT" (#1768347) + +* Sun Nov 3 2019 Mark Michelson - 2.11.1-11 +- Backport "Revert conjunctive match removal patches" (#1764032) +- Backport "Combine conjunctions with identical matches into one" (#1764032) + +* Wed Oct 30 2019 Lorenzo Bianconi - 2.11.1-10 +- Backport "Add DNSSL support to OVN" (#1764718) + +* Wed Oct 30 2019 Lorenzo Bianconi - 2.11.1-9 +- Backport "Add RDNSS support to OVN" (#1699332) + +* Wed Oct 16 2019 Dumitru Ceara - 2.11.1-8 +- Backport "ovn-northd: Fix IP multicast flooding to mrouter." (#1757714) + +* Thu Oct 10 2019 Numan Siddique - 2.11.1-7 +- Fixed the patch apply errors. + +* Wed Oct 9 2019 Dumitru Ceara - 2.11.1-6 +- Backport "ovn-northd: Use HMAP_FOR_EACH when adding multicast lflows" (#1757714) +- Backport "ovn-northd: Add IGMP Relay support" (#1757714) +- Backport "ovn-northd: Add static IP multicast flood configuration" (#1757715) + +* Wed Oct 2 2019 Dumitru Ceara - 2.11.1-5 +- Backport "Learn the mac binding only if required" (#1729846) + +* Tue Oct 1 2019 Lorenzo Bianconi - 2.11.1-4 +- Backport "Introduce localnet egress QoS support" (#1580542) + +* Fri Sep 27 2019 Numan Siddique - 2.11.1-3 +- Backport " Disable conjunction by force cross product for all the fields." (#1756466) + +* Mon Sep 16 2019 Lorenzo Bianconi - 2.11.1-2 +- Backport "Northd: add empty_lb controller_event for logical router" (#1743577) + +* Sat Sep 14 2019 Numan Siddique - 2.11.1-1 +- Backport "Exclude inport and outport symbol tables from conjunction" (#1751942) + +* Fri Sep 06 2019 Numan Siddique - 2.11.1-0 +- Sync 2.11.1-0 with upstream 2.12.0 so that we have all the I-P series patches (#1748102) + +* Tue Sep 03 2019 Numan Siddique - 2.11.0-37 +- Backport firt 3 patches of I-P series (#1748102) + +* Fri Aug 23 2019 Lorenzo Bianconi - 2.11.0-36 +- Backport "Remove ageing check in run_put_mac_binding" (#1745002) + +* Fri Aug 23 2019 Dumitru Ceara - 2.11.0-35 +- Backport "pinctrl: Fix DNS packet parsing" (#1744991) + +* Fri Aug 23 2019 Numan Siddique - 2.11.0-34 +- Backport "ovn-controller: Provide the option to configure inactivity probe interval for OpenFlow conn" (#1744962) + +* Tue Aug 20 2019 Numan Siddique - 2.11.0-33 +- Backport "Make pidfile_is_running more robust against empty pidfiles" (#1741057) + +* Tue Aug 06 2019 Numan Siddique - 2.11.0-32 +- Backport "Support binding a logical port based on the GARP from the VIF" (#1699350) + +* Tue Aug 06 2019 Lorenzo Bianconi - 2.11.0-31 +- Backport "Fix default L4 default proto reported by ovn-nbctl" (#1734743) + +* Mon Aug 05 2019 Numan Siddique - 2.11.0-30 +- Backport "Don't emit ICMP need to frag on LRP with no IPv4 address" (#1737341) + +* Mon Aug 05 2019 Numan Siddique - 2.11.0-29 +- Revert Backport "Support binding a logical port based on the GARP from the VIF" (#1699350) + +* Mon Aug 05 2019 Numan Siddique - 2.11.0-28 +- Backport " Support binding a logical port based on the GARP from the VIF" (#1699350) + +* Mon Aug 05 2019 Numan Siddique - 2.11.0-27 +- Backport ovn-northd pause/resume support (#1720728) + +* Wed Jul 17 2019 Lorenzo Bianconi - 2.11.0-26 +- Backport ovn uindling support (#1730759) + +* Wed Jul 17 2019 Numan Siddique - 2.11.0-25 +- Backport related to GARP handling for router ips (#1561880) + +* Tue Jul 9 2019 Lorenzo Bianconi - 2.11.0-24 +- Backport related to ip buffering with gw router port issue (#1728318) + +* Mon Jul 1 2019 Lorenzo Bianconi - 2.11.0-23 +- Backport "OVN: add the possibility to specify tunnel dst port" (#1720371) + +* Mon Jun 24 2019 Dumitru Ceara - 2.11.0-22 +- Backport "ovn-controller: Fix parsing of OVN tunnel IDs" (#1708131) + +* Mon Jun 24 2019 Numan Siddique - 2.11.0-21 +- Backport "ovn-nbctl.8.xml: Fix typo." (#1720194) + +* Mon Jun 17 2019 Numan Siddique - 2.11.0-20 +- Backport "ovn: Add support for DHCP option 15 - domain name" (#1721012) + +* Thu Jun 06 2019 Timothy Redaelli - 2.11.0-19 +- Avoid collisions during installation of sources in debuginfo package (#1717933) + +* Mon May 27 2019 Dumitru Ceara - 2.11.0-18 +- Backport "ovn: Properly set the index for chassis lookup" (#1698462) + +* Tue May 14 2019 Dumitru Ceara - 2.11.0-17 +- Backport "stopwatch: Free stopwatch packets after processing" (#1698462) + +* Fri Apr 26 2019 Numan Siddique - 2.11.0-16 +- Fix the ovn-northd sync issue with HA_Chassis group (#1666673) + +* Wed Apr 24 2019 Numan Siddique - 2.11.0-15 +- Backport "RFE: Limit Geneve to within Transport Zones" (#1702550) + +* Wed Apr 24 2019 Numan Siddique - 2.11.0-14 +- Backport "RFE: [OVN] Fragmentation support" (#1702331) + +* Sat Apr 20 2019 Numan Siddique - 2.11.0-13 +- Backport "RFE: OVN - Support Logical ports of type external" (#1666673) + +* Thu Apr 18 2019 Lorenzo Bianconi - 2.11.0-12 +- Backport "OVN: fix DVR Floating IP support" (#1701183) + +* Wed Apr 17 2019 Timothy Redaelli - 2.11.0-11 +- Add 'Obsoletes' to the old OVN openvswitch2.11 sub-packages to allow to upgrade from FDP.A + +* Wed Apr 17 2019 Lorenzo Bianconi - 2.11.0-10 +- Backport "OVN: add the possibility to configure a static IPv4/IPv6 address and dynamic MAC" + +* Tue Apr 16 2019 Timothy Redaelli - 2.11.0-9 +- Remove 'Obsoletes' lines like on openvswitch2.11 package + +* Tue Apr 16 2019 Numan Siddique - 2.11.0-8 +- Fix the 'Provides' to include '%pkgver' + +* Tue Mar 26 2019 Numan Siddique - 2.11.0-7 +- Backport Fixes for #1677616 (pinctrl thread) and fixes related to IPv6 RA." + +* Tue Mar 19 2019 Numan Siddique - 2.11.0-6 +- Removed ovn-common package and moved all the related files to main 'ovn' package. + +* Tue Mar 19 2019 Timothy Redaelli - 2.11.0-5 +- Disable ovn-docker subpackage by default + +* Thu Mar 07 2019 Mark Michelson - 2.11.0-4 +- Backport "OVN: Add port addresses to IPAM after all ports are joined." + +* Sat Mar 02 2019 Numan Siddique - 2.11.0-3 +- Backport "ovn-controller: Provide the option to set the datapath-type of br-int" (#1684796) + +* Fri Mar 01 2019 Timothy Redaelli - 2.11.0-2 +- Backport "rhel: Use PIDFile on forking systemd service files" (#1684477) + +* Thu Feb 28 2019 Numan Siddique - 2.11.0-1 +- Update to official 2.11 release + +* Wed Jan 23 2019 Numan Siddique - 2.11.0-0.20190117git4e4f80e +- Update to OVS 2.11 branch + +* Thu Jan 17 2019 Numan Siddique - 2.11.0-0.20190117gitab66223 +- Update to a snapshot of OVS 2.11 from master + +* Thu Dec 20 2018 Numan Siddique +- OVS/OVN split.