diff --git a/.openvswitch.metadata b/.openvswitch.metadata index 48a19dd..c54e59a 100644 --- a/.openvswitch.metadata +++ b/.openvswitch.metadata @@ -1,6 +1,6 @@ +d34f96421a86004aa5d26ecf975edefd09f948b1 SOURCES/Pygments-1.4.tar.gz +3a11f130c63b057532ca37fe49c8967d0cbae1d5 SOURCES/Sphinx-1.2.3.tar.gz 002450621b33c5690060345b0aac25bc2426d675 SOURCES/docutils-0.12.tar.gz 6fa486bc4c6af2bc692fe25f1d0e7737f05fa7d4 SOURCES/openvswitch-3.3.0.tar.gz 8509a716f9f936526f64fb23f313c5a9baf2f123 SOURCES/pyelftools-0.27.tar.gz -d34f96421a86004aa5d26ecf975edefd09f948b1 SOURCES/Pygments-1.4.tar.gz -3a11f130c63b057532ca37fe49c8967d0cbae1d5 SOURCES/Sphinx-1.2.3.tar.gz 061198752d3d8b64d33113b7c8c1e272c973403d SOURCES/dpdk-23.11.tar.xz diff --git a/SOURCES/openvswitch-3.3.0.patch b/SOURCES/openvswitch-3.3.0.patch index adebfb4..b3ca344 100644 --- a/SOURCES/openvswitch-3.3.0.patch +++ b/SOURCES/openvswitch-3.3.0.patch @@ -1,8 +1,72 @@ +diff --git a/.ci/dpdk-prepare.sh b/.ci/dpdk-prepare.sh +index f7e6215dda..4424f9eb97 100755 +--- a/.ci/dpdk-prepare.sh ++++ b/.ci/dpdk-prepare.sh +@@ -8,4 +8,4 @@ set -ev + # https://github.com/pypa/pip/issues/10655 + pip3 install --disable-pip-version-check --user wheel + pip3 install --disable-pip-version-check --user pyelftools +-pip3 install --user 'meson==0.53.2' ++pip3 install --user 'meson>=1.4,<1.5' +diff --git a/.ci/linux-prepare.sh b/.ci/linux-prepare.sh +index 5028bdc442..2a191b57fb 100755 +--- a/.ci/linux-prepare.sh ++++ b/.ci/linux-prepare.sh +@@ -23,7 +23,7 @@ cd .. + # https://github.com/pypa/pip/issues/10655 + pip3 install --disable-pip-version-check --user wheel + pip3 install --disable-pip-version-check --user \ +- flake8 'hacking>=3.0' netaddr pyparsing sarif-tools sphinx setuptools ++ flake8 netaddr pyparsing sarif-tools sphinx setuptools + + # Install python test dependencies + pip3 install -r python/test_requirements.txt +diff --git a/.cirrus.yml b/.cirrus.yml +index d8a9722809..8db385f002 100644 +--- a/.cirrus.yml ++++ b/.cirrus.yml +@@ -2,7 +2,7 @@ freebsd_build_task: + + freebsd_instance: + matrix: +- image_family: freebsd-13-2-snap ++ image_family: freebsd-13-3-snap + image_family: freebsd-14-0-snap + cpu: 4 + memory: 4G diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml -index fc75581486..6f5139304a 100644 +index fc75581486..44491db3e3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml -@@ -238,6 +238,14 @@ jobs: +@@ -2,6 +2,9 @@ name: Build and Test + + on: [push, pull_request] + ++env: ++ python_default: 3.12 ++ + jobs: + build-dpdk: + env: +@@ -54,7 +57,7 @@ jobs: + if: steps.dpdk_cache.outputs.cache-hit != 'true' + uses: actions/setup-python@v5 + with: +- python-version: '3.9' ++ python-version: ${{ env.python_default }} + + - name: update APT cache + if: steps.dpdk_cache.outputs.cache-hit != 'true' +@@ -217,7 +220,7 @@ jobs: + - name: set up python + uses: actions/setup-python@v5 + with: +- python-version: '3.9' ++ python-version: ${{ env.python_default }} + + - name: cache + if: matrix.dpdk != '' || matrix.dpdk_shared != '' +@@ -238,6 +241,14 @@ jobs: if: matrix.m32 != '' run: sudo apt install -y gcc-multilib @@ -17,6 +81,24 @@ index fc75581486..6f5139304a 100644 - name: prepare run: ./.ci/linux-prepare.sh +@@ -346,7 +357,7 @@ jobs: + - name: set up python + uses: actions/setup-python@v5 + with: +- python-version: '3.9' ++ python-version: ${{ env.python_default }} + + - name: get cached dpdk-dir + uses: actions/cache/restore@v4 +@@ -399,7 +410,7 @@ jobs: + - name: set up python + uses: actions/setup-python@v5 + with: +- python-version: '3.9' ++ python-version: ${{ env.python_default }} + - name: install dependencies + run: brew install automake libtool + - name: prepare diff --git a/AUTHORS.rst b/AUTHORS.rst index aa9284fb16..80678854bd 100644 --- a/AUTHORS.rst @@ -29,6 +111,22 @@ index aa9284fb16..80678854bd 100644 Dhaval Badiani dbadiani@vmware.com DK Moon Ding Zhi zhi.ding@6wind.com +diff --git a/Documentation/intro/install/general.rst b/Documentation/intro/install/general.rst +index 19e360d47c..7eb3a5d370 100644 +--- a/Documentation/intro/install/general.rst ++++ b/Documentation/intro/install/general.rst +@@ -176,10 +176,7 @@ following to obtain better warnings: + + - clang, version 3.4 or later + +-- flake8 along with the hacking flake8 plugin (for Python code). The automatic +- flake8 check that runs against Python code has some warnings enabled that +- come from the "hacking" flake8 plugin. If it's not installed, the warnings +- just won't occur until it's run on a system with "hacking" installed. ++- flake8 (for Python code) + + - the python packages listed in "python/test_requirements.txt" (compatible + with pip). If they are installed, the pytest-based Python unit tests will diff --git a/Documentation/intro/install/windows.rst b/Documentation/intro/install/windows.rst index fce099d5dc..efdb8aebce 100644 --- a/Documentation/intro/install/windows.rst @@ -60,6 +158,27 @@ index fce099d5dc..efdb8aebce 100644 --with-vstudiotarget="" \ --with-vstudiotargetver="" +diff --git a/Makefile.am b/Makefile.am +index 94f488d183..0b7c832469 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -400,16 +400,10 @@ ALL_LOCAL += flake8-check + # F811 redefinition of unused from line (only from flake8 v2.0) + # D*** -- warnings from flake8-docstrings plugin + # H*** -- warnings from flake8 hacking plugin (custom style checks beyond PEP8) +-# H231 Python 3.x incompatible 'except x,y:' construct +-# H232 Python 3.x incompatible octal 077 should be written as 0o77 +-# H233 Python 3.x incompatible use of print operator +-# H238 old style class declaration, use new style (inherit from `object`) +-FLAKE8_SELECT = H231,H232,H233,H238 + FLAKE8_IGNORE = E121,E123,E125,E126,E127,E128,E129,E131,E203,E722,W503,W504,F811,D,H,I + flake8-check: $(FLAKE8_PYFILES) + $(FLAKE8_WERROR)$(AM_V_GEN) \ + src='$^' && \ +- flake8 $$src --select=$(FLAKE8_SELECT) $(FLAKE8_FLAGS) && \ + flake8 $$src --ignore=$(FLAKE8_IGNORE) $(FLAKE8_FLAGS) && \ + touch $@ + endif diff --git a/NEWS b/NEWS index 8888fb3ec5..4bfb341cf4 100644 --- a/NEWS @@ -98,6 +217,43 @@ index 2049ddaa26..22c767a4ce 100644 openvswitch (3.3.0-1) unstable; urgency=low * New upstream version +diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in +index 7945162f9f..bc7ac55237 100755 +--- a/ipsec/ovs-monitor-ipsec.in ++++ b/ipsec/ovs-monitor-ipsec.in +@@ -457,14 +457,30 @@ conn prevent_unencrypted_vxlan + CERTKEY_PREFIX = "ovs_certkey_" + + def __init__(self, libreswan_root_prefix, args): ++ # Collect version infromation ++ self.IPSEC = libreswan_root_prefix + "/usr/sbin/ipsec" ++ proc = subprocess.Popen([self.IPSEC, "--version"], ++ stdout=subprocess.PIPE, ++ encoding="latin1") ++ pout, perr = proc.communicate() ++ ++ v = re.match("^Libreswan (.*)$", pout) ++ try: ++ version = int(v.group(1).split(".")[0]) ++ except: ++ version = 0 ++ ++ if version >= 4: ++ ipsec_d = args.ipsec_d if args.ipsec_d else "/var/lib/ipsec/nss" ++ else: ++ ipsec_d = args.ipsec_d if args.ipsec_d else "/etc/ipsec.d" ++ + ipsec_conf = args.ipsec_conf if args.ipsec_conf else "/etc/ipsec.conf" +- ipsec_d = args.ipsec_d if args.ipsec_d else "/etc/ipsec.d" + ipsec_secrets = (args.ipsec_secrets if args.ipsec_secrets + else "/etc/ipsec.secrets") + ipsec_ctl = (args.ipsec_ctl if args.ipsec_ctl + else "/run/pluto/pluto.ctl") + +- self.IPSEC = libreswan_root_prefix + "/usr/sbin/ipsec" + self.IPSEC_CONF = libreswan_root_prefix + ipsec_conf + self.IPSEC_SECRETS = libreswan_root_prefix + ipsec_secrets + self.IPSEC_D = "sql:" + libreswan_root_prefix + ipsec_d diff --git a/lib/bfd.c b/lib/bfd.c index 9af258917b..b8149e7897 100644 --- a/lib/bfd.c @@ -118,10 +274,21 @@ index 9af258917b..b8149e7897 100644 bfd_put_details(&ds, bfd); VLOG_INFO("%s", ds_cstr(&ds)); diff --git a/lib/conntrack.c b/lib/conntrack.c -index 013709bd62..6d02eaba8b 100644 +index 013709bd62..3c652aeb70 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c -@@ -2637,25 +2637,19 @@ conntrack_dump_start(struct conntrack *ct, struct conntrack_dump *dump, +@@ -2572,7 +2572,9 @@ tuple_to_conn_key(const struct ct_dpif_tuple *tuple, uint16_t zone, + key->src.icmp_type = tuple->icmp_type; + key->src.icmp_code = tuple->icmp_code; + key->dst.icmp_id = tuple->icmp_id; +- key->dst.icmp_type = reverse_icmp_type(tuple->icmp_type); ++ key->dst.icmp_type = (tuple->ip_proto == IPPROTO_ICMP) ++ ? reverse_icmp_type(tuple->icmp_type) ++ : reverse_icmp6_type(tuple->icmp_type); + key->dst.icmp_code = tuple->icmp_code; + } else { + key->src.port = tuple->src_port; +@@ -2637,25 +2639,19 @@ conntrack_dump_start(struct conntrack *ct, struct conntrack_dump *dump, dump->ct = ct; *ptot_bkts = 1; /* Need to clean up the callers. */ @@ -188,8 +355,74 @@ index 305822293b..df7bf8e6b3 100644 } if (dp_packet_hwol_tx_ip_csum(p)) { +diff --git a/lib/dp-packet.h b/lib/dp-packet.h +index 2fa17d8140..3622764c47 100644 +--- a/lib/dp-packet.h ++++ b/lib/dp-packet.h +@@ -1300,6 +1300,14 @@ dp_packet_hwol_set_tunnel_vxlan(struct dp_packet *b) + *dp_packet_ol_flags_ptr(b) |= DP_PACKET_OL_TX_TUNNEL_VXLAN; + } + ++/* Clears tunnel offloading marks. */ ++static inline void ++dp_packet_hwol_reset_tunnel(struct dp_packet *b) ++{ ++ *dp_packet_ol_flags_ptr(b) &= ~(DP_PACKET_OL_TX_TUNNEL_VXLAN | ++ DP_PACKET_OL_TX_TUNNEL_GENEVE); ++} ++ + /* Mark packet 'b' as a tunnel packet with outer IPv4 header. */ + static inline void + dp_packet_hwol_set_tx_outer_ipv4(struct dp_packet *b) +diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c +index 46e24d204d..99ff9b3693 100644 +--- a/lib/dpif-netdev.c ++++ b/lib/dpif-netdev.c +@@ -115,6 +115,7 @@ COVERAGE_DEFINE(datapath_drop_lock_error); + COVERAGE_DEFINE(datapath_drop_userspace_action_error); + COVERAGE_DEFINE(datapath_drop_tunnel_push_error); + COVERAGE_DEFINE(datapath_drop_tunnel_pop_error); ++COVERAGE_DEFINE(datapath_drop_tunnel_tso_recirc); + COVERAGE_DEFINE(datapath_drop_recirc_error); + COVERAGE_DEFINE(datapath_drop_invalid_port); + COVERAGE_DEFINE(datapath_drop_invalid_bond); +@@ -8912,6 +8913,34 @@ static void + dp_netdev_recirculate(struct dp_netdev_pmd_thread *pmd, + struct dp_packet_batch *packets) + { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ size_t i, size = dp_packet_batch_size(packets); ++ struct dp_packet *packet; ++ ++ DP_PACKET_BATCH_REFILL_FOR_EACH (i, size, packet, packets) { ++ if (dp_packet_hwol_is_tunnel_geneve(packet) || ++ dp_packet_hwol_is_tunnel_vxlan(packet)) { ++ ++ if (dp_packet_hwol_is_tso(packet)) { ++ /* Can't perform GSO in the middle of a pipeline. */ ++ COVERAGE_INC(datapath_drop_tunnel_tso_recirc); ++ dp_packet_delete(packet); ++ VLOG_WARN_RL(&rl, "Recirculating tunnel packets with " ++ "TSO is not supported"); ++ continue; ++ } ++ /* Have to fix all the checksums before re-parsing, because the ++ * packet will be treated as having a single set of headers. */ ++ dp_packet_ol_send_prepare(packet, 0); ++ /* This packet must not be marked with anything tunnel-related. */ ++ dp_packet_hwol_reset_tunnel(packet); ++ /* Clear inner offsets. Other ones are collateral, but they will ++ * be re-initialized on re-parsing. */ ++ dp_packet_reset_offsets(packet); ++ } ++ dp_packet_batch_refill(packets, packet, i); ++ } ++ + dp_netdev_input__(pmd, packets, true, 0); + } + diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c -index 45f61930d4..8c52accff9 100644 +index 45f61930d4..9249b9e9c6 100644 --- a/lib/netdev-dpdk.c +++ b/lib/netdev-dpdk.c @@ -607,6 +607,9 @@ int netdev_dpdk_get_vid(const struct netdev_dpdk *dev); @@ -202,7 +435,52 @@ index 45f61930d4..8c52accff9 100644 static bool is_dpdk_class(const struct netdev_class *class) { -@@ -2569,9 +2572,29 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk *dev, struct rte_mbuf *mbuf) +@@ -1351,6 +1354,18 @@ dpdk_eth_dev_init(struct netdev_dpdk *dev) + info.tx_offload_capa &= ~RTE_ETH_TX_OFFLOAD_TCP_CKSUM; + } + ++ if (!strcmp(info.driver_name, "net_ice") ++ || !strcmp(info.driver_name, "net_i40e")) { ++ /* FIXME: Driver advertises the capability but doesn't seem ++ * to actually support it correctly. Can remove this once ++ * the driver is fixed on DPDK side. */ ++ VLOG_INFO("%s: disabled Tx outer udp checksum offloads for a " ++ "net/ice or net/i40e port.", netdev_get_name(&dev->up)); ++ info.tx_offload_capa &= ~RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM; ++ info.tx_offload_capa &= ~RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO; ++ info.tx_offload_capa &= ~RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO; ++ } ++ + if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) { + dev->hw_ol_features |= NETDEV_TX_IPV4_CKSUM_OFFLOAD; + } else { +@@ -2364,17 +2379,16 @@ netdev_dpdk_set_config(struct netdev *netdev, const struct smap *args, + struct eth_addr mac; + + if (!dpdk_port_is_representor(dev)) { +- VLOG_WARN_BUF(errp, "'%s' is trying to set the VF MAC '%s' " +- "but 'options:dpdk-vf-mac' is only supported for " +- "VF representors.", +- netdev_get_name(netdev), vf_mac); ++ VLOG_WARN("'%s' is trying to set the VF MAC '%s' " ++ "but 'options:dpdk-vf-mac' is only supported for " ++ "VF representors.", ++ netdev_get_name(netdev), vf_mac); + } else if (!eth_addr_from_string(vf_mac, &mac)) { +- VLOG_WARN_BUF(errp, "interface '%s': cannot parse VF MAC '%s'.", +- netdev_get_name(netdev), vf_mac); ++ VLOG_WARN("interface '%s': cannot parse VF MAC '%s'.", ++ netdev_get_name(netdev), vf_mac); + } else if (eth_addr_is_multicast(mac)) { +- VLOG_WARN_BUF(errp, +- "interface '%s': cannot set VF MAC to multicast " +- "address '%s'.", netdev_get_name(netdev), vf_mac); ++ VLOG_WARN("interface '%s': cannot set VF MAC to multicast " ++ "address '%s'.", netdev_get_name(netdev), vf_mac); + } else if (!eth_addr_equals(dev->requested_hwaddr, mac)) { + dev->requested_hwaddr = mac; + netdev_request_reconfigure(netdev); +@@ -2569,21 +2583,57 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk *dev, struct rte_mbuf *mbuf) struct dp_packet *pkt = CONTAINER_OF(mbuf, struct dp_packet, mbuf); struct tcp_header *th; @@ -235,7 +513,73 @@ index 45f61930d4..8c52accff9 100644 return true; } -@@ -2664,6 +2687,35 @@ netdev_dpdk_prep_hwol_batch(struct netdev_dpdk *dev, struct rte_mbuf **pkts, + /* If packet is vxlan or geneve tunnel packet, calculate outer + * l2 len and outer l3 len. Inner l2/l3/l4 len are calculated + * before. */ +- if (mbuf->ol_flags & +- (RTE_MBUF_F_TX_TUNNEL_GENEVE | RTE_MBUF_F_TX_TUNNEL_VXLAN)) { ++ const uint64_t tunnel_type = mbuf->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK; ++ if (tunnel_type == RTE_MBUF_F_TX_TUNNEL_GENEVE || ++ tunnel_type == RTE_MBUF_F_TX_TUNNEL_VXLAN) { + mbuf->outer_l2_len = (char *) dp_packet_l3(pkt) - + (char *) dp_packet_eth(pkt); + mbuf->outer_l3_len = (char *) dp_packet_l4(pkt) - + (char *) dp_packet_l3(pkt); ++ ++ /* If neither inner checksums nor TSO is requested, inner marks ++ * should not be set. */ ++ if (!(mbuf->ol_flags & (RTE_MBUF_F_TX_IP_CKSUM | ++ RTE_MBUF_F_TX_L4_MASK | ++ RTE_MBUF_F_TX_TCP_SEG))) { ++ mbuf->ol_flags &= ~(RTE_MBUF_F_TX_IPV4 | ++ RTE_MBUF_F_TX_IPV6); ++ } ++ } else if (OVS_UNLIKELY(tunnel_type)) { ++ VLOG_WARN_RL(&rl, "%s: Unexpected tunnel type: %#"PRIx64, ++ netdev_get_name(&dev->up), tunnel_type); ++ netdev_dpdk_mbuf_dump(netdev_get_name(&dev->up), ++ "Packet with unexpected tunnel type", mbuf); ++ return false; + } else { + mbuf->l2_len = (char *) dp_packet_l3(pkt) - + (char *) dp_packet_eth(pkt); +@@ -2602,15 +2652,14 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk *dev, struct rte_mbuf *mbuf) + } + } + +- if (mbuf->ol_flags & RTE_MBUF_F_TX_TCP_CKSUM) { ++ if ((mbuf->ol_flags & RTE_MBUF_F_TX_L4_MASK) == RTE_MBUF_F_TX_TCP_CKSUM) { + if (!th) { + VLOG_WARN_RL(&rl, "%s: TCP offloading without L4 header" + " pkt len: %"PRIu32"", dev->up.name, mbuf->pkt_len); + return false; + } + +- if (mbuf->ol_flags & (RTE_MBUF_F_TX_TUNNEL_GENEVE | +- RTE_MBUF_F_TX_TUNNEL_VXLAN)) { ++ if (tunnel_type) { + mbuf->tso_segsz = dev->mtu - mbuf->l2_len - mbuf->l3_len - + mbuf->l4_len - mbuf->outer_l3_len; + } else { +@@ -2629,11 +2678,14 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk *dev, struct rte_mbuf *mbuf) + return false; + } + } ++ } + +- if (mbuf->ol_flags & RTE_MBUF_F_TX_IPV4) { +- mbuf->ol_flags |= RTE_MBUF_F_TX_IP_CKSUM; +- } ++ /* If L4 checksum is requested, IPv4 should be requested as well. */ ++ if (mbuf->ol_flags & RTE_MBUF_F_TX_L4_MASK ++ && mbuf->ol_flags & RTE_MBUF_F_TX_IPV4) { ++ mbuf->ol_flags |= RTE_MBUF_F_TX_IP_CKSUM; + } ++ + return true; + } + +@@ -2664,6 +2716,35 @@ netdev_dpdk_prep_hwol_batch(struct netdev_dpdk *dev, struct rte_mbuf **pkts, return cnt; } @@ -271,7 +615,7 @@ index 45f61930d4..8c52accff9 100644 /* Tries to transmit 'pkts' to txq 'qid' of device 'dev'. Takes ownership of * 'pkts', even in case of failure. * -@@ -2680,6 +2732,8 @@ netdev_dpdk_eth_tx_burst(struct netdev_dpdk *dev, int qid, +@@ -2680,6 +2761,8 @@ netdev_dpdk_eth_tx_burst(struct netdev_dpdk *dev, int qid, VLOG_WARN_RL(&rl, "%s: Output batch contains invalid packets. " "Only %u/%u are valid: %s", netdev_get_name(&dev->up), nb_tx_prep, cnt, rte_strerror(rte_errno)); @@ -326,6 +670,73 @@ index cd7e85a818..e8bbf8d514 100644 unixctl_command_reply(conn, "OK"); } else { unixctl_command_reply_error(conn, error); +diff --git a/lib/netlink-protocol.h b/lib/netlink-protocol.h +index 6eaa7035a4..e4bb28ac9f 100644 +--- a/lib/netlink-protocol.h ++++ b/lib/netlink-protocol.h +@@ -155,6 +155,11 @@ enum { + #define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + #endif + ++/* Introduced in v4.4. */ ++#ifndef NLM_F_DUMP_FILTERED ++#define NLM_F_DUMP_FILTERED 0x20 ++#endif ++ + /* These were introduced all together in 2.6.14. (We want our programs to + * support the newer kernel features even if compiled with older headers.) */ + #ifndef NETLINK_ADD_MEMBERSHIP +@@ -168,6 +173,11 @@ enum { + #define NETLINK_LISTEN_ALL_NSID 8 + #endif + ++/* Strict checking of netlink arguments introduced in Linux kernel v4.20. */ ++#ifndef NETLINK_GET_STRICT_CHK ++#define NETLINK_GET_STRICT_CHK 12 ++#endif ++ + /* These were introduced all together in 2.6.23. (We want our programs to + * support the newer kernel features even if compiled with older headers.) */ + #ifndef CTRL_ATTR_MCAST_GRP_MAX +diff --git a/lib/netlink-socket.c b/lib/netlink-socket.c +index 80da20d9f0..5cb1fc89ae 100644 +--- a/lib/netlink-socket.c ++++ b/lib/netlink-socket.c +@@ -205,6 +205,15 @@ nl_sock_create(int protocol, struct nl_sock **sockp) + } + } + ++ /* Strict checking only supported for NETLINK_ROUTE. */ ++ if (protocol == NETLINK_ROUTE ++ && setsockopt(sock->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, ++ &one, sizeof one) < 0) { ++ VLOG_RL(&rl, errno == ENOPROTOOPT ? VLL_DBG : VLL_WARN, ++ "netlink: could not enable strict checking (%s)", ++ ovs_strerror(errno)); ++ } ++ + retval = get_socket_rcvbuf(sock->fd); + if (retval < 0) { + retval = -retval; +diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c +index d3d42b4148..232ebeb97b 100644 +--- a/lib/ofpbuf.c ++++ b/lib/ofpbuf.c +@@ -197,12 +197,12 @@ ofpbuf_clone_with_headroom(const struct ofpbuf *b, size_t headroom) + struct ofpbuf *new_buffer; + + new_buffer = ofpbuf_clone_data_with_headroom(b->data, b->size, headroom); +- if (b->header) { ++ if (new_buffer->data && b->header) { + ptrdiff_t header_offset = (char *) b->header - (char *) b->data; + + new_buffer->header = (char *) new_buffer->data + header_offset; + } +- if (b->msg) { ++ if (new_buffer->data && b->msg) { + ptrdiff_t msg_offset = (char *) b->msg - (char *) b->data; + + new_buffer->msg = (char *) new_buffer->data + msg_offset; diff --git a/lib/ovs-router.c b/lib/ovs-router.c index ca014d80ed..3d84c9a30a 100644 --- a/lib/ovs-router.c @@ -367,6 +778,207 @@ index eb4ff85d9e..d7dc7e55f3 100644 void ovs_router_flush(void); void ovs_router_disable_system_routing_table(void); +diff --git a/lib/route-table.c b/lib/route-table.c +index 9927dcc185..f1fe32714e 100644 +--- a/lib/route-table.c ++++ b/lib/route-table.c +@@ -26,6 +26,7 @@ + #include + #include + ++#include "coverage.h" + #include "hash.h" + #include "netdev.h" + #include "netlink.h" +@@ -44,6 +45,8 @@ + + VLOG_DEFINE_THIS_MODULE(route_table); + ++COVERAGE_DEFINE(route_table_dump); ++ + struct route_data { + /* Copied from struct rtmsg. */ + unsigned char rtm_dst_len; +@@ -80,7 +83,7 @@ static struct nln_notifier *name_notifier = NULL; + + static bool route_table_valid = false; + +-static int route_table_reset(void); ++static void route_table_reset(void); + static void route_table_handle_msg(const struct route_table_msg *); + static int route_table_parse(struct ofpbuf *, struct route_table_msg *); + static void route_table_change(const struct route_table_msg *, void *); +@@ -153,26 +156,22 @@ route_table_wait(void) + ovs_mutex_unlock(&route_table_mutex); + } + +-static int +-route_table_reset(void) ++static bool ++route_table_dump_one_table(unsigned char id) + { +- struct nl_dump dump; +- struct rtgenmsg *rtgenmsg; + uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; + struct ofpbuf request, reply, buf; +- +- route_map_clear(); +- netdev_get_addrs_list_flush(); +- route_table_valid = true; +- rt_change_seq++; ++ struct rtmsg *rq_msg; ++ bool filtered = true; ++ struct nl_dump dump; + + ofpbuf_init(&request, 0); + +- nl_msg_put_nlmsghdr(&request, sizeof *rtgenmsg, RTM_GETROUTE, +- NLM_F_REQUEST); ++ nl_msg_put_nlmsghdr(&request, sizeof *rq_msg, RTM_GETROUTE, NLM_F_REQUEST); + +- rtgenmsg = ofpbuf_put_zeros(&request, sizeof *rtgenmsg); +- rtgenmsg->rtgen_family = AF_UNSPEC; ++ rq_msg = ofpbuf_put_zeros(&request, sizeof *rq_msg); ++ rq_msg->rtm_family = AF_UNSPEC; ++ rq_msg->rtm_table = id; + + nl_dump_start(&dump, NETLINK_ROUTE, &request); + ofpbuf_uninit(&request); +@@ -182,12 +181,43 @@ route_table_reset(void) + struct route_table_msg msg; + + if (route_table_parse(&reply, &msg)) { ++ struct nlmsghdr *nlmsghdr = nl_msg_nlmsghdr(&reply); ++ ++ /* Older kernels do not support filtering. */ ++ if (!(nlmsghdr->nlmsg_flags & NLM_F_DUMP_FILTERED)) { ++ filtered = false; ++ } + route_table_handle_msg(&msg); + } + } + ofpbuf_uninit(&buf); ++ nl_dump_done(&dump); ++ ++ return filtered; ++} ++ ++static void ++route_table_reset(void) ++{ ++ unsigned char tables[] = { ++ RT_TABLE_DEFAULT, ++ RT_TABLE_MAIN, ++ RT_TABLE_LOCAL, ++ }; + +- return nl_dump_done(&dump); ++ route_map_clear(); ++ netdev_get_addrs_list_flush(); ++ route_table_valid = true; ++ rt_change_seq++; ++ ++ COVERAGE_INC(route_table_dump); ++ ++ for (size_t i = 0; i < ARRAY_SIZE(tables); i++) { ++ if (!route_table_dump_one_table(tables[i])) { ++ /* Got unfiltered reply, no need to dump further. */ ++ break; ++ } ++ } + } + + /* Return RTNLGRP_IPV4_ROUTE or RTNLGRP_IPV6_ROUTE on success, 0 on parse +@@ -203,6 +233,7 @@ route_table_parse(struct ofpbuf *buf, struct route_table_msg *change) + [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true }, + [RTA_MARK] = { .type = NL_A_U32, .optional = true }, + [RTA_PREFSRC] = { .type = NL_A_U32, .optional = true }, ++ [RTA_TABLE] = { .type = NL_A_U32, .optional = true }, + }; + + static const struct nl_policy policy6[] = { +@@ -211,6 +242,7 @@ route_table_parse(struct ofpbuf *buf, struct route_table_msg *change) + [RTA_MARK] = { .type = NL_A_U32, .optional = true }, + [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true }, + [RTA_PREFSRC] = { .type = NL_A_IPV6, .optional = true }, ++ [RTA_TABLE] = { .type = NL_A_U32, .optional = true }, + }; + + struct nlattr *attrs[ARRAY_SIZE(policy)]; +@@ -232,6 +264,7 @@ route_table_parse(struct ofpbuf *buf, struct route_table_msg *change) + + if (parsed) { + const struct nlmsghdr *nlmsg; ++ uint32_t table_id; + int rta_oif; /* Output interface index. */ + + nlmsg = buf->data; +@@ -247,6 +280,19 @@ route_table_parse(struct ofpbuf *buf, struct route_table_msg *change) + rtm->rtm_type != RTN_LOCAL) { + change->relevant = false; + } ++ ++ table_id = rtm->rtm_table; ++ if (attrs[RTA_TABLE]) { ++ table_id = nl_attr_get_u32(attrs[RTA_TABLE]); ++ } ++ /* Do not consider changes in non-standard routing tables. */ ++ if (table_id ++ && table_id != RT_TABLE_DEFAULT ++ && table_id != RT_TABLE_MAIN ++ && table_id != RT_TABLE_LOCAL) { ++ change->relevant = false; ++ } ++ + change->nlmsg_type = nlmsg->nlmsg_type; + change->rd.rtm_dst_len = rtm->rtm_dst_len + (ipv4 ? 96 : 0); + change->rd.local = rtm->rtm_type == RTN_LOCAL; +@@ -312,7 +358,9 @@ static void + route_table_change(const struct route_table_msg *change OVS_UNUSED, + void *aux OVS_UNUSED) + { +- route_table_valid = false; ++ if (!change || change->relevant) { ++ route_table_valid = false; ++ } + } + + static void +diff --git a/lib/vlog.c b/lib/vlog.c +index b2653142f3..e78c785f7b 100644 +--- a/lib/vlog.c ++++ b/lib/vlog.c +@@ -29,6 +29,7 @@ + #include + #include + #include "async-append.h" ++#include "backtrace.h" + #include "coverage.h" + #include "dirs.h" + #include "openvswitch/dynamic-string.h" +@@ -1274,8 +1275,9 @@ vlog_fatal(const struct vlog_module *module, const char *message, ...) + va_end(args); + } + +-/* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always +- * writes the message to stderr, even if the console destination is disabled. ++/* Attempts to log a stack trace, logs 'message' to 'module' at maximum ++ * verbosity, then calls abort(). Always writes the message to stderr, even ++ * if the console destination is disabled. + * + * Choose this function instead of vlog_fatal_valist() if the daemon monitoring + * facility should automatically restart the current daemon. */ +@@ -1289,6 +1291,10 @@ vlog_abort_valist(const struct vlog_module *module_, + * message written by the later ovs_abort_valist(). */ + module->levels[VLF_CONSOLE] = VLL_OFF; + ++ /* Printing the stack trace before the 'message', because the 'message' ++ * will flush the async log queue (VLL_EMER). With a different order we ++ * would need to flush the queue manually again. */ ++ log_backtrace(); + vlog_valist(module, VLL_EMER, message, args); + ovs_abort_valist(0, message, args); + } diff --git a/m4/ax_check_openssl.m4 b/m4/ax_check_openssl.m4 index 281d4dc65e..faa5babde2 100644 --- a/m4/ax_check_openssl.m4 @@ -494,8 +1106,58 @@ index b86e7fe07e..87506aa785 100644 } void +diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c +index b5cbeed878..a046f8a339 100644 +--- a/ofproto/ofproto-dpif-upcall.c ++++ b/ofproto/ofproto-dpif-upcall.c +@@ -59,6 +59,7 @@ COVERAGE_DEFINE(handler_duplicate_upcall); + COVERAGE_DEFINE(revalidate_missed_dp_flow); + COVERAGE_DEFINE(ukey_dp_change); + COVERAGE_DEFINE(ukey_invalid_stat_reset); ++COVERAGE_DEFINE(ukey_replace_contention); + COVERAGE_DEFINE(upcall_flow_limit_grew); + COVERAGE_DEFINE(upcall_flow_limit_hit); + COVERAGE_DEFINE(upcall_flow_limit_kill); +@@ -1428,8 +1429,6 @@ upcall_cb(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufi + } + + if (upcall.ukey && !ukey_install(udpif, upcall.ukey)) { +- static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 1); +- VLOG_WARN_RL(&rll, "upcall_cb failure: ukey installation fails"); + error = ENOSPC; + } + out: +@@ -1927,15 +1926,15 @@ try_ukey_replace(struct umap *umap, struct udpif_key *old_ukey, + transition_ukey(old_ukey, UKEY_DELETED); + transition_ukey(new_ukey, UKEY_VISIBLE); + replaced = true; ++ COVERAGE_INC(upcall_ukey_replace); ++ } else { ++ COVERAGE_INC(handler_duplicate_upcall); + } + ovs_mutex_unlock(&old_ukey->mutex); +- } +- +- if (replaced) { +- COVERAGE_INC(upcall_ukey_replace); + } else { +- COVERAGE_INC(handler_duplicate_upcall); ++ COVERAGE_INC(ukey_replace_contention); + } ++ + return replaced; + } + +@@ -2973,6 +2972,7 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge) + /* Handler threads could be holding a ukey lock while it installs a + * new flow, so don't hang around waiting for access to it. */ + if (ovs_mutex_trylock(&ukey->mutex)) { ++ COVERAGE_INC(upcall_ukey_contention); + continue; + } + ukey_state = ukey->state; diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c -index 1cf4d5f7c9..89f183182e 100644 +index 1cf4d5f7c9..7c49508950 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -3815,6 +3815,8 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct xport *xport, @@ -507,6 +1169,852 @@ index 1cf4d5f7c9..89f183182e 100644 } err = tnl_route_lookup_flow(ctx, flow, &d_ip6, &s_ip6, &out_dev); +@@ -5078,10 +5080,37 @@ put_controller_user_action(struct xlate_ctx *ctx, + bool dont_send, bool continuation, + uint32_t recirc_id, int len, + enum ofp_packet_in_reason reason, ++ uint32_t provider_meter_id, + uint16_t controller_id) + { + struct user_action_cookie cookie; + ++ /* If the controller action didn't request a meter (indicated by a ++ * 'meter_id' argument other than NX_CTLR_NO_METER), see if one was ++ * configured through the "controller" virtual meter. ++ * ++ * Internally, ovs-vswitchd uses UINT32_MAX to indicate no meter is ++ * configured. */ ++ uint32_t meter_id; ++ if (provider_meter_id == UINT32_MAX) { ++ meter_id = ctx->xbridge->ofproto->up.controller_meter_id; ++ } else { ++ meter_id = provider_meter_id; ++ } ++ ++ size_t offset; ++ size_t ac_offset; ++ if (meter_id != UINT32_MAX) { ++ /* If controller meter is configured, generate ++ * clone(meter,userspace) action. */ ++ offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_SAMPLE); ++ nl_msg_put_u32(ctx->odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, ++ UINT32_MAX); ++ ac_offset = nl_msg_start_nested(ctx->odp_actions, ++ OVS_SAMPLE_ATTR_ACTIONS); ++ nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_METER, meter_id); ++ } ++ + memset(&cookie, 0, sizeof cookie); + cookie.type = USER_ACTION_COOKIE_CONTROLLER; + cookie.ofp_in_port = OFPP_NONE, +@@ -5099,6 +5128,11 @@ put_controller_user_action(struct xlate_ctx *ctx, + uint32_t pid = dpif_port_get_pid(ctx->xbridge->dpif, odp_port); + odp_put_userspace_action(pid, &cookie, sizeof cookie, ODPP_NONE, + false, ctx->odp_actions, NULL); ++ ++ if (meter_id != UINT32_MAX) { ++ nl_msg_end_nested(ctx->odp_actions, ac_offset); ++ nl_msg_end_nested(ctx->odp_actions, offset); ++ } + } + + static void +@@ -5143,32 +5177,6 @@ xlate_controller_action(struct xlate_ctx *ctx, int len, + } + recirc_refs_add(&ctx->xout->recircs, recirc_id); + +- /* If the controller action didn't request a meter (indicated by a +- * 'meter_id' argument other than NX_CTLR_NO_METER), see if one was +- * configured through the "controller" virtual meter. +- * +- * Internally, ovs-vswitchd uses UINT32_MAX to indicate no meter is +- * configured. */ +- uint32_t meter_id; +- if (provider_meter_id == UINT32_MAX) { +- meter_id = ctx->xbridge->ofproto->up.controller_meter_id; +- } else { +- meter_id = provider_meter_id; +- } +- +- size_t offset; +- size_t ac_offset; +- if (meter_id != UINT32_MAX) { +- /* If controller meter is configured, generate clone(meter, userspace) +- * action. */ +- offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_SAMPLE); +- nl_msg_put_u32(ctx->odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, +- UINT32_MAX); +- ac_offset = nl_msg_start_nested(ctx->odp_actions, +- OVS_SAMPLE_ATTR_ACTIONS); +- nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_METER, meter_id); +- } +- + /* Generate the datapath flows even if we don't send the packet-in + * so that debugging more closely represents normal state. */ + bool dont_send = false; +@@ -5176,12 +5184,7 @@ xlate_controller_action(struct xlate_ctx *ctx, int len, + dont_send = true; + } + put_controller_user_action(ctx, dont_send, false, recirc_id, len, +- reason, controller_id); +- +- if (meter_id != UINT32_MAX) { +- nl_msg_end_nested(ctx->odp_actions, ac_offset); +- nl_msg_end_nested(ctx->odp_actions, offset); +- } ++ reason, provider_meter_id, controller_id); + } + + /* Creates a frozen state, and allocates a unique recirc id for the given +@@ -5233,6 +5236,7 @@ finish_freezing__(struct xlate_ctx *ctx, uint8_t table) + put_controller_user_action(ctx, false, true, recirc_id, + ctx->pause->max_len, + ctx->pause->reason, ++ ctx->pause->provider_meter_id, + ctx->pause->controller_id); + } else { + if (ctx->recirc_update_dp_hash) { +diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c +index f59d69c4d1..fe034f9717 100644 +--- a/ofproto/ofproto-dpif.c ++++ b/ofproto/ofproto-dpif.c +@@ -3904,15 +3904,21 @@ port_query_by_name(const struct ofproto *ofproto_, const char *devname, + int error; + + if (sset_contains(&ofproto->ghost_ports, devname)) { +- const char *type = netdev_get_type_from_name(devname); +- + /* We may be called before ofproto->up.port_by_name is populated with + * the appropriate ofport. For this reason, we must get the name and +- * type from the netdev layer directly. */ +- if (type) { +- const struct ofport *ofport; ++ * type from the netdev layer directly. ++ * However, when a port deleted, the corresponding netdev is also ++ * removed from netdev_shash. netdev_get_type_from_name returns NULL ++ * in such case and we should try to get type from ofport->netdev. */ ++ const char *type = netdev_get_type_from_name(devname); ++ const struct ofport *ofport = ++ shash_find_data(&ofproto->up.port_by_name, devname); + +- ofport = shash_find_data(&ofproto->up.port_by_name, devname); ++ if (!type && ofport && ofport->netdev) { ++ type = netdev_get_type(ofport->netdev); ++ } ++ ++ if (type) { + ofproto_port->ofp_port = ofport ? ofport->ofp_port : OFPP_NONE; + ofproto_port->name = xstrdup(devname); + ofproto_port->type = xstrdup(type); +diff --git a/ovsdb/automake.mk b/ovsdb/automake.mk +index eba713bb6d..d484fe9deb 100644 +--- a/ovsdb/automake.mk ++++ b/ovsdb/automake.mk +@@ -114,11 +114,13 @@ $(OVSIDL_BUILT): ovsdb/ovsdb-idlc.in python/ovs/dirs.py + + # ovsdb-doc + EXTRA_DIST += ovsdb/ovsdb-doc ++FLAKE8_PYFILES += ovsdb/ovsdb-doc + OVSDB_DOC = $(run_python) $(srcdir)/ovsdb/ovsdb-doc + ovsdb/ovsdb-doc: python/ovs/dirs.py + + # ovsdb-dot + EXTRA_DIST += ovsdb/ovsdb-dot.in ovsdb/dot2pic ++FLAKE8_PYFILES += ovsdb/ovsdb-dot.in ovsdb/dot2pic + noinst_SCRIPTS += ovsdb/ovsdb-dot + CLEANFILES += ovsdb/ovsdb-dot + OVSDB_DOT = $(run_python) $(srcdir)/ovsdb/ovsdb-dot.in +diff --git a/ovsdb/dot2pic b/ovsdb/dot2pic +index 2f858e19d5..3db6444de6 100755 +--- a/ovsdb/dot2pic ++++ b/ovsdb/dot2pic +@@ -17,6 +17,7 @@ + import getopt + import sys + ++ + def dot2pic(src, dst): + scale = 1.0 + while True: +@@ -49,8 +50,8 @@ def dot2pic(src, dst): + dst.write("box at %f,%f wid %f height %f\n" + % (x, y, width, height)) + elif command == 'edge': +- tail = words[1] +- head = words[2] ++ # tail = words[1] ++ # head = words[2] + n = int(words[3]) + + # Extract x,y coordinates. +@@ -114,4 +115,3 @@ else: + if font_scale: + print(".ps %+d" % font_scale) + print(".PE") +- +diff --git a/ovsdb/ovsdb-doc b/ovsdb/ovsdb-doc +index 099770d253..2edf487a28 100755 +--- a/ovsdb/ovsdb-doc ++++ b/ovsdb/ovsdb-doc +@@ -14,9 +14,7 @@ + # See the License for the specific language governing permissions and + # limitations under the License. + +-from datetime import date + import getopt +-import os + import sys + import xml.dom.minidom + +@@ -24,10 +22,13 @@ import ovs.json + from ovs.db import error + import ovs.db.schema + +-from ovs_build_helpers.nroff import * ++from ovs_build_helpers.nroff import block_xml_to_nroff ++from ovs_build_helpers.nroff import escape_nroff_literal ++from ovs_build_helpers.nroff import text_to_nroff + + argv0 = sys.argv[0] + ++ + def typeAndConstraintsToNroff(column): + type = column.type.toEnglish(escape_nroff_literal) + constraints = column.type.constraintsToEnglish(escape_nroff_literal, +@@ -38,6 +39,7 @@ def typeAndConstraintsToNroff(column): + type += " (must be unique within table)" + return type + ++ + def columnGroupToNroff(table, groupXml, documented_columns): + introNodes = [] + columnNodes = [] +@@ -49,7 +51,10 @@ def columnGroupToNroff(table, groupXml, documented_columns): + if (columnNodes + and not (node.nodeType == node.TEXT_NODE + and node.data.isspace())): +- raise error.Error("text follows or inside : %s" % node) ++ raise error.Error( ++ "text follows or inside : %s" ++ % node ++ ) + introNodes += [node] + + summary = [] +@@ -65,15 +70,9 @@ def columnGroupToNroff(table, groupXml, documented_columns): + if node.hasAttribute('type'): + type_string = node.attributes['type'].nodeValue + type_json = ovs.json.from_string(str(type_string)) +- # py2 -> py3 means str -> bytes and unicode -> str +- try: +- if type(type_json) in (str, unicode): +- raise error.Error("%s %s:%s has invalid 'type': %s" +- % (table.name, name, key, type_json)) +- except: +- if type(type_json) in (bytes, str): +- raise error.Error("%s %s:%s has invalid 'type': %s" +- % (table.name, name, key, type_json)) ++ if type(type_json) in (bytes, str): ++ raise error.Error("%s %s:%s has invalid 'type': %s" ++ % (table.name, name, key, type_json)) + type_ = ovs.db.types.BaseType.from_json(type_json) + else: + type_ = column.type.value +@@ -91,10 +90,11 @@ def columnGroupToNroff(table, groupXml, documented_columns): + else: + if type_.type != column.type.value.type: + type_english = type_.toEnglish() ++ typeNroff += ", containing " + if type_english[0] in 'aeiou': +- typeNroff += ", containing an %s" % type_english ++ typeNroff += "an %s" % type_english + else: +- typeNroff += ", containing a %s" % type_english ++ typeNroff += "a %s" % type_english + constraints = ( + type_.constraintsToEnglish(escape_nroff_literal, + text_to_nroff)) +@@ -121,6 +121,7 @@ def columnGroupToNroff(table, groupXml, documented_columns): + raise error.Error("unknown element %s in " % node.tagName) + return summary, intro, body + ++ + def tableSummaryToNroff(summary, level=0): + s = "" + for type, name, arg in summary: +@@ -132,6 +133,7 @@ def tableSummaryToNroff(summary, level=0): + s += ".RE\n" + return s + ++ + def tableToNroff(schema, tableXml): + tableName = tableXml.attributes['name'].nodeValue + table = schema.tables[tableName] +@@ -156,20 +158,17 @@ def tableToNroff(schema, tableXml): + + return s + ++ + def docsToNroff(schemaFile, xmlFile, erFile, version=None): + schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile)) + doc = xml.dom.minidom.parse(xmlFile).documentElement + +- schemaDate = os.stat(schemaFile).st_mtime +- xmlDate = os.stat(xmlFile).st_mtime +- d = date.fromtimestamp(max(schemaDate, xmlDate)) +- + if doc.hasAttribute('name'): + manpage = doc.attributes['name'].nodeValue + else: + manpage = schema.name + +- if version == None: ++ if version is None: + version = "UNKNOWN" + + # Putting '\" p as the first line tells "man" that the manpage +@@ -194,7 +193,6 @@ def docsToNroff(schemaFile, xmlFile, erFile, version=None): + .PP + ''' % (manpage, schema.version, version, text_to_nroff(manpage), schema.name) + +- tables = "" + introNodes = [] + tableNodes = [] + summary = [] +@@ -237,8 +235,8 @@ Purpose + """ % (name, text_to_nroff(title)) + + if erFile: +- s += """ +-.\\" check if in troff mode (TTY) ++ s += r""" ++.\" check if in troff mode (TTY) + .if t \{ + .bp + .SH "TABLE RELATIONSHIPS" +@@ -248,8 +246,8 @@ database. Each node represents a table. Tables that are part of the + ``root set'' are shown with double borders. Each edge leads from the + table that contains it and points to the table that its value + represents. Edges are labeled with their column names, followed by a +-constraint on the number of allowed values: \\fB?\\fR for zero or one, +-\\fB*\\fR for zero or more, \\fB+\\fR for one or more. Thick lines ++constraint on the number of allowed values: \fB?\fR for zero or one, ++\fB*\fR for zero or more, \fB+\fR for one or more. Thick lines + represent strong references; thin lines represent weak references. + .RS -1in + """ +@@ -263,6 +261,7 @@ represent strong references; thin lines represent weak references. + s += tableToNroff(schema, node) + "\n" + return s + ++ + def usage(): + print("""\ + %(argv0)s: ovsdb schema documentation generator +@@ -278,6 +277,7 @@ The following options are also available: + """ % {'argv0': argv0}) + sys.exit(0) + ++ + if __name__ == "__main__": + try: + try: +diff --git a/ovsdb/ovsdb-dot.in b/ovsdb/ovsdb-dot.in +index 41b986c0ac..f1eefd49cb 100755 +--- a/ovsdb/ovsdb-dot.in ++++ b/ovsdb/ovsdb-dot.in +@@ -1,15 +1,13 @@ + #! @PYTHON3@ + +-from datetime import date + import ovs.db.error + import ovs.db.schema + import getopt +-import os +-import re + import sys + + argv0 = sys.argv[0] + ++ + def printEdge(tableName, type, baseType, label): + if baseType.ref_table_name: + if type.n_min == 0: +@@ -31,38 +29,42 @@ def printEdge(tableName, type, baseType, label): + options['label'] = '"%s%s"' % (label, arity) + if baseType.ref_type == 'weak': + options['style'] = 'dotted' +- print ("\t%s -> %s [%s];" % ( ++ print("\t%s -> %s [%s];" % ( + tableName, + baseType.ref_table_name, +- ', '.join(['%s=%s' % (k,v) for k,v in options.items()]))) ++ ', '.join(['%s=%s' % (k, v) for k, v in options.items()]))) ++ + + def schemaToDot(schemaFile, arrows): + schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile)) + +- print ("digraph %s {" % schema.name) +- print ('\trankdir=LR;') +- print ('\tsize="6.5,4";') +- print ('\tmargin="0";') +- print ("\tnode [shape=box];") ++ print("digraph %s {" % schema.name) ++ print('\trankdir=LR;') ++ print('\tsize="6.5,4";') ++ print('\tmargin="0";') ++ print("\tnode [shape=box];") + if not arrows: +- print ("\tedge [dir=none, arrowhead=none, arrowtail=none];") ++ print("\tedge [dir=none, arrowhead=none, arrowtail=none];") + for tableName, table in schema.tables.items(): + options = {} + if table.is_root: + options['style'] = 'bold' +- print ("\t%s [%s];" % ( ++ print("\t%s [%s];" % ( + tableName, +- ', '.join(['%s=%s' % (k,v) for k,v in options.items()]))) ++ ', '.join(['%s=%s' % (k, v) for k, v in options.items()]))) + for columnName, column in table.columns.items(): + if column.type.value: +- printEdge(tableName, column.type, column.type.key, "%s key" % columnName) +- printEdge(tableName, column.type, column.type.value, "%s value" % columnName) ++ printEdge(tableName, column.type, column.type.key, ++ "%s key" % columnName) ++ printEdge(tableName, column.type, column.type.value, ++ "%s value" % columnName) + else: + printEdge(tableName, column.type, column.type.key, columnName) +- print ("}"); ++ print("}") ++ + + def usage(): +- print ("""\ ++ print("""\ + %(argv0)s: compiles ovsdb schemas to graphviz format + Prints a .dot file that "dot" can render to an entity-relationship diagram + usage: %(argv0)s [OPTIONS] SCHEMA +@@ -75,12 +77,13 @@ The following options are also available: + """ % {'argv0': argv0}) + sys.exit(0) + ++ + if __name__ == "__main__": + try: + try: + options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', + ['no-arrows', +- 'help', 'version',]) ++ 'help', 'version']) + except getopt.GetoptError as geo: + sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) + sys.exit(1) +@@ -92,7 +95,7 @@ if __name__ == "__main__": + elif key in ['-h', '--help']: + usage() + elif key in ['-V', '--version']: +- print ("ovsdb-dot (Open vSwitch) @VERSION@") ++ print("ovsdb-dot (Open vSwitch) @VERSION@") + else: + sys.exit(0) + +diff --git a/ovsdb/raft.c b/ovsdb/raft.c +index f463afcb3d..ac3d37ac40 100644 +--- a/ovsdb/raft.c ++++ b/ovsdb/raft.c +@@ -81,6 +81,7 @@ enum raft_failure_test { + FT_STOP_RAFT_RPC, + FT_TRANSFER_LEADERSHIP, + FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ, ++ FT_TRANSFER_LEADERSHIP_AFTER_STARTING_TO_ADD, + }; + static enum raft_failure_test failure_test; + +@@ -280,6 +281,7 @@ struct raft { + /* Used for joining a cluster. */ + bool joining; /* Attempting to join the cluster? */ + struct sset remote_addresses; /* Addresses to try to find other servers. */ ++#define RAFT_JOIN_TIMEOUT_MS 1000 + long long int join_timeout; /* Time to re-send add server request. */ + + /* Used for leaving a cluster. */ +@@ -385,6 +387,7 @@ static void raft_get_servers_from_log(struct raft *, enum vlog_level); + static void raft_get_election_timer_from_log(struct raft *); + + static bool raft_handle_write_error(struct raft *, struct ovsdb_error *); ++static bool raft_has_uncommitted_configuration(const struct raft *); + + static void raft_run_reconfigure(struct raft *); + +@@ -1015,8 +1018,13 @@ raft_conn_update_probe_interval(struct raft *raft, struct raft_conn *r_conn) + * inactivity probe follower will just try to initiate election + * indefinitely staying in 'candidate' role. And the leader will continue + * to send heartbeats to the dead connection thinking that remote server +- * is still part of the cluster. */ +- int probe_interval = raft->election_timer + ELECTION_RANGE_MSEC; ++ * is still part of the cluster. ++ * ++ * While joining, the real value of the election timeout is not known to ++ * this server, so using the maximum. */ ++ int probe_interval = (raft->joining ? ELECTION_MAX_MSEC ++ : raft->election_timer) ++ + ELECTION_RANGE_MSEC; + + jsonrpc_session_set_probe_interval(r_conn->js, probe_interval); + } +@@ -1083,7 +1091,7 @@ raft_open(struct ovsdb_log *log, struct raft **raftp) + raft_start_election(raft, false, false); + } + } else { +- raft->join_timeout = time_msec() + 1000; ++ raft->join_timeout = time_msec() + RAFT_JOIN_TIMEOUT_MS; + } + + raft_reset_ping_timer(raft); +@@ -1261,10 +1269,30 @@ raft_transfer_leadership(struct raft *raft, const char *reason) + return; + } + +- struct raft_server *s; ++ struct raft_server **servers, *s; ++ uint64_t threshold = 0; ++ size_t n = 0, start, i; ++ ++ servers = xmalloc(hmap_count(&raft->servers) * sizeof *servers); ++ + HMAP_FOR_EACH (s, hmap_node, &raft->servers) { +- if (!uuid_equals(&raft->sid, &s->sid) +- && s->phase == RAFT_PHASE_STABLE) { ++ if (uuid_equals(&raft->sid, &s->sid) ++ || s->phase != RAFT_PHASE_STABLE) { ++ continue; ++ } ++ if (s->match_index > threshold) { ++ threshold = s->match_index; ++ } ++ servers[n++] = s; ++ } ++ ++ start = n ? random_range(n) : 0; ++ ++retry: ++ for (i = 0; i < n; i++) { ++ s = servers[(start + i) % n]; ++ ++ if (s->match_index >= threshold) { + struct raft_conn *conn = raft_find_conn_by_sid(raft, &s->sid); + if (!conn) { + continue; +@@ -1280,7 +1308,10 @@ raft_transfer_leadership(struct raft *raft, const char *reason) + .term = raft->term, + } + }; +- raft_send_to_conn(raft, &rpc, conn); ++ ++ if (!raft_send_to_conn(raft, &rpc, conn)) { ++ continue; ++ } + + raft_record_note(raft, "transfer leadership", + "transferring leadership to %s because %s", +@@ -1288,6 +1319,23 @@ raft_transfer_leadership(struct raft *raft, const char *reason) + break; + } + } ++ ++ if (n && i == n && threshold) { ++ if (threshold > raft->commit_index) { ++ /* Failed to transfer to servers with the highest 'match_index'. ++ * Try other servers that are not behind the majority. */ ++ threshold = raft->commit_index; ++ } else { ++ /* Try any other server. It is safe, because they either have all ++ * the append requests queued up for them before the leadership ++ * transfer message or their connection is broken and we will not ++ * transfer anyway. */ ++ threshold = 0; ++ } ++ goto retry; ++ } ++ ++ free(servers); + } + + /* Send a RemoveServerRequest to the rest of the servers in the cluster. +@@ -2078,7 +2126,7 @@ raft_run(struct raft *raft) + raft_start_election(raft, true, false); + } + } else { +- raft_start_election(raft, true, false); ++ raft_start_election(raft, hmap_count(&raft->servers) > 1, false); + } + + } +@@ -2088,7 +2136,7 @@ raft_run(struct raft *raft) + } + + if (raft->joining && time_msec() >= raft->join_timeout) { +- raft->join_timeout = time_msec() + 1000; ++ raft->join_timeout = time_msec() + RAFT_JOIN_TIMEOUT_MS; + LIST_FOR_EACH (conn, list_node, &raft->conns) { + raft_send_add_server_request(raft, conn); + } +@@ -2122,10 +2170,12 @@ raft_run(struct raft *raft) + raft_reset_ping_timer(raft); + } + ++ uint64_t interval = raft->joining ++ ? RAFT_JOIN_TIMEOUT_MS ++ : RAFT_TIMER_THRESHOLD(raft->election_timer); + cooperative_multitasking_set( + &raft_run_cb, (void *) raft, time_msec(), +- RAFT_TIMER_THRESHOLD(raft->election_timer) +- + RAFT_TIMER_THRESHOLD(raft->election_timer) / 10, "raft_run"); ++ interval + interval / 10, "raft_run"); + + /* Do this only at the end; if we did it as soon as we set raft->left or + * raft->failed in handling the RemoveServerReply, then it could easily +@@ -2696,15 +2746,22 @@ raft_become_follower(struct raft *raft) + * new configuration. Our AppendEntries processing will properly update + * the server configuration later, if necessary. + * ++ * However, since we're sending replies about a failure to add, those new ++ * servers has to be cleaned up. Otherwise, they will stuck in a 'CATCHUP' ++ * phase in case this server regains leadership before they join through ++ * the current new leader. They are not yet in 'raft->servers', so not ++ * part of the shared configuration. ++ * + * Also we do not complete commands here, as they can still be completed + * if their log entries have already been replicated to other servers. + * If the entries were actually committed according to the new leader, our + * AppendEntries processing will complete the corresponding commands. + */ + struct raft_server *s; +- HMAP_FOR_EACH (s, hmap_node, &raft->add_servers) { ++ HMAP_FOR_EACH_POP (s, hmap_node, &raft->add_servers) { + raft_send_add_server_reply__(raft, &s->sid, s->address, false, + RAFT_SERVER_LOST_LEADERSHIP); ++ raft_server_destroy(s); + } + if (raft->remove_server) { + raft_send_remove_server_reply__(raft, &raft->remove_server->sid, +@@ -2768,6 +2825,13 @@ raft_send_heartbeats(struct raft *raft) + raft_reset_ping_timer(raft); + } + ++static void ++raft_join_complete(struct raft *raft) ++{ ++ raft->joining = false; ++ raft_update_probe_intervals(raft); ++} ++ + /* Initializes the fields in 's' that represent the leader's view of the + * server. */ + static void +@@ -2805,6 +2869,18 @@ raft_become_leader(struct raft *raft) + raft_reset_election_timer(raft); + raft_reset_ping_timer(raft); + ++ if (raft->joining) { ++ /* It is possible that the server committing this one to the list of ++ * servers lost leadership before the entry is committed but after ++ * it was already replicated to majority of servers. In this case ++ * other servers will recognize this one as a valid cluster member ++ * and may transfer leadership to it and vote for it. This way ++ * we're becoming a cluster leader without receiving reply for a ++ * join request and will commit addition of this server ourselves. */ ++ VLOG_INFO_RL(&rl, "elected as leader while joining"); ++ raft_join_complete(raft); ++ } ++ + struct raft_server *s; + HMAP_FOR_EACH (s, hmap_node, &raft->servers) { + raft_server_init_leader(raft, s); +@@ -2963,12 +3039,12 @@ raft_update_commit_index(struct raft *raft, uint64_t new_commit_index) + } + + while (raft->commit_index < new_commit_index) { ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); + uint64_t index = ++raft->commit_index; + const struct raft_entry *e = raft_get_entry(raft, index); + + if (raft_entry_has_data(e)) { + struct raft_command *cmd = raft_find_command_by_eid(raft, &e->eid); +- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); + + if (cmd) { + if (!cmd->index && raft->role == RAFT_LEADER) { +@@ -3012,6 +3088,35 @@ raft_update_commit_index(struct raft *raft, uint64_t new_commit_index) + * reallocate raft->entries, which would invalidate 'e', so + * this case must be last, after the one for 'e->data'. */ + raft_run_reconfigure(raft); ++ } else if (e->servers && !raft_has_uncommitted_configuration(raft)) { ++ struct ovsdb_error *error; ++ struct raft_server *s; ++ struct hmap servers; ++ ++ error = raft_servers_from_json(e->servers, &servers); ++ ovs_assert(!error); ++ HMAP_FOR_EACH (s, hmap_node, &servers) { ++ struct raft_server *server = raft_find_server(raft, &s->sid); ++ ++ if (server && server->phase == RAFT_PHASE_COMMITTING) { ++ /* This server lost leadership while committing ++ * server 's', but it was committed later by a ++ * new leader. */ ++ server->phase = RAFT_PHASE_STABLE; ++ } ++ ++ if (raft->joining && uuid_equals(&s->sid, &raft->sid)) { ++ /* Leadership change happened before previous leader ++ * could commit the change of a servers list, but it ++ * was replicated and a new leader committed it. */ ++ VLOG_INFO_RL(&rl, ++ "added to configuration without reply " ++ "(eid: "UUID_FMT", commit index: %"PRIu64")", ++ UUID_ARGS(&e->eid), index); ++ raft_join_complete(raft); ++ } ++ } ++ raft_servers_destroy(&servers); + } + } + +@@ -3938,6 +4043,10 @@ raft_handle_add_server_request(struct raft *raft, + "to cluster "CID_FMT, s->nickname, SID_ARGS(&s->sid), + rq->address, CID_ARGS(&raft->cid)); + raft_send_append_request(raft, s, 0, "initialize new server"); ++ ++ if (failure_test == FT_TRANSFER_LEADERSHIP_AFTER_STARTING_TO_ADD) { ++ failure_test = FT_TRANSFER_LEADERSHIP; ++ } + } + + static void +@@ -3952,7 +4061,7 @@ raft_handle_add_server_reply(struct raft *raft, + } + + if (rpy->success) { +- raft->joining = false; ++ raft_join_complete(raft); + + /* It is tempting, at this point, to check that this server is part of + * the current configuration. However, this is not necessarily the +@@ -4926,6 +5035,7 @@ raft_get_election_timer_from_log(struct raft *raft) + break; + } + } ++ raft_update_probe_intervals(raft); + } + + static void +@@ -5063,6 +5173,8 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED, + } else if (!strcmp(test, + "transfer-leadership-after-sending-append-request")) { + failure_test = FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ; ++ } else if (!strcmp(test, "transfer-leadership-after-starting-to-add")) { ++ failure_test = FT_TRANSFER_LEADERSHIP_AFTER_STARTING_TO_ADD; + } else if (!strcmp(test, "transfer-leadership")) { + failure_test = FT_TRANSFER_LEADERSHIP; + } else if (!strcmp(test, "clear")) { +diff --git a/python/ovs/fatal_signal.py b/python/ovs/fatal_signal.py +index cb2e99e87d..16a7e78a03 100644 +--- a/python/ovs/fatal_signal.py ++++ b/python/ovs/fatal_signal.py +@@ -16,6 +16,7 @@ import atexit + import os + import signal + import sys ++import threading + + import ovs.vlog + +@@ -112,29 +113,29 @@ def _unlink(file_): + def _signal_handler(signr, _): + _call_hooks(signr) + +- # Re-raise the signal with the default handling so that the program +- # termination status reflects that we were killed by this signal. +- signal.signal(signr, signal.SIG_DFL) +- os.kill(os.getpid(), signr) +- + + def _atexit_handler(): + _call_hooks(0) + + +-recurse = False ++mutex = threading.Lock() + + + def _call_hooks(signr): +- global recurse +- if recurse: ++ global mutex ++ if not mutex.acquire(blocking=False): + return +- recurse = True + + for hook, cancel, run_at_exit in _hooks: + if signr != 0 or run_at_exit: + hook() + ++ if signr != 0: ++ # Re-raise the signal with the default handling so that the program ++ # termination status reflects that we were killed by this signal. ++ signal.signal(signr, signal.SIG_DFL) ++ os.kill(os.getpid(), signr) ++ + + _inited = False + +@@ -150,7 +151,9 @@ def _init(): + signal.SIGALRM] + + for signr in signals: +- if signal.getsignal(signr) == signal.SIG_DFL: ++ handler = signal.getsignal(signr) ++ if (handler == signal.SIG_DFL or ++ handler == signal.default_int_handler): + signal.signal(signr, _signal_handler) + atexit.register(_atexit_handler) + +@@ -165,7 +168,6 @@ def signal_alarm(timeout): + + if sys.platform == "win32": + import time +- import threading + + class Alarm (threading.Thread): + def __init__(self, timeout): +diff --git a/rhel/usr_lib_systemd_system_ovsdb-server.service b/rhel/usr_lib_systemd_system_ovsdb-server.service +index 49dc06e38c..558632320c 100644 +--- a/rhel/usr_lib_systemd_system_ovsdb-server.service ++++ b/rhel/usr_lib_systemd_system_ovsdb-server.service +@@ -29,3 +29,4 @@ ExecStop=/usr/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd stop + ExecReload=/usr/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd \ + ${OVS_USER_OPT} \ + --no-monitor restart $OPTIONS ++TimeoutSec=300 +diff --git a/tests/library.at b/tests/library.at +index 7b4acebb8a..d962e1b3fd 100644 +--- a/tests/library.at ++++ b/tests/library.at +@@ -230,7 +230,9 @@ AT_CHECK([ovstest test-util -voff -vfile:info '-vPATTERN:file:%c|%p|%m' --log-fi + [$exit_status], [], [stderr]) + + AT_CHECK([sed 's/\(opened log file\) .*/\1/ +-s/|[[^|]]*: /|/' test-util.log], [0], [dnl ++s/|[[^|]]*: /|/ ++/backtrace/d ++/|.*|/!d' test-util.log], [0], [dnl + vlog|INFO|opened log file + util|EMER|assertion false failed in test_assert() + ]) diff --git a/tests/nsh.at b/tests/nsh.at index 55296e5593..0040a50b36 100644 --- a/tests/nsh.at @@ -568,7 +2076,7 @@ index 55296e5593..0040a50b36 100644 AT_CHECK([ diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at -index e305e7b9cd..a1393f7f8e 100644 +index e305e7b9cd..3eaccb13a6 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -547,6 +547,23 @@ ovs-appctl time/warp 1000 100 @@ -595,7 +2103,65 @@ index e305e7b9cd..a1393f7f8e 100644 OVS_VSWITCHD_STOP() AT_CLEANUP -@@ -7653,12 +7670,14 @@ dummy@ovs-dummy: hit:0 missed:0 +@@ -6178,6 +6195,57 @@ AT_CHECK([test 1 = `$PYTHON3 "$top_srcdir/utilities/ovs-pcap.in" p2-tx.pcap | wc + OVS_VSWITCHD_STOP + AT_CLEANUP + ++AT_SETUP([ofproto-dpif - continuation with meters]) ++AT_KEYWORDS([continuations pause meters]) ++OVS_VSWITCHD_START ++add_of_ports br0 1 2 ++ ++dnl Add meter with id=1. ++AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br0 'meter=1 pktps bands=type=drop rate=1']) ++ ++AT_DATA([flows.txt], [dnl ++table=0 dl_dst=50:54:00:00:00:0a actions=goto_table(1) ++table=1 dl_dst=50:54:00:00:00:0a actions=controller(pause,meter_id=1) ++]) ++AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt]) ++ ++on_exit 'kill $(cat ovs-ofctl.pid)' ++AT_CAPTURE_FILE([ofctl_monitor.log]) ++AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in \ ++ --detach --no-chdir --pidfile 2> ofctl_monitor.log]) ++ ++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \ ++ 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)']) ++ ++OVS_WAIT_UNTIL([test $(wc -l < ofctl_monitor.log) -ge 2]) ++OVS_APP_EXIT_AND_WAIT([ovs-ofctl]) ++AT_CHECK([cat ofctl_monitor.log], [0], [dnl ++NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via action) data_len=14 (unbuffered) ++vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234 ++]) ++ ++AT_CHECK([ovs-appctl revalidator/purge], [0]) ++AT_CHECK([ovs-ofctl -O OpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl ++ n_packets=1, n_bytes=14, dl_dst=50:54:00:00:00:0a actions=goto_table:1 ++ table=1, n_packets=1, n_bytes=14, dl_dst=50:54:00:00:00:0a actions=controller(pause,meter_id=1) ++OFPST_FLOW reply (OF1.3): ++]) ++ ++AT_CHECK([ovs-ofctl -O OpenFlow13 dump-meters br0 | ofctl_strip | sort], [0], [dnl ++OFPST_METER_CONFIG reply (OF1.3): ++meter=1 pktps bands= ++type=drop rate=1 ++]) ++ ++AT_CHECK([ovs-ofctl -O OpenFlow13 meter-stats br0 | strip_timers], [0], [dnl ++OFPST_METER reply (OF1.3) (xid=0x2): ++meter:1 flow_count:0 packet_in_count:1 byte_in_count:14 duration:0.0s bands: ++0: packet_count:0 byte_count:0 ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_SETUP([ofproto-dpif - continuation with patch port]) + AT_KEYWORDS([continuations pause resume]) + OVS_VSWITCHD_START( +@@ -7653,12 +7721,14 @@ dummy@ovs-dummy: hit:0 missed:0 vm1 5/3: (dummy: ifindex=2011) ]) @@ -613,7 +2179,7 @@ index e305e7b9cd..a1393f7f8e 100644 dnl Prime ARP Cache for 1.1.2.92 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)']) -@@ -7669,10 +7688,13 @@ ovs-vsctl \ +@@ -7669,10 +7739,13 @@ ovs-vsctl \ --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" agent=127.0.0.1 \ header=128 sampling=1 polling=0 @@ -629,6 +2195,123 @@ index e305e7b9cd..a1393f7f8e 100644 ]) dnl add rule for int-br to force packet onto tunnel. There is no ifindex +diff --git a/tests/ovsdb-cluster.at b/tests/ovsdb-cluster.at +index 481afc08b3..9d8b4d06a4 100644 +--- a/tests/ovsdb-cluster.at ++++ b/tests/ovsdb-cluster.at +@@ -473,6 +473,112 @@ done + + AT_CLEANUP + ++AT_SETUP([OVSDB cluster - leadership change after replication while joining]) ++AT_KEYWORDS([ovsdb server negative unix cluster join]) ++ ++n=5 ++AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db dnl ++ $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) ++cid=$(ovsdb-tool db-cid s1.db) ++schema_name=$(ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema) ++for i in $(seq 2 $n); do ++ AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) ++done ++ ++on_exit 'kill $(cat *.pid)' ++on_exit " ++ for i in \$(ls $(pwd)/s[[0-$n]]); do ++ ovs-appctl --timeout 1 -t \$i cluster/status $schema_name; ++ done ++" ++ ++dnl Starting servers one by one asking all exisitng servers to transfer ++dnl leadership after append reply forcing the joining server to try another ++dnl one that will also transfer leadership. Since transfer is happening ++dnl after the servers update is replicated to other servers, one of the ++dnl other servers will actually commit it. It may be a new leader from ++dnl one of the old members or the new joining server itself. ++for i in $(seq $n); do ++ dnl Make sure that all already started servers joined the cluster. ++ for j in $(seq $((i - 1)) ); do ++ AT_CHECK([ovsdb_client_wait unix:s$j.ovsdb $schema_name connected]) ++ done ++ for j in $(seq $((i - 1)) ); do ++ OVS_WAIT_UNTIL([ovs-appctl -t "$(pwd)"/s$j \ ++ cluster/failure-test \ ++ transfer-leadership-after-sending-append-request \ ++ | grep -q "engaged"]) ++ done ++ ++ AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off \ ++ --detach --no-chdir --log-file=s$i.log \ ++ --pidfile=s$i.pid --unixctl=s$i \ ++ --remote=punix:s$i.ovsdb s$i.db]) ++done ++ ++dnl Make sure that all servers joined the cluster. ++for i in $(seq $n); do ++ AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) ++done ++ ++for i in $(seq $n); do ++ OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s$i], [s$i.pid]) ++done ++ ++AT_CLEANUP ++ ++AT_SETUP([OVSDB cluster - leadership change before replication while joining]) ++AT_KEYWORDS([ovsdb server negative unix cluster join]) ++ ++n=5 ++AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db dnl ++ $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr]) ++cid=$(ovsdb-tool db-cid s1.db) ++schema_name=$(ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema) ++for i in $(seq 2 $n); do ++ AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft]) ++done ++ ++on_exit 'kill $(cat *.pid)' ++on_exit " ++ for i in \$(ls $(pwd)/s[[0-$n]]); do ++ ovs-appctl --timeout 1 -t \$i cluster/status $schema_name; ++ done ++" ++ ++dnl Starting servers one by one asking all exisitng servers to transfer ++dnl leadership right after starting to add a server. Joining server will ++dnl need to find a new leader that will also transfer leadership. ++dnl This will continue until the same server will not become a leader ++dnl for the second time and will be able to add a new server. ++for i in $(seq $n); do ++ dnl Make sure that all already started servers joined the cluster. ++ for j in $(seq $((i - 1)) ); do ++ AT_CHECK([ovsdb_client_wait unix:s$j.ovsdb $schema_name connected]) ++ done ++ for j in $(seq $((i - 1)) ); do ++ OVS_WAIT_UNTIL([ovs-appctl -t "$(pwd)"/s$j \ ++ cluster/failure-test \ ++ transfer-leadership-after-starting-to-add \ ++ | grep -q "engaged"]) ++ done ++ ++ AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off \ ++ --detach --no-chdir --log-file=s$i.log \ ++ --pidfile=s$i.pid --unixctl=s$i \ ++ --remote=punix:s$i.ovsdb s$i.db]) ++done ++ ++dnl Make sure that all servers joined the cluster. ++for i in $(seq $n); do ++ AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected]) ++done ++ ++for i in $(seq $n); do ++ OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s$i], [s$i.pid]) ++done ++ ++AT_CLEANUP + + + OVS_START_SHELL_HELPERS diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at index b8ccc4c8e2..ce6d32aee1 100644 --- a/tests/ovsdb-server.at @@ -787,19 +2470,154 @@ index 6fbdedb64f..5dcdd2afae 100644 AT_SETUP([layer3 - ping over MPLS Bareudp]) OVS_CHECK_BAREUDP() OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) +diff --git a/tests/system-route.at b/tests/system-route.at +index 114aaebc77..c0ecad6cfb 100644 +--- a/tests/system-route.at ++++ b/tests/system-route.at +@@ -64,3 +64,67 @@ Cached: fc00:db8:beef::13/128 dev br0 GW fc00:db8:cafe::1 SRC fc00:db8:cafe::2]) + + OVS_TRAFFIC_VSWITCHD_STOP + AT_CLEANUP ++ ++dnl Checks that OVS doesn't use routes from non-standard tables. ++AT_SETUP([ovs-route - route tables]) ++AT_KEYWORDS([route]) ++OVS_TRAFFIC_VSWITCHD_START() ++ ++dnl Create tap port. ++on_exit 'ip link del p1-route' ++AT_CHECK([ip tuntap add name p1-route mode tap]) ++AT_CHECK([ip link set p1-route up]) ++ ++dnl Add ip address. ++AT_CHECK([ip addr add 10.0.0.17/24 dev p1-route], [0], [stdout]) ++ ++dnl Check that OVS catches route updates. ++OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl ++Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 ++Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local]) ++ ++dnl Add a route to the main routing table and check that OVS caches ++dnl this new route. ++AT_CHECK([ip route add 10.0.0.18/32 dev p1-route]) ++OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl ++Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 ++Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local ++Cached: 10.0.0.18/32 dev p1-route SRC 10.0.0.17]) ++ ++dnl Add a route to a custom routing table and check that OVS doesn't cache it. ++AT_CHECK([ip route add 10.0.0.19/32 dev p1-route table 42]) ++AT_CHECK([ip route show table 42 | grep 'p1-route' | grep -q '10.0.0.19']) ++dnl Give the main thread a chance to act. ++AT_CHECK([ovs-appctl revalidator/wait]) ++dnl Check that OVS didn't learn this route. ++AT_CHECK([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [0], [dnl ++Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 ++Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local ++Cached: 10.0.0.18/32 dev p1-route SRC 10.0.0.17 ++]) ++ ++dnl Delete a route from the main table and check that OVS removes the route ++dnl from the cache. ++AT_CHECK([ip route del 10.0.0.18/32 dev p1-route]) ++OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl ++Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 ++Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local]) ++ ++dnl Delete a route from a custom routing table and check that the cache ++dnl dosn't change. ++AT_CHECK([ip route del 10.0.0.19/32 dev p1-route table 42]) ++dnl Give the main thread a chance to act. ++AT_CHECK([ovs-appctl revalidator/wait]) ++dnl Check that the cache is still the same. ++AT_CHECK([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [0], [dnl ++Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17 ++Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local ++]) ++ ++dnl Delete ip address. ++AT_CHECK([ip addr del 10.0.0.17/24 dev p1-route], [0], [stdout]) ++dnl Check that routes were removed from OVS. ++OVS_WAIT_UNTIL([test $(ovs-appctl ovs/route/show | grep -c 'p1-route') -eq 0 ]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP ++AT_CLEANUP diff --git a/tests/system-traffic.at b/tests/system-traffic.at -index 98e494abf4..2d12d558ec 100644 +index 98e494abf4..0008bc1720 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at -@@ -6388,6 +6388,7 @@ OVS_TRAFFIC_VSWITCHD_STOP +@@ -3103,7 +3103,10 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl + icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=,type=0,code=0) + ]) + +-AT_CHECK([ovs-appctl dpctl/flush-conntrack]) ++AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl ++]) + + dnl Pings from ns1->ns0 should fail. + NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | FORMAT_PING], [0], [dnl +@@ -3244,6 +3247,11 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl + icmpv6,orig=(src=fc00::1,dst=fc00::2,id=,type=128,code=0),reply=(src=fc00::2,dst=fc00::1,id=,type=129,code=0) + ]) + ++AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_ipv6_src=fc00::1,ct_ipv6_dst=fc00::2']) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fc00::2)], [0], [dnl ++]) ++ + OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP - AT_SETUP([conntrack - SNAT with port range with exhaustion]) -+OVS_CHECK_GITHUB_ACTION() - CHECK_CONNTRACK() - CHECK_CONNTRACK_NAT() - OVS_TRAFFIC_VSWITCHD_START() -@@ -8389,6 +8390,53 @@ AT_CHECK([ovs-pcap client.pcap | grep 000000002010000000002000], [0], [dnl +@@ -6397,11 +6405,11 @@ ADD_NAMESPACES(at_ns0, at_ns1) + ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") + NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88]) + ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") ++NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address 80:89:89:89:89:89]) + + dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0. + AT_DATA([flows.txt], [dnl +-in_port=1,tcp,action=ct(commit,zone=1,nat(src=10.1.1.240:34568,random)),2 +-in_port=2,ct_state=-trk,tcp,tp_dst=34567,action=ct(table=0,zone=1,nat) ++in_port=1,tcp,action=ct(commit,zone=1,nat(src=10.1.1.240:34568)),2 + in_port=2,ct_state=-trk,tcp,tp_dst=34568,action=ct(table=0,zone=1,nat) + in_port=2,ct_state=+trk,ct_zone=1,tcp,action=1 + dnl +@@ -6425,17 +6433,28 @@ AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) + + dnl HTTP requests from p0->p1 should work fine. + OVS_START_L7([at_ns1], [http]) +-NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 1 -T 1 --retry-connrefused -v -o wget0.log]) ++ ++dnl Send a valid SYN to make conntrack pick it up. ++dnl The source port used is 123 to prevent unwanted reuse in the next HTTP request. ++syn_pkt=$(ovs-ofctl compose-packet --bare "eth_src=80:88:88:88:88:88,eth_dst=80:89:89:89:89:89,\ ++ dl_type=0x0800,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_proto=6,nw_ttl=64,nw_frag=no,tcp_flags=syn,\ ++ tcp_src=123,tcp_dst=80") ++AT_CHECK([ovs-ofctl packet-out br0 "packet=${syn_pkt} actions=ct(commit,zone=1,nat(src=10.1.1.240:34568))"]) ++ ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | uniq], [0], [dnl ++tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),zone=1,protoinfo=(state=) ++]) + + NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 1 -T 1 --retry-connrefused -v -o wget0.log], [4]) + +-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sed -e 's/dst=10.1.1.2[[45]][[0-9]]/dst=10.1.1.2XX/' | uniq], [0], [dnl +-tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.2XX,sport=,dport=),zone=1,protoinfo=(state=) ++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | uniq], [0], [dnl ++tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=,dport=),reply=(src=10.1.1.2,dst=10.1.1.240,sport=,dport=),zone=1,protoinfo=(state=) + ]) + + OVS_TRAFFIC_VSWITCHD_STOP(["dnl + /Unable to NAT due to tuple space exhaustion - if DoS attack, use firewalling and\/or zone partitioning./d +-/Dropped .* log messages in last .* seconds \(most recently, .* seconds ago\) due to excessive rate/d"]) ++/Dropped .* log messages in last .* seconds \(most recently, .* seconds ago\) due to excessive rate/d ++/|WARN|.* execute ct.* failed/d"]) + AT_CLEANUP + + AT_SETUP([conntrack - more complex SNAT]) +@@ -8389,6 +8408,53 @@ AT_CHECK([ovs-pcap client.pcap | grep 000000002010000000002000], [0], [dnl OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP @@ -854,7 +2672,7 @@ index 98e494abf4..2d12d558ec 100644 AT_SETUP([IGMP - flood under normal action]) diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at -index a8dd28c5b5..3f2cf84292 100644 +index a8dd28c5b5..f1c5d42f66 100644 --- a/tests/tunnel-push-pop-ipv6.at +++ b/tests/tunnel-push-pop-ipv6.at @@ -19,11 +19,12 @@ AT_CHECK([ovs-vsctl add-port int-br3 t3 -- set Interface t3 type=srv6 \ @@ -931,7 +2749,7 @@ index a8dd28c5b5..3f2cf84292 100644 ]) AT_CHECK([ovs-ofctl add-flow br0 action=normal]) -@@ -636,3 +642,87 @@ Listening ports: +@@ -636,3 +642,177 @@ Listening ports: OVS_VSWITCHD_STOP AT_CLEANUP @@ -1019,8 +2837,98 @@ index a8dd28c5b5..3f2cf84292 100644 + +OVS_VSWITCHD_STOP +AT_CLEANUP ++ ++dnl This is a regression test for outer header checksum offloading ++dnl with recirculation. ++AT_SETUP([tunnel_push_pop_ipv6 - recirculation after encapsulation]) ++ ++OVS_VSWITCHD_START( ++ [add-port br0 p0 \ ++ -- set Interface p0 type=dummy ofport_request=1 \ ++ other-config:hwaddr=aa:55:aa:55:00:00]) ++AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) ++AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) ++AT_CHECK([ovs-vsctl add-port int-br t2 \ ++ -- set Interface t2 type=geneve \ ++ options:remote_ip=2001:cafe::92 \ ++ options:key=123 ofport_request=2]) ++ ++dnl Setup an IP address. ++AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK ++]) ++dnl Checking that a local route for added IP was successfully installed. ++AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl ++Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 local ++]) ++ ++dnl Add a dp-hash selection group. ++AT_CHECK([ovs-ofctl add-group br0 \ ++ 'group_id=1234,type=select,selection_method=dp_hash,bucket=weight=1,output:p0']) ++AT_CHECK([ovs-ofctl add-flow br0 in_port=br0,action=group:1234]) ++AT_CHECK([ovs-ofctl add-flow br0 in_port=p0,action=normal]) ++ ++AT_CHECK([ovs-ofctl add-flow int-br action=normal]) ++ ++dnl This Neighbor Advertisement from p0 has two effects: ++dnl 1. The neighbor cache will learn that 2001:cafe::92 is at f8:bc:12:44:34:b6. ++dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. ++AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl ++ 'recirc_id(0),in_port(1),dnl ++ eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),dnl ++ ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),dnl ++ icmpv6(type=136,code=0),dnl ++ nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)' ++]) ++ ++dnl Check that selection group is used in the trace. ++AT_CHECK([ovs-appctl ofproto/trace int-br in_port=LOCAL \ ++ | grep -E 'tunnel|actions'], [0], [dnl ++ -> output to native tunnel ++ -> tunneling to 2001:cafe::92 via br0 ++ -> tunneling from aa:55:aa:55:00:00 2001:cafe::88 to f8:bc:12:44:34:b6 2001:cafe::92 ++Datapath actions: tnl_push(tnl_port(6081),header(size=70,type=5,dnl ++eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),dnl ++ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),dnl ++udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)),dnl ++hash(l4(0)),recirc(0x1) ++]) ++ ++dnl Now check that the packet is actually encapsulated and delivered. ++AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) ++ ++packet=50540000000a5054000000091234 ++eth=f8bc124434b6aa55aa55000086dd ++ip6=60000000001e11402001cafe0000000000000000000000882001cafe000000000000000000000092 ++dnl Source port is based on a packet hash, so it may differ depending on the ++dnl compiler flags and CPU type. Same for UDP checksum. Masked with '....'. ++udp=....17c1001e.... ++geneve=0000655800007b00 ++encap=${eth}${ip6}${udp}${geneve} ++dnl Output to tunnel from a int-br internal port. ++dnl Checking that the packet arrived and it was correctly encapsulated. ++AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) ++OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 1]) ++dnl Sending again to exercise the non-miss upcall path. ++AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) ++OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 2]) ++ ++dnl Finally, checking that the datapath flow is also correct. ++AT_CHECK([ovs-appctl dpctl/dump-flows | grep tnl_push \ ++ | strip_ufid | strip_used], [0], [dnl ++recirc_id(0),in_port(2),packet_type(ns=0,id=0),dnl ++eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234), dnl ++packets:1, bytes:14, used:0.0s, dnl ++actions:tnl_push(tnl_port(6081),header(size=70,type=5,dnl ++eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),dnl ++ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),dnl ++udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)),dnl ++hash(l4(0)),recirc(0x2) ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at -index b1440f5904..97405636f9 100644 +index b1440f5904..508737c53e 100644 --- a/tests/tunnel-push-pop.at +++ b/tests/tunnel-push-pop.at @@ -30,17 +30,15 @@ dummy@ovs-dummy: hit:0 missed:0 @@ -1255,7 +3163,7 @@ index b1440f5904..97405636f9 100644 ]) dnl Send an ARP reply to port b8 on br0, so that packets will be forwarded -@@ -993,3 +1085,81 @@ udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(100)),8),7 +@@ -993,3 +1085,170 @@ udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(100)),8),7 OVS_VSWITCHD_STOP AT_CLEANUP @@ -1337,8 +3245,97 @@ index b1440f5904..97405636f9 100644 + +OVS_VSWITCHD_STOP +AT_CLEANUP ++ ++dnl This is a regression test for outer header checksum offloading ++dnl with recirculation. ++AT_SETUP([tunnel_push_pop - recirculation after encapsulation]) ++ ++OVS_VSWITCHD_START( ++ [add-port br0 p0 \ ++ -- set Interface p0 type=dummy ofport_request=1 \ ++ other-config:hwaddr=aa:55:aa:55:00:00]) ++AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg]) ++AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy]) ++AT_CHECK([ovs-vsctl add-port int-br t2 \ ++ -- set Interface t2 type=geneve \ ++ options:remote_ip=1.1.2.92 \ ++ options:key=123 ofport_request=2]) ++ ++dnl Setup an IP address. ++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK ++]) ++dnl Checking that a local route for added IP was successfully installed. ++AT_CHECK([ovs-appctl ovs/route/show | grep Cached | sort], [0], [dnl ++Cached: 1.1.2.0/24 dev br0 SRC 1.1.2.88 local ++]) ++ ++dnl Add a dp-hash selection group. ++AT_CHECK([ovs-ofctl add-group br0 \ ++ 'group_id=1234,type=select,selection_method=dp_hash,bucket=weight=1,output:p0']) ++AT_CHECK([ovs-ofctl add-flow br0 in_port=br0,action=group:1234]) ++AT_CHECK([ovs-ofctl add-flow br0 in_port=p0,action=normal]) ++ ++AT_CHECK([ovs-ofctl add-flow int-br action=normal]) ++ ++dnl This ARP reply from p0 has two effects: ++dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6. ++dnl 2. The br0 mac learning will learn that f8:bc:12:44:34:b6 is on p0. ++AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl ++ 'recirc_id(0),in_port(1),dnl ++ eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl ++ arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)' ++]) ++ ++dnl Check that selection group is used in the trace. ++AT_CHECK([ovs-appctl ofproto/trace int-br in_port=LOCAL \ ++ | grep -E 'tunnel|actions'], [0], [dnl ++ -> output to native tunnel ++ -> tunneling to 1.1.2.92 via br0 ++ -> tunneling from aa:55:aa:55:00:00 1.1.2.88 to f8:bc:12:44:34:b6 1.1.2.92 ++Datapath actions: tnl_push(tnl_port(6081),header(size=50,type=5,dnl ++eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ++ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),dnl ++udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)),dnl ++hash(l4(0)),recirc(0x1) ++]) ++ ++dnl Now check that the packet is actually encapsulated and delivered. ++AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap]) ++ ++packet=50540000000a5054000000091234 ++eth=f8bc124434b6aa55aa5500000800 ++ip4=450000320000400040113406010102580101025c ++dnl Source port is based on a packet hash, so it may differ depending on the ++dnl compiler flags and CPU type. Masked with '....'. ++udp=....17c1001e0000 ++geneve=0000655800007b00 ++encap=${eth}${ip4}${udp}${geneve} ++dnl Output to tunnel from a int-br internal port. ++dnl Checking that the packet arrived and it was correctly encapsulated. ++AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) ++OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 1]) ++ ++dnl Sending again to exercise the non-miss upcall path. ++AT_CHECK([ovs-appctl netdev-dummy/receive int-br "${packet}"]) ++OVS_WAIT_UNTIL([test $(ovs-pcap p0.pcap | grep -c "${encap}${packet}") -eq 2]) ++ ++dnl Finally, checking that the datapath flow is also correct. ++AT_CHECK([ovs-appctl dpctl/dump-flows | grep tnl_push \ ++ | strip_ufid | strip_used], [0], [dnl ++recirc_id(0),in_port(2),packet_type(ns=0,id=0),dnl ++eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234), dnl ++packets:1, bytes:14, used:0.0s, dnl ++actions:tnl_push(tnl_port(6081),header(size=50,type=5,dnl ++eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),dnl ++ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),dnl ++udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)),dnl ++hash(l4(0)),recirc(0x2) ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP diff --git a/tests/tunnel.at b/tests/tunnel.at -index 282651ac73..71e7c2df4e 100644 +index 282651ac73..9d539ee6f6 100644 --- a/tests/tunnel.at +++ b/tests/tunnel.at @@ -524,11 +524,12 @@ dummy@ovs-dummy: hit:0 missed:0 @@ -1357,7 +3354,26 @@ index 282651ac73..71e7c2df4e 100644 ]) dnl change the flow table to bump the internal table version -@@ -1276,15 +1277,12 @@ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \ +@@ -1268,6 +1269,18 @@ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) + OVS_APP_EXIT_AND_WAIT([ovsdb-server])] + AT_CLEANUP + ++AT_SETUP([tunnel - re-create port with different name]) ++OVS_VSWITCHD_START( ++ [add-port br0 p0 -- set int p0 type=vxlan options:remote_ip=10.10.10.1]) ++ ++AT_CHECK([ovs-vsctl --if-exists del-port p0 -- \ ++ add-port br0 p1 -- \ ++ set int p1 type=vxlan options:remote_ip=10.10.10.1]) ++ ++OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) ++OVS_APP_EXIT_AND_WAIT([ovsdb-server])] ++AT_CLEANUP ++ + AT_SETUP([tunnel - SRV6 basic]) + OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \ + ofport_request=1 \ +@@ -1276,15 +1289,12 @@ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \ ofport_request=2]) OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP @@ -1377,6 +3393,18 @@ index 282651ac73..71e7c2df4e 100644 ]) AT_DATA([flows.txt], [dnl +diff --git a/tests/vlog.at b/tests/vlog.at +index 785014956e..efe91479a6 100644 +--- a/tests/vlog.at ++++ b/tests/vlog.at +@@ -8,6 +8,7 @@ AT_CHECK([$PYTHON3 $srcdir/test-vlog.py --log-file log_file \ + + AT_CHECK([sed -e 's/.*-.*-.*T..:..:..Z |//' \ + -e 's/File ".*", line [[0-9]][[0-9]]*,/File , line ,/' \ ++-e '/\^\+/d' \ + stderr_log], [0], [dnl + 0 | module_0 | EMER | emergency + 1 | module_0 | ERR | error diff --git a/utilities/ovs-pki.in b/utilities/ovs-pki.in index e0ba910f94..285018e41e 100755 --- a/utilities/ovs-pki.in @@ -1487,3 +3515,48 @@ index e0ba910f94..285018e41e 100755 openssl req -config "$TMP/req.cnf" -new -text \ -key "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3 } +diff --git a/utilities/ovs-tcpdump.in b/utilities/ovs-tcpdump.in +index 4cbd9a5d31..eada803bb4 100755 +--- a/utilities/ovs-tcpdump.in ++++ b/utilities/ovs-tcpdump.in +@@ -534,29 +534,19 @@ def main(): + ovsdb.close_idl() + + pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs)) +- try: +- while pipes.poll() is None: +- data = pipes.stdout.readline().strip(b'\n') +- if len(data) == 0: +- raise KeyboardInterrupt +- print(data.decode('utf-8')) +- raise KeyboardInterrupt +- except KeyboardInterrupt: +- # If there is a pipe behind ovs-tcpdump (such as ovs-tcpdump +- # -i eth0 | grep "192.168.1.1"), the pipe is no longer available +- # after received Ctrl+C. +- # If we write data to an unavailable pipe, a pipe error will be +- # reported, so we turn off stdout to avoid subsequent flushing +- # of data into the pipe. +- try: +- sys.stdout.close() +- except IOError: +- pass ++ while pipes.poll() is None: ++ data = pipes.stdout.readline().strip(b'\n') ++ if len(data) == 0: ++ break ++ print(data.decode('utf-8')) + +- if pipes.poll() is None: +- pipes.terminate() ++ try: ++ sys.stdout.close() ++ except IOError: ++ pass + +- sys.exit(0) ++ if pipes.poll() is None: ++ pipes.terminate() + + + if __name__ == '__main__': diff --git a/SPECS/openvswitch3.3.spec b/SPECS/openvswitch3.3.spec index 970068f..29940f2 100644 --- a/SPECS/openvswitch3.3.spec +++ b/SPECS/openvswitch3.3.spec @@ -57,7 +57,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 3.3.0 -Release: 2%{?dist} +Release: 14%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -767,6 +767,96 @@ exit 0 %endif %changelog +* Mon Apr 29 2024 Open vSwitch CI - 3.3.0-14 +- Merging upstream branch-3.3 [RH git: fd340e5b9e] + Commit list: + a6c3b5202c netdev-dpdk: Fix possible memory leak configuring VF MAC address. + + +* Tue Apr 23 2024 Open vSwitch CI - 3.3.0-13 +- Merging upstream branch-3.3 [RH git: 2914158441] + Commit list: + 42e685916e ovsdb: raft: Fix probe intervals after install snapshot request. + 1c44cb5963 ovsdb: raft: Fix inability to join a cluster with a large database. + 5966c22b85 rhel/systemd: Set ovsdb-server timeout to 5 minutes. + + +* Thu Apr 11 2024 Open vSwitch CI - 3.3.0-12 +- Merging upstream branch-3.3 [RH git: 9b5e829705] + Commit list: + f19448b861 github: Update python to 3.12. + b705fb8dd2 ovsdb-dot: Fix flake8 issues. + 1ac823cb03 ovsdb-doc: Fix syntax warning with Python 3.12 and flake8 issues. + + +* Wed Apr 10 2024 Open vSwitch CI - 3.3.0-11 +- Merging upstream branch-3.3 [RH git: 7956e878a6] + Commit list: + 7e99dbd8a0 python: Remove hacking dependency and use recent flake8. + 41055da769 cirrus: Update to FreeBSD 13.3. + 6448c1b697 vlog: Log stack trace on vlog_abort. + + +* Tue Apr 09 2024 Open vSwitch CI - 3.3.0-10 +- Merging upstream branch-3.3 [RH git: 41fe6a6437] + Commit list: + a6852319b4 tests: Fix compatibility issue with Python 3.13 in vlog.at. + + +* Fri Apr 05 2024 Open vSwitch CI - 3.3.0-9 +- Merging upstream branch-3.3 [RH git: 3b2958ef7b] + Commit list: + 775507fe91 ofproto-dpif-upcall: Fix ukey installation failure logs and counters. + + +* Wed Apr 03 2024 Open vSwitch CI - 3.3.0-8 +- Merging upstream branch-3.3 [RH git: 69817f7024] + Commit list: + b26baf873e conntrack: Do not use icmp reverse helper for icmpv6. + b9f28c5862 conntrack: Fix SNAT with exhaustion system test. + + +* Wed Mar 27 2024 Open vSwitch CI - 3.3.0-7 +- Merging upstream branch-3.3 [RH git: 1a2a88672e] + Commit list: + 43db937876 ovsdb: raft: Fix inability to join after leadership change round trip. + 2a2f162b57 ovsdb: raft: Fix assertion when 1-node cluster looses leadership. + a174a5ab8b ovsdb: raft: Fix permanent joining state on a cluster member. + 5eac230f22 ovsdb: raft: Fix time intervals for multitasking while joining. + 99fe661f04 ovsdb: raft: Avoid transferring leadership to unavailable servers. + + +* Wed Mar 27 2024 Open vSwitch CI - 3.3.0-6 +- Merging upstream branch-3.3 [RH git: 918ba37710] + Commit list: + c560f6ca32 ofproto-dpif-xlate: Fix continuations with associated metering. + + +* Fri Mar 22 2024 Open vSwitch CI - 3.3.0-5 +- Merging upstream branch-3.3 [RH git: 53f3f1b833] + Commit list: + 1c1f173ce8 dpif-netdev: Fix crash due to tunnel offloading on recirculation. + feb0fefd8d netdev-dpdk: Disable outer UDP checksum offload for ice/i40e driver. + 3280d95c42 ovs-monitor-ipsec: LibreSwan autodetect paths. (#1975039) + 4fedcae6ee route-table: Avoid routes from non-standard routing tables. + 9f39cd4a11 ovs-tcpdump: Fix cleanup mirror failed with twice fatal signals. + + +* Tue Mar 19 2024 Open vSwitch CI - 3.3.0-4 +- Merging upstream branch-3.3 [RH git: e9ada703c5] + Commit list: + 7fa40643e7 ofproto-dpif: Fix tunnel with different name del/add failure. + 04dc9d117a ofpbuf: Prevent undefined behavior in ofpbuf_clone. + + +* Sat Mar 16 2024 Open vSwitch CI - 3.3.0-3 +- Merging upstream branch-3.3 [RH git: 742462843f] + Commit list: + 667c9eb2b0 netdev-dpdk: Fix tunnel type check during Tx offload preparation. + 332300c360 netdev-dpdk: Fix TCP check during Tx offload preparation. + e7778b3716 netdev-dpdk: Clear inner packet marks if no inner offloads requested. + + * Wed Mar 13 2024 Open vSwitch CI - 3.3.0-2 - Merging upstream branch-3.3 [RH git: ed97fb253e] Commit list: