From dbf927c50d74202090862133dc60334f2b7b8978 Mon Sep 17 00:00:00 2001 From: Open vSwitch CI Date: Nov 11 2024 21:10:16 +0000 Subject: Import openvswitch3.3-3.3.0-12 from Fast DataPath --- diff --git a/SOURCES/openvswitch-3.3.0.patch b/SOURCES/openvswitch-3.3.0.patch index ba72404..c479d55 100644 --- a/SOURCES/openvswitch-3.3.0.patch +++ b/SOURCES/openvswitch-3.3.0.patch @@ -63,10 +63,10 @@ index d8a9722809..d73154a971 100644 memory: 4G diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml -index fc75581486..fba2d16031 100644 +index fc75581486..abda9549bf 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml -@@ -2,13 +2,16 @@ name: Build and Test +@@ -2,17 +2,23 @@ name: Build and Test on: [push, pull_request] @@ -75,9 +75,12 @@ index fc75581486..fba2d16031 100644 + jobs: build-dpdk: ++ strategy: ++ matrix: ++ runner: [ubuntu-24.04] env: - dependencies: gcc libbpf-dev libnuma-dev libpcap-dev ninja-build pkgconf -+ dependencies: gcc libbpf-dev libnuma-dev ninja-build pkgconf ++ dependencies: gcc libnuma-dev libxdp-dev ninja-build pkgconf CC: gcc - DPDK_GIT: https://dpdk.org/git/dpdk - DPDK_VER: 23.11 @@ -86,7 +89,22 @@ index fc75581486..fba2d16031 100644 name: dpdk gcc outputs: dpdk_key: ${{ steps.gen_dpdk_key.outputs.key }} -@@ -54,7 +57,7 @@ jobs: +- runs-on: ubuntu-22.04 ++ runs-on: ${{ matrix.runner }} + timeout-minutes: 30 + + steps: +@@ -30,7 +36,8 @@ jobs: + # This also allows us to use cache from any branch as long as version + # and a way we're building DPDK stays the same. + run: | +- cat .ci/dpdk-* > dpdk-ci-signature ++ echo ${{ matrix.runner }} > dpdk-ci-signature ++ cat .ci/dpdk-* >> dpdk-ci-signature + grep -rwE 'DPDK_GIT|DPDK_VER' .github/ >> dpdk-ci-signature + if [ "${DPDK_VER##refs/*/}" != "${DPDK_VER}" ]; then + git ls-remote --heads $DPDK_GIT $DPDK_VER >> dpdk-ci-signature +@@ -54,7 +61,7 @@ jobs: if: steps.dpdk_cache.outputs.cache-hit != 'true' uses: actions/setup-python@v5 with: @@ -95,17 +113,26 @@ index fc75581486..fba2d16031 100644 - name: update APT cache if: steps.dpdk_cache.outputs.cache-hit != 'true' -@@ -76,8 +79,7 @@ jobs: +@@ -76,8 +83,7 @@ jobs: env: dependencies: | automake libtool gcc bc libjemalloc2 libjemalloc-dev libssl-dev \ - llvm-dev libnuma-dev libpcap-dev selinux-policy-dev libbpf-dev \ - lftp libreswan -+ llvm-dev libnuma-dev selinux-policy-dev libbpf-dev lftp libreswan ++ llvm-dev libnuma-dev selinux-policy-dev libxdp-dev lftp libreswan CC: ${{ matrix.compiler }} DPDK: ${{ matrix.dpdk }} DPDK_SHARED: ${{ matrix.dpdk_shared }} -@@ -217,7 +219,7 @@ jobs: +@@ -90,7 +96,7 @@ jobs: + TEST_RANGE: ${{ matrix.test_range }} + + name: linux ${{ join(matrix.*, ' ') }} +- runs-on: ubuntu-22.04 ++ runs-on: ubuntu-24.04 + timeout-minutes: 30 + + strategy: +@@ -217,7 +223,7 @@ jobs: - name: set up python uses: actions/setup-python@v5 with: @@ -114,18 +141,24 @@ index fc75581486..fba2d16031 100644 - name: cache if: matrix.dpdk != '' || matrix.dpdk_shared != '' -@@ -268,8 +270,8 @@ jobs: +@@ -268,13 +274,13 @@ jobs: needs: build-dpdk env: dependencies: | - automake bc clang-tools libbpf-dev libnuma-dev libpcap-dev \ - libunbound-dev libunwind-dev libssl-dev libtool llvm-dev -+ automake bc clang-tools libbpf-dev libnuma-dev libunbound-dev \ -+ libunwind-dev libssl-dev libtool llvm-dev ++ automake bc clang-tools libnuma-dev libunbound-dev libunwind-dev \ ++ libssl-dev libtool libxdp-dev llvm-dev CC: clang DPDK: dpdk CLANG_ANALYZE: true -@@ -346,7 +348,7 @@ jobs: + name: clang-analyze +- runs-on: ubuntu-22.04 ++ runs-on: ubuntu-24.04 + timeout-minutes: 30 + + steps: +@@ -346,7 +352,7 @@ jobs: - name: set up python uses: actions/setup-python@v5 with: @@ -134,7 +167,7 @@ index fc75581486..fba2d16031 100644 - name: get cached dpdk-dir uses: actions/cache/restore@v4 -@@ -399,7 +401,7 @@ jobs: +@@ -399,7 +405,7 @@ jobs: - name: set up python uses: actions/setup-python@v5 with: @@ -143,6 +176,15 @@ index fc75581486..fba2d16031 100644 - name: install dependencies run: brew install automake libtool - name: prepare +@@ -421,7 +427,7 @@ jobs: + DPDK: ${{ matrix.dpdk }} + + name: linux deb ${{ matrix.dpdk }} dpdk +- runs-on: ubuntu-22.04 ++ runs-on: ubuntu-24.04 + timeout-minutes: 30 + + strategy: diff --git a/AUTHORS.rst b/AUTHORS.rst index aa9284fb16..fe4064ca71 100644 --- a/AUTHORS.rst @@ -725,23 +767,185 @@ index 0000000000..9a23d7f746 + +#include_next diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in -index 7945162f9f..37c509ac68 100755 +index 7945162f9f..6c60c07e3f 100755 --- a/ipsec/ovs-monitor-ipsec.in +++ b/ipsec/ovs-monitor-ipsec.in -@@ -457,14 +457,36 @@ conn prevent_unencrypted_vxlan +@@ -20,6 +20,7 @@ import os + import re + import subprocess + import sys ++import time + from string import Template + + import ovs.daemon +@@ -82,6 +83,41 @@ vlog = ovs.vlog.Vlog("ovs-monitor-ipsec") + exiting = False + monitor = None + xfrm = None ++command_timeout = None ++RECONCILIATION_INTERVAL = 15 # seconds ++TIMEOUT_EXPIRED = 137 # Exit code for a SIGKILL (128 + 9). ++ ++ ++def run_command(args, description=None): ++ """ This function runs the process args[0] with args[1:] arguments ++ and returns a tuple: return-code, stdout, stderr. """ ++ ++ if not description: ++ description = "run %s command" % args[0] ++ ++ vlog.dbg("Running %s" % args) ++ proc = subprocess.Popen(args, stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE) ++ try: ++ pout, perr = proc.communicate(timeout=command_timeout) ++ ret = proc.returncode ++ except subprocess.TimeoutExpired: ++ vlog.warn("Timed out after %s seconds trying to %s." ++ % (command_timeout, description)) ++ pout, perr = b'', b'' ++ # Just kill the process here. We can't afford waiting for it, ++ # as it may be stuck and may not actually be terminated. ++ proc.kill() ++ ret = TIMEOUT_EXPIRED ++ ++ if proc.returncode or perr: ++ vlog.warn("Failed to %s; exit code: %d" ++ % (description, proc.returncode)) ++ vlog.warn("cmdline: %s" % proc.args) ++ vlog.warn("stderr: %s" % perr) ++ vlog.warn("stdout: %s" % pout) ++ ++ return ret, pout.decode(), perr.decode() + + + class XFRM(object): +@@ -99,13 +135,14 @@ class XFRM(object): + where is destination IPv4 address and is SELECTOR of + the IPsec policy.""" + policies = {} +- proc = subprocess.Popen([self.IP, 'xfrm', 'policy'], +- stdout=subprocess.PIPE) +- while True: +- line = proc.stdout.readline().strip().decode() +- if line == '': +- break +- a = line.split(" ") ++ ++ ret, pout, perr = run_command([self.IP, 'xfrm', 'policy'], ++ "get XFRM policies") ++ if ret: ++ return policies ++ ++ for line in pout.splitlines(): ++ a = line.strip().split(" ") + if len(a) >= 4 and a[0] == "src" and a[2] == "dst": + dst = (a[3].split("/"))[0] + if dst not in policies: +@@ -122,13 +159,14 @@ class XFRM(object): + in a dictionary where is destination IPv4 address and + is SELECTOR.""" + securities = {} +- proc = subprocess.Popen([self.IP, 'xfrm', 'state'], +- stdout=subprocess.PIPE) +- while True: +- line = proc.stdout.readline().strip().decode() +- if line == '': +- break +- a = line.split(" ") ++ ++ ret, pout, perr = run_command([self.IP, 'xfrm', 'state'], ++ "get XFRM state") ++ if ret: ++ return securities ++ ++ for line in pout.splitlines(): ++ a = line.strip().split(" ") + if len(a) >= 4 and a[0] == "sel" \ + and a[1] == "src" and a[3] == "dst": + remote_ip = a[4].rstrip().split("/")[0] +@@ -242,7 +280,7 @@ conn prevent_unencrypted_vxlan + f.close() + + vlog.info("Restarting StrongSwan") +- subprocess.call([self.IPSEC, "restart"]) ++ run_command([self.IPSEC, "restart"], "restart StrongSwan") + + def get_active_conns(self): + """This function parses output from 'ipsec status' command. +@@ -252,13 +290,13 @@ conn prevent_unencrypted_vxlan + sample line from the parsed outpus as . """ + + conns = {} +- proc = subprocess.Popen([self.IPSEC, 'status'], stdout=subprocess.PIPE) ++ ret, pout, perr = run_command([self.IPSEC, 'status'], ++ "get active connections") ++ if ret: ++ return conns + +- while True: +- line = proc.stdout.readline().strip().decode() +- if line == '': +- break +- tunnel_name = line.split(":") ++ for line in pout.splitlines(): ++ tunnel_name = line.strip().split(":") + if len(tunnel_name) < 2: + continue + m = re.match(r"(.*)(-in-\d+|-out-\d+|-\d+).*", tunnel_name[0]) +@@ -271,6 +309,9 @@ conn prevent_unencrypted_vxlan + + return conns + ++ def need_to_reconcile(self, monitor): ++ return False ++ + def config_init(self): + self.conf_file = open(self.IPSEC_CONF, "w") + self.secrets_file = open(self.IPSEC_SECRETS, "w") +@@ -341,15 +382,11 @@ conn prevent_unencrypted_vxlan + Once strongSwan vici bindings will be distributed with major + Linux distributions this function could be simplified.""" + vlog.info("Refreshing StrongSwan configuration") +- proc = subprocess.Popen([self.IPSEC, "update"], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- outs, errs = proc.communicate() +- if proc.returncode != 0: +- vlog.err("StrongSwan failed to update configuration:\n" +- "%s \n %s" % (str(outs), str(errs))) +- +- subprocess.call([self.IPSEC, "rereadsecrets"]) ++ ++ run_command([self.IPSEC, "update"], ++ "update StrongSwan's configuration") ++ run_command([self.IPSEC, "rereadsecrets"], "re-read secrets") ++ + # "ipsec update" command does not remove those tunnels that were + # updated or that disappeared from the ipsec.conf file. So, we have + # to manually remove them by calling "ipsec stroke down-nb " +@@ -382,7 +419,8 @@ conn prevent_unencrypted_vxlan + + if not tunnel or tunnel.version != ver: + vlog.info("%s is outdated %u" % (conn, ver)) +- subprocess.call([self.IPSEC, "stroke", "down-nb", conn]) ++ run_command([self.IPSEC, "stroke", "down-nb", conn], ++ "stroke the outdated %s" % conn) + + + class LibreSwanHelper(object): +@@ -457,19 +495,41 @@ 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" + self.IPSEC_AUTO = [self.IPSEC] -+ proc = subprocess.Popen([self.IPSEC, "--version"], -+ stdout=subprocess.PIPE, -+ encoding="latin1") -+ pout, perr = proc.communicate() + -+ v = re.match("^Libreswan v?(.*)$", pout) ++ ret, pout, perr = run_command([self.IPSEC, "--version"], ++ "get Libreswan's version") + try: ++ v = re.match("^Libreswan v?(.*)$", pout.strip()) + version = int(v.group(1).split(".")[0]) + except: + version = 0 @@ -767,52 +971,165 @@ index 7945162f9f..37c509ac68 100755 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 -@@ -577,7 +599,7 @@ conn prevent_unencrypted_vxlan + self.IPSEC_CTL = libreswan_root_prefix + ipsec_ctl + self.conf_file = None ++ self.conns_not_active = set() ++ self.last_refresh = time.time() + self.secrets_file = None + vlog.dbg("Using: " + self.IPSEC) + vlog.dbg("Configuration file: " + self.IPSEC_CONF) +@@ -491,7 +551,7 @@ conn prevent_unencrypted_vxlan + f.close() + + vlog.info("Restarting LibreSwan") +- subprocess.call([self.IPSEC, "restart"]) ++ run_command([self.IPSEC, "restart"], "restart Libreswan") + + def config_init(self): + self.conf_file = open(self.IPSEC_CONF, "w") +@@ -577,116 +637,139 @@ conn prevent_unencrypted_vxlan def refresh(self, monitor): vlog.info("Refreshing LibreSwan configuration") - subprocess.call([self.IPSEC, "auto", "--ctlsocket", self.IPSEC_CTL, -+ subprocess.call(self.IPSEC_AUTO + ["--ctlsocket", self.IPSEC_CTL, - "--config", self.IPSEC_CONF, "--rereadsecrets"]) - tunnels = set(monitor.tunnels.keys()) - -@@ -605,7 +627,7 @@ conn prevent_unencrypted_vxlan - - if not tunnel or tunnel.version != ver: - vlog.info("%s is outdated %u" % (conn, ver)) +- "--config", self.IPSEC_CONF, "--rereadsecrets"]) +- tunnels = set(monitor.tunnels.keys()) +- +- # Delete old connections +- conns_dict = self.get_active_conns() +- for ifname, conns in conns_dict.items(): +- tunnel = monitor.tunnels.get(ifname) +- +- for conn in conns: +- # IPsec "connection" names must start with Interface name +- if not conn.startswith(ifname): +- vlog.err("%s does not start with %s" % (conn, ifname)) +- continue +- +- # version number should be the first integer after +- # interface name in IPsec "connection" +- try: +- ver = int(re.findall(r'\d+', conn[len(ifname):])[0]) +- except ValueError: +- vlog.err("%s does not contain version number") +- continue +- except IndexError: +- vlog.err("%s does not contain version number") +- continue +- +- if not tunnel or tunnel.version != ver: +- vlog.info("%s is outdated %u" % (conn, ver)) - subprocess.call([self.IPSEC, "auto", "--ctlsocket", -+ subprocess.call(self.IPSEC_AUTO + ["--ctlsocket", - self.IPSEC_CTL, "--config", - self.IPSEC_CONF, "--delete", conn]) - elif ifname in tunnels: -@@ -627,44 +649,44 @@ conn prevent_unencrypted_vxlan +- self.IPSEC_CTL, "--config", +- self.IPSEC_CONF, "--delete", conn]) +- elif ifname in tunnels: +- tunnels.remove(ifname) +- +- # Activate new connections +- for name in tunnels: +- ver = monitor.tunnels[name].version +- +- if monitor.tunnels[name].conf["tunnel_type"] == "gre": +- conn = "%s-%s" % (name, ver) +- self._start_ipsec_connection(conn) ++ run_command(self.IPSEC_AUTO + ["--ctlsocket", self.IPSEC_CTL, ++ "--config", self.IPSEC_CONF, ++ "--rereadsecrets"], ++ "re-read secrets") ++ ++ loaded_conns = self.get_loaded_conns() ++ active_conns = self.get_active_conns() ++ ++ all_names = set(monitor.tunnels.keys()) | \ ++ set(loaded_conns.keys()) | \ ++ set(active_conns.keys()) ++ ++ for name in all_names: ++ desired = set(self.get_conn_names(monitor, name)) ++ loaded = set(loaded_conns.get(name, dict()).keys()) ++ active = set(active_conns.get(name, dict()).keys()) ++ ++ # Untrack connections that became active. ++ self.conns_not_active.difference_update(active) ++ # Remove connections that didn't become active after --start ++ # and another explicit --up. ++ for conn in self.conns_not_active & loaded: ++ self._delete_ipsec_connection(conn, "is defunct") ++ loaded.remove(conn) ++ ++ # Remove all the loaded or active but not desired connections. ++ for conn in loaded | active: ++ if conn not in desired: ++ self._delete_ipsec_connection(conn, "is outdated") ++ loaded.discard(conn) ++ active.discard(conn) ++ ++ # If not all desired are loaded, remove all the loaded and ++ # active for this tunnel and re-load only the desired ones. ++ # Need to do that, because connections for the same tunnel ++ # may share SAs. If one is loaded and the other is not, ++ # it means the second one failed, so the shared SA may be in ++ # a broken state. ++ if desired != loaded: ++ for conn in loaded | active: ++ self._delete_ipsec_connection(conn, "is half-loaded") ++ loaded.discard(conn) ++ active.discard(conn) ++ ++ for conn in desired: ++ # Start (add + up) outgoing connections and only add ++ # incoming ones. If the other side will not initiate ++ # the connection and it will not become active, we'll ++ # bring it up during the next refresh. ++ if re.match(r".*-in-\d+$", conn): ++ vlog.info("Adding ipsec connection %s" % conn) ++ self._start_ipsec_connection(conn, "add") ++ else: ++ vlog.info("Starting ipsec connection %s" % conn) ++ self._start_ipsec_connection(conn, "start") + else: +- conn_in = "%s-in-%s" % (name, ver) +- conn_out = "%s-out-%s" % (name, ver) +- self._start_ipsec_connection(conn_in) +- self._start_ipsec_connection(conn_out) ++ # Ask pluto to bring UP connections that are loaded, ++ # but not active for some reason. ++ # ++ # desired == loaded and desired >= loaded + active, ++ # so loaded >= active ++ for conn in loaded - active: ++ vlog.info("Bringing up ipsec connection %s" % conn) ++ # On failure to --up it will be removed from the set. ++ self.conns_not_active.add(conn) ++ self._start_ipsec_connection(conn, "up") + # Update shunt policy if changed if monitor.conf_in_use["skb_mark"] != monitor.conf["skb_mark"]: if monitor.conf["skb_mark"]: - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", "--asynchronous", "prevent_unencrypted_gre"]) - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", "--asynchronous", "prevent_unencrypted_geneve"]) - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", "--asynchronous", "prevent_unencrypted_stt"]) - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--add", @@ -820,43 +1137,363 @@ index 7945162f9f..37c509ac68 100755 else: - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_gre"]) - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_geneve"]) - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_stt"]) - subprocess.call([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ subprocess.call(self.IPSEC_AUTO + ++ run_command(self.IPSEC_AUTO + + ["--config", self.IPSEC_CONF, "--ctlsocket", self.IPSEC_CTL, "--delete", "--asynchronous", "prevent_unencrypted_vxlan"]) -@@ -710,8 +732,8 @@ conn prevent_unencrypted_vxlan - # the "ipsec auto --start" command is lost. Just retry to make sure - # the command is received by LibreSwan. - while True: + monitor.conf_in_use["skb_mark"] = monitor.conf["skb_mark"] ++ self.last_refresh = time.time() ++ vlog.info("Refreshing is done.") + +- def get_active_conns(self): ++ def get_conns_from_status(self, pattern): + """This function parses output from 'ipsec status' command. + It returns dictionary where is interface name (as in OVSDB) + and is another dictionary. This another dictionary + uses LibreSwan connection name as and more detailed +- sample line from the parsed outpus as . """ ++ sample line from the parsed outpus as . 'pattern' should ++ be a regular expression that parses out the connection name. ++ Only the lines that match the pattern will be parsed. """ + + conns = {} +- proc = subprocess.Popen([self.IPSEC, 'status', '--ctlsocket', +- self.IPSEC_CTL], stdout=subprocess.PIPE) +- +- while True: +- line = proc.stdout.readline().strip().decode() +- if line == '': +- break +- +- m = re.search(r"#\d+: \"(.*)\".*", line) ++ ret, pout, perr = run_command([self.IPSEC, 'status', ++ '--ctlsocket', self.IPSEC_CTL], ++ "get ipsec status") ++ if ret: ++ return conns ++ ++ for line in pout.splitlines(): ++ m = re.search(pattern, line) + if not m: + continue + +@@ -705,122 +788,130 @@ conn prevent_unencrypted_vxlan + + return conns + +- def _start_ipsec_connection(self, conn): +- # In a corner case, LibreSwan daemon restarts for some reason and +- # the "ipsec auto --start" command is lost. Just retry to make sure +- # the command is received by LibreSwan. +- while True: - proc = subprocess.Popen([self.IPSEC, "auto", - "--config", self.IPSEC_CONF, -+ proc = subprocess.Popen(self.IPSEC_AUTO + -+ ["--config", self.IPSEC_CONF, - "--ctlsocket", self.IPSEC_CTL, - "--start", - "--asynchronous", conn], +- "--ctlsocket", self.IPSEC_CTL, +- "--start", +- "--asynchronous", conn], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- perr = str(proc.stderr.read()) +- pout = str(proc.stdout.read()) +- if not re.match(r".*Connection refused.*", perr) and \ +- not re.match(r".*need --listen.*", pout): +- break ++ def get_active_conns(self): ++ return self.get_conns_from_status(r"#\d+: .*\"(.*)\".*") ++ ++ def get_loaded_conns(self): ++ return self.get_conns_from_status(r"\"(.*)\": \d+.*(===|\.\.\.).*") ++ ++ def get_conn_names(self, monitor, ifname): ++ conns = [] ++ if ifname not in monitor.tunnels: ++ return conns ++ ++ tunnel = monitor.tunnels.get(ifname) ++ ver = tunnel.version ++ ++ if tunnel.conf["tunnel_type"] == "gre": ++ conns.append("%s-%s" % (ifname, ver)) ++ else: ++ conns.append("%s-in-%s" % (ifname, ver)) ++ conns.append("%s-out-%s" % (ifname, ver)) ++ ++ return conns ++ ++ def need_to_reconcile(self, monitor): ++ if time.time() - self.last_refresh < RECONCILIATION_INTERVAL: ++ return False ++ ++ conns_dict = self.get_active_conns() ++ for ifname, tunnel in monitor.tunnels.items(): ++ if ifname not in conns_dict: ++ vlog.info("Connection for port %s is not active, " ++ "need to reconcile" % ifname) ++ return True ++ ++ existing_conns = conns_dict.get(ifname) ++ desired_conns = self.get_conn_names(monitor, ifname) ++ ++ if set(existing_conns.keys()) != set(desired_conns): ++ vlog.info("Active connections for port %s %s do not match " ++ "desired %s, need to reconcile" ++ % (ifname, list(existing_conns.keys()), ++ desired_conns)) ++ return True ++ ++ return False ++ ++ def _delete_ipsec_connection(self, conn, reason): ++ vlog.info("%s %s, removing" % (conn, reason)) ++ self.conns_not_active.discard(conn) ++ run_command(self.IPSEC_AUTO + ++ ["--ctlsocket", self.IPSEC_CTL, ++ "--config", self.IPSEC_CONF, ++ "--delete", conn], "delete %s" % conn) ++ ++ def _start_ipsec_connection(self, conn, action): ++ asynchronous = [] if action == "add" else ["--asynchronous"] ++ ret, pout, perr = run_command(self.IPSEC_AUTO + ++ ["--config", self.IPSEC_CONF, ++ "--ctlsocket", self.IPSEC_CTL, ++ "--" + action, ++ *asynchronous, conn], ++ "%s %s" % (action, conn)) + + if re.match(r".*[F|f]ailed to initiate connection.*", pout): + vlog.err('Failed to initiate connection through' + ' Interface %s.\n' % (conn.split('-')[0])) +- vlog.err(pout) ++ vlog.err("stdout: %s" % pout) ++ ret = 1 ++ ++ if ret: ++ # We don't know in which state the connection was left on ++ # failure. Try to clean it up. ++ self._delete_ipsec_connection(conn, "--%s failed" % action) + + def _nss_clear_database(self): + """Remove all OVS IPsec related state from the NSS database""" +- try: +- proc = subprocess.Popen(['certutil', '-L', '-d', +- self.IPSEC_D], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE, +- universal_newlines=True) +- lines = proc.stdout.readlines() +- +- for line in lines: +- s = line.strip().split() +- if len(s) < 1: +- continue +- name = s[0] +- if name.startswith(self.CERT_PREFIX): +- self._nss_delete_cert(name) +- elif name.startswith(self.CERTKEY_PREFIX): +- self._nss_delete_cert_and_key(name) ++ ret, pout, perr = run_command(['certutil', '-L', '-d', self.IPSEC_D], ++ "clear NSS database") ++ if ret: ++ return + +- except Exception as e: +- vlog.err("Failed to clear NSS database.\n" + str(e)) ++ for line in pout.splitlines(): ++ s = line.strip().split() ++ if len(s) < 1: ++ continue ++ name = s[0] ++ if name.startswith(self.CERT_PREFIX): ++ self._nss_delete_cert(name) ++ elif name.startswith(self.CERTKEY_PREFIX): ++ self._nss_delete_cert_and_key(name) + + def _nss_import_cert(self, cert, name, cert_type): + """Cert_type is 'CT,,' for the CA certificate and 'P,P,P' for the + normal certificate.""" +- try: +- proc = subprocess.Popen(['certutil', '-A', '-a', '-i', cert, +- '-d', self.IPSEC_D, '-n', +- name, '-t', cert_type], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- proc.wait() +- if proc.returncode: +- raise Exception(proc.stderr.read()) +- except Exception as e: +- vlog.err("Failed to import certificate into NSS.\n" + str(e)) ++ run_command(['certutil', '-A', '-a', '-i', cert, '-d', self.IPSEC_D, ++ '-n', name, '-t', cert_type], ++ "import certificate %s into NSS" % name) + + def _nss_delete_cert(self, name): +- try: +- proc = subprocess.Popen(['certutil', '-D', '-d', +- self.IPSEC_D, '-n', name], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- proc.wait() +- if proc.returncode: +- raise Exception(proc.stderr.read()) +- except Exception as e: +- vlog.err("Failed to delete certificate from NSS.\n" + str(e)) ++ run_command(['certutil', '-D', '-d', self.IPSEC_D, '-n', name], ++ "delete certificate %s from NSS" % name) + + def _nss_import_cert_and_key(self, cert, key, name): +- try: +- # Avoid deleting other files +- path = os.path.abspath('/tmp/%s.p12' % name) +- if not path.startswith('/tmp/'): +- raise Exception("Illegal certificate name!") +- +- # Create p12 file from pem files +- proc = subprocess.Popen(['openssl', 'pkcs12', '-export', +- '-in', cert, '-inkey', key, '-out', +- path, '-name', name, '-passout', 'pass:'], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- proc.wait() +- if proc.returncode: +- raise Exception(proc.stderr.read()) +- +- # Load p12 file to the database +- proc = subprocess.Popen(['pk12util', '-i', path, '-d', +- self.IPSEC_D, '-W', ''], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- proc.wait() +- if proc.returncode: +- raise Exception(proc.stderr.read()) +- +- except Exception as e: +- vlog.err("Import cert and key failed.\n" + str(e)) ++ # Avoid deleting other files ++ path = os.path.abspath('/tmp/%s.p12' % name) ++ if not path.startswith('/tmp/'): ++ vlog.err("Illegal certificate name '%s'!" % name) ++ return ++ ++ if run_command(['openssl', 'pkcs12', '-export', ++ '-in', cert, '-inkey', key, ++ '-out', path, '-name', name, ++ '-passout', 'pass:'], ++ "create p12 file from pem files")[0]: ++ return ++ ++ # Load p12 file to the database ++ run_command(['pk12util', '-i', path, '-d', self.IPSEC_D, '-W', ''], ++ "load p12 file to the NSS database") + os.remove(path) + + def _nss_delete_cert_and_key(self, name): +- try: +- # Delete certificate and private key +- proc = subprocess.Popen(['certutil', '-F', '-d', +- self.IPSEC_D, '-n', name], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- proc.wait() +- if proc.returncode: +- raise Exception(proc.stderr.read()) +- +- except Exception as e: +- vlog.err("Delete cert and key failed.\n" + str(e)) ++ # Delete certificate and private key ++ run_command(['certutil', '-F', '-d', self.IPSEC_D, '-n', name], ++ "delete certificate and private key for %s" % name) + + + class IPsecTunnel(object): +@@ -1194,23 +1285,19 @@ class IPsecMonitor(object): + self.ike_helper.clear_tunnel_state(self.tunnels[name]) + del self.tunnels[name] + +- if needs_refresh: ++ if needs_refresh or self.ike_helper.need_to_reconcile(self): + self.ike_helper.refresh(self) + + def _get_cn_from_cert(self, cert): +- try: +- proc = subprocess.Popen(['openssl', 'x509', '-noout', '-subject', +- '-nameopt', 'RFC2253', '-in', cert], +- stdout=subprocess.PIPE, +- stderr=subprocess.PIPE) +- proc.wait() +- if proc.returncode: +- raise Exception(proc.stderr.read()) +- m = re.search(r"CN=(.+?),", proc.stdout.readline().decode()) +- if not m: +- raise Exception("No CN in the certificate subject.") +- except Exception as e: +- vlog.warn(str(e)) ++ ret, pout, perr = run_command(['openssl', 'x509', '-noout', '-subject', ++ '-nameopt', 'RFC2253', '-in', cert], ++ "get certificate %s options" % cert) ++ if ret: ++ return None ++ ++ m = re.search(r"CN=(.+?),", pout.strip()) ++ if not m: ++ vlog.warn("No CN in the certificate subject (%s)." % cert) + return None + + return m.group(1) +@@ -1311,6 +1398,10 @@ def main(): + parser.add_argument("--ipsec-ctl", metavar="IPSEC-CTL", + help="Use DIR/IPSEC-CTL as location for " + " pluto ctl socket (libreswan only).") ++ parser.add_argument("--command-timeout", metavar="TIMEOUT", ++ type=int, default=120, ++ help="Timeout for external commands called by the " ++ "ovs-monitor-ipsec daemon, e.g. ipsec --start.") + + ovs.vlog.add_args(parser) + ovs.daemon.add_args(parser) +@@ -1320,11 +1411,13 @@ def main(): + + global monitor + global xfrm ++ global command_timeout + + root_prefix = args.root_prefix if args.root_prefix else "" + xfrm = XFRM(root_prefix) + monitor = IPsecMonitor(root_prefix, args.ike_daemon, + not args.no_restart_ike_daemon, args) ++ command_timeout = args.command_timeout + + remote = args.database + schema_helper = ovs.db.idl.SchemaHelper() +@@ -1371,6 +1464,7 @@ def main(): + poller = ovs.poller.Poller() + unixctl_server.wait(poller) + idl.wait(poller) ++ poller.timer_wait(RECONCILIATION_INTERVAL * 1000) + poller.block() + + unixctl_server.close() diff --git a/lib/bfd.c b/lib/bfd.c index 9af258917b..b8149e7897 100644 --- a/lib/bfd.c @@ -1190,10 +1827,18 @@ index 34ee7d0e2d..cdff4206e2 100644 if (n < 0) { dpctl_error(dpctl_p, -n, "parsing flow ufid"); diff --git a/lib/dpdk.c b/lib/dpdk.c -index d76d53f8f1..940c43c070 100644 +index d76d53f8f1..b7516257c5 100644 --- a/lib/dpdk.c +++ b/lib/dpdk.c -@@ -337,7 +337,9 @@ dpdk_init__(const struct smap *ovs_other_config) +@@ -323,7 +323,6 @@ dpdk_init__(const struct smap *ovs_other_config) + if (log_stream == NULL) { + VLOG_ERR("Can't redirect DPDK log: %s.", ovs_strerror(errno)); + } else { +- setbuf(log_stream, NULL); + rte_openlog_stream(log_stream); + } + +@@ -337,7 +336,9 @@ dpdk_init__(const struct smap *ovs_other_config) } #endif @@ -5878,6 +6523,31 @@ index 49ac45275a..7cbea51654 100755 try: sockfd = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) +diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at +index 01ebe364ee..b8c2757fed 100644 +--- a/tests/system-common-macros.at ++++ b/tests/system-common-macros.at +@@ -2,10 +2,7 @@ + # + # Delete namespaces from the running OS + m4_define([DEL_NAMESPACES], +- [m4_foreach([ns], [$@], +- [ip netns del ns +-]) +- ] ++ [m4_foreach([ns], [$@], [echo removing namespace ns; ip netns del ns])] + ) + + # ADD_NAMESPACES(ns [, ns ... ]) +@@ -72,7 +69,7 @@ m4_define([ADD_INT], + # + m4_define([ADD_VETH], + [ AT_CHECK([ip link add $1 type veth peer name ovs-$1 || return 77]) +- on_exit 'ip link del ovs-$1' ++ on_exit 'echo removing interface ovs-$1; ip link del ovs-$1' + CONFIGURE_VETH_OFFLOADS([$1]) + AT_CHECK([ip link set $1 netns $2]) + AT_CHECK([ip link set dev ovs-$1 up]) diff --git a/tests/system-dpdk-macros.at b/tests/system-dpdk-macros.at index 7cf9bac170..f8ba766739 100644 --- a/tests/system-dpdk-macros.at @@ -5909,30 +6579,255 @@ index 1c97bf7772..e79c755657 100644 AT_CHECK([ovs-vsctl del-port br10 dpdkvhostuserclient0], [], [stdout], [stderr]) OVS_DPDK_STOP_VSWITCHD(["dnl diff --git a/tests/system-ipsec.at b/tests/system-ipsec.at -index d3d27133b9..1e155fecea 100644 +index d3d27133b9..4ab384d89c 100644 --- a/tests/system-ipsec.at +++ b/tests/system-ipsec.at -@@ -110,16 +110,16 @@ m4_define([CHECK_LIBRESWAN], +@@ -8,6 +8,18 @@ m4_define([IPSEC_SETUP_UNDERLAY], + dnl Set up the underlay switch + AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])]) + ++m4_define([START_PLUTO], [ ++ rm -f $ovs_base/$1/pluto.pid ++ mkdir -p $ovs_base/$1/ipsec.d ++ touch $ovs_base/$1/ipsec.conf ++ touch $ovs_base/$1/secrets ++ ipsec initnss --nssdir $ovs_base/$1/ipsec.d ++ NS_CHECK_EXEC([$1], [ipsec pluto --config $ovs_base/$1/ipsec.conf \ ++ --ipsecdir $ovs_base/$1 --nssdir $ovs_base/$1/ipsec.d \ ++ --logfile $ovs_base/$1/pluto.log --secretsfile $ovs_base/$1/secrets \ ++ --rundir $ovs_base/$1], [0], [], [stderr]) ++]) ++ + dnl IPSEC_ADD_NODE([namespace], [device], [address], [peer address])) + dnl + dnl Creates a dummy host that acts as an IPsec endpoint. Creates host in +@@ -45,15 +57,8 @@ m4_define([IPSEC_ADD_NODE], + on_exit "kill_ovs_vswitchd `cat $ovs_base/$1/vswitchd.pid`" + + dnl Start pluto +- mkdir -p $ovs_base/$1/ipsec.d +- touch $ovs_base/$1/ipsec.conf +- touch $ovs_base/$1/secrets +- ipsec initnss --nssdir $ovs_base/$1/ipsec.d +- NS_CHECK_EXEC([$1], [ipsec pluto --config $ovs_base/$1/ipsec.conf \ +- --ipsecdir $ovs_base/$1 --nssdir $ovs_base/$1/ipsec.d \ +- --logfile $ovs_base/$1/pluto.log --secretsfile $ovs_base/$1/secrets \ +- --rundir $ovs_base/$1], [0], [], [stderr]) +- on_exit "kill `cat $ovs_base/$1/pluto.pid`" ++ START_PLUTO([$1]) ++ on_exit 'kill $(cat $ovs_base/$1/pluto.pid)' + + dnl Start ovs-monitor-ipsec + NS_CHECK_EXEC([$1], [ovs-monitor-ipsec unix:${OVS_RUNDIR}/$1/db.sock\ +@@ -66,7 +71,9 @@ m4_define([IPSEC_ADD_NODE], + on_exit "kill `cat $ovs_base/$1/ovs-monitor-ipsec.pid`" + + dnl Set up OVS bridge +- NS_EXEC([$1], [ovs-vsctl --db unix:$ovs_base/$1/db.sock add-br br-ipsec])] ++ NS_CHECK_EXEC([$1], ++ [ovs-vsctl --db unix:$ovs_base/$1/db.sock add-br br-ipsec \ ++ -- set-controller br-ipsec punix:$ovs_base/br-ipsec.$1.mgmt])] + ) + m4_define([IPSEC_ADD_NODE_LEFT], [IPSEC_ADD_NODE(left, p0, $1, $2)]) + m4_define([IPSEC_ADD_NODE_RIGHT], [IPSEC_ADD_NODE(right, p1, $1, $2)]) +@@ -110,16 +117,18 @@ m4_define([CHECK_LIBRESWAN], dnl IPSEC_STATUS_LOADED([]) dnl dnl Get number of loaded connections from ipsec status -m4_define([IPSEC_STATUS_LOADED], [ipsec status --rundir $ovs_base/$1 | \ -+m4_define([IPSEC_STATUS_LOADED], [ipsec --rundir $ovs_base/$1 status | \ ++m4_define([IPSEC_STATUS_LOADED], [ ++ ipsec --rundir $ovs_base/$1 status | \ grep "Total IPsec connections" | \ - sed 's/[[0-9]]* Total IPsec connections: loaded \([[0-2]]\), active \([[0-2]]\).*/\1/m']) -+ sed 's/[[0-9]]* *Total IPsec connections: loaded \([[0-2]]\), active \([[0-2]]\).*/\1/m']) ++ sed 's/[[0-9]]* *Total IPsec connections: loaded \([[0-9]]*\), active \([[0-9]]*\).*/\1/m']) dnl IPSEC_STATUS_ACTIVE([]) dnl dnl Get number of active connections from ipsec status -m4_define([IPSEC_STATUS_ACTIVE], [ipsec status --rundir $ovs_base/$1 | \ -+m4_define([IPSEC_STATUS_ACTIVE], [ipsec --rundir $ovs_base/$1 status | \ ++m4_define([IPSEC_STATUS_ACTIVE], [ ++ ipsec --rundir $ovs_base/$1 status | \ grep "Total IPsec connections" | \ - sed 's/[[0-9]]* Total IPsec connections: loaded \([[0-2]]\), active \([[0-2]]\).*/\2/m']) -+ sed 's/[[0-9]]* *Total IPsec connections: loaded \([[0-2]]\), active \([[0-2]]\).*/\2/m']) ++ sed 's/[[0-9]]* *Total IPsec connections: loaded \([[0-9]]*\), active \([[0-9]]*\).*/\2/m']) dnl CHECK_ESP_TRAFFIC() dnl +@@ -401,3 +410,174 @@ CHECK_ESP_TRAFFIC + + OVS_TRAFFIC_VSWITCHD_STOP() + AT_CLEANUP ++ ++AT_SETUP([IPsec -- Libreswan NxN geneve tunnels + reconciliation]) ++AT_KEYWORDS([ipsec libreswan scale reconciliation]) ++dnl Note: Geneve test may not work on older kernels due to CVE-2020-25645 ++dnl https://bugzilla.redhat.com/show_bug.cgi?id=1883988 ++ ++CHECK_LIBRESWAN() ++OVS_TRAFFIC_VSWITCHD_START() ++IPSEC_SETUP_UNDERLAY() ++ ++m4_define([NODES], [20]) ++ ++dnl Set up fake hosts. ++m4_for([id], [1], NODES, [1], [ ++ IPSEC_ADD_NODE([node-id], [p-id], 10.1.1.id, 10.1.1.254) ++ AT_CHECK([ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log \ ++ req -u node-id], [0], [stdout]) ++ AT_CHECK([ovs-pki -b -d ${ovs_base} -l ${ovs_base}/ovs-pki.log \ ++ self-sign node-id], [0], [stdout]) ++ AT_CHECK(OVS_VSCTL([node-id], set Open_vSwitch . \ ++ other_config:certificate=${ovs_base}/node-id-cert.pem \ ++ other_config:private_key=${ovs_base}/node-id-privkey.pem \ ++ -- set bridge br-ipsec other-config:hwaddr=f2:ff:00:00:00:id), ++ [0], [ignore], [ignore]) ++ on_exit "ipsec --rundir $ovs_base/node-id status > $ovs_base/node-id/status" ++]) ++ ++dnl Create a full mesh of tunnels. ++m4_for([LEFT], [1], NODES, [1], [ ++ m4_for([RIGHT], [1], NODES, [1], [ ++ if test LEFT -ne RIGHT; then ++ AT_CHECK(OVS_VSCTL(node-LEFT, add-port br-ipsec tun-RIGHT \ ++ -- set Interface tun-RIGHT type=geneve options:remote_ip=10.1.1.RIGHT \ ++ options:remote_cert=${ovs_base}/node-RIGHT-cert.pem), ++ [0], [ignore], [ignore]) ++ fi ++])]) ++ ++dnl These are not necessary, but nice to have in the test log in ++dnl order to spot pluto failures during the test. ++on_exit "grep -E 'Timed out|outdated|half-loaded|defunct' \ ++ $ovs_base/node-*/ovs-monitor-ipsec.log" ++on_exit "grep -E 'ABORT|ERROR' $ovs_base/node-*/pluto.log" ++ ++m4_define([WAIT_FOR_LOADED_CONNS], [ ++ m4_for([id], [1], NODES, [1], [ ++ echo "================== node-id =========================" ++ iterations=0 ++ loaded=0 ++ active=0 ++ dnl Using a custom loop instead of OVS_WAIT_UNTIL, because it may take ++ dnl much longer than a default timeout. The default retransmit timeout ++ dnl for pluto is 60 seconds. Also, we need to make sure pluto didn't ++ dnl crash in the process and revive it if it did, unfortunately. ++ while true; do ++ date ++ AT_CHECK([ipsec --rundir $ovs_base/node-id status 2>&1 \ ++ | grep -E "whack|Total"], [ignore], [stdout]) ++ if grep -E 'is Pluto running?|refused' stdout; then ++ echo "node-id: Pluto died, restarting..." ++ START_PLUTO([node-id]) ++ else ++ loaded=$(IPSEC_STATUS_LOADED(node-id)) ++ m4_if([$1], [active], ++ [active=$(IPSEC_STATUS_ACTIVE(node-id))], [active=$loaded]) ++ fi ++ if test "$loaded" -ne "$(( (NODES - 1) * 2 ))" -o \ ++ "$loaded" -ne "$active"; then ++ sleep 3 ++ else ++ break ++ fi ++ let iterations=$iterations+1 ++ AT_CHECK([test $iterations -lt 100]) ++ done ++ ]) ++]) ++ ++dnl Wait for all the connections to be loaded to pluto. Not waiting for ++dnl them to become active, because if pluto is down on one of the nodes, ++dnl some connections may not become active until we revive it. Some ++dnl connections may also never become active due to bugs in libreswan 4.x. ++WAIT_FOR_LOADED_CONNS() ++ ++AT_CHECK([ipsec auto --help], [ignore], [ignore], [stderr]) ++auto=auto ++if test -s stderr; then ++ auto= ++fi ++ ++dnl Remove connections for two tunnels. One fully and one partially. ++AT_CHECK([ipsec $auto --ctlsocket $ovs_base/node-1/pluto.ctl \ ++ --config $ovs_base/node-1/ipsec.conf \ ++ --delete tun-5-out-1], [0], [stdout]) ++AT_CHECK([ipsec $auto --ctlsocket $ovs_base/node-1/pluto.ctl \ ++ --config $ovs_base/node-1/ipsec.conf \ ++ --delete tun-2-in-1], [0], [stdout]) ++AT_CHECK([ipsec $auto --ctlsocket $ovs_base/node-1/pluto.ctl \ ++ --config $ovs_base/node-1/ipsec.conf \ ++ --delete tun-2-out-1], [0], [stdout]) ++ ++dnl Wait for the monitor to notice the missing connections. ++OVS_WAIT_UNTIL([grep -q 'tun-2.*need to reconcile' \ ++ $ovs_base/node-1/ovs-monitor-ipsec.log]) ++ ++dnl Wait for all the connections to be loaded back. ++WAIT_FOR_LOADED_CONNS() ++ ++dnl Next section will check connectivity between all the nodes. ++dnl Different versions of Libreswan 4.x have issues where connections ++dnl are not being correctly established or never become active in a ++dnl way that can not be mitigated from ovs-monitor-ipsec or the test. ++dnl So, only checking connectivity for Libreswan 3- or 5+. ++dnl Skipping in the middle of the test, so test can still fail while ++dnl testing with Libreswan 4, if the first half fails. ++AT_SKIP_IF([ipsec --version 2>&1 | grep -q 'Libreswan 4\.']) ++ ++dnl Turn off IPv6 and add static ARP entries for all namespaces to avoid ++dnl any broadcast / multicast traffic that would otherwise be multiplied ++dnl by each node creating a traffic storm. Add specific OpenFlow rules ++dnl to forward traffic to exact destinations without any MAC learning. ++m4_for([LEFT], [1], NODES, [1], [ ++ NS_CHECK_EXEC([node-LEFT], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], ++ [0], [ignore]) ++ AT_CHECK([ovs-ofctl del-flows unix:$ovs_base/br-ipsec.node-LEFT.mgmt]) ++ AT_CHECK([ovs-ofctl add-flow unix:$ovs_base/br-ipsec.node-LEFT.mgmt \ ++ "dl_dst=f2:ff:00:00:00:LEFT actions=LOCAL"]) ++ m4_for([RIGHT], [1], NODES, [1], [ ++ if test LEFT -ne RIGHT; then ++ NS_CHECK_EXEC([node-LEFT], ++ [ip neigh add 192.0.0.RIGHT lladdr f2:ff:00:00:00:RIGHT dev br-ipsec]) ++ AT_CHECK([ovs-ofctl add-flow unix:$ovs_base/br-ipsec.node-LEFT.mgmt \ ++ "dl_dst=f2:ff:00:00:00:RIGHT actions=tun-RIGHT"]) ++ fi ++ ]) ++]) ++ ++dnl Bring up and add IP addresses for br-ipsec interface. ++m4_for([id], [1], NODES, [1], [ ++ echo "================== node-id =========================" ++ NS_CHECK_EXEC([node-id], [ip addr add 192.0.0.id/24 dev br-ipsec]) ++ NS_CHECK_EXEC([node-id], [ip link set dev br-ipsec up]) ++]) ++ ++dnl Wait for all the connections to be loaded and active. In case one of ++dnl the pluto processes crashed some of the connections may never become ++dnl active. But we did run this loop with a pluto reviving logic twice ++dnl already, so the chances for pluto to be down here are much lower. ++WAIT_FOR_LOADED_CONNS([active]) ++ ++dnl Check the full mesh ping. ++m4_for([LEFT], [1], NODES, [1], [ ++ m4_for([RIGHT], [1], NODES, [1], [ ++ if test LEFT -ne RIGHT; then ++ echo "====== ping: node-LEFT --> node-RIGHT ==========" ++ dnl Ping without checking in case connection will recover after the ++ dnl first packet. ++ NS_CHECK_EXEC([node-LEFT], ++ [ping -q -c 1 -W 2 192.0.0.RIGHT | FORMAT_PING], ++ [ignore], [stdout]) ++ dnl Now check. If this one fails, there is no actual connectivity. ++ NS_CHECK_EXEC([node-LEFT], ++ [ping -q -c 3 -i 0.1 -W 2 192.0.0.RIGHT | FORMAT_PING], ++ [0], [dnl ++3 packets transmitted, 3 received, 0% packet loss, time 0ms ++]) ++ fi ++])]) ++ ++OVS_TRAFFIC_VSWITCHD_STOP() ++AT_CLEANUP diff --git a/tests/system-layer3-tunnels.at b/tests/system-layer3-tunnels.at index 6fbdedb64f..5dcdd2afae 100644 --- a/tests/system-layer3-tunnels.at diff --git a/SPECS/openvswitch3.3.spec b/SPECS/openvswitch3.3.spec index 4e90fe1..884658e 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: 10%{?dist} +Release: 12%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -769,6 +769,28 @@ exit 0 %endif %changelog +* Mon Nov 11 2024 Open vSwitch CI - 3.3.0-12 +- Merging upstream branch-3.3 [RH git: 3b0e3b25f5] + Commit list: + c51063b552 ci: Update GitHub actions runner from Ubuntu 22.04 to 24.04. + 9944e864ea dpdk: Fix dpdk logs being split over multiple lines. + + +* Sun Nov 03 2024 Open vSwitch CI - 3.3.0-11 +- Merging upstream branch-3.3 [RH git: 7bd8af7541] + Commit list: + f872d79c9d ipsec: libreswan: Reduce chances for crossing streams. + d7f5090848 tests: ipsec: Check that nodes can ping each other in the NxN test. + 6e2cfd3add tests: ipsec: Add NxN + reconciliation test. + 641cba7027 system-tests: Verbose cleanup of ports and namespaces. + 63a753b758 ipsec: Make command timeout configurable. + ace5640c9f ipsec: libreswan: Avoid monitor hanging on stuck ipsec commands. (FDP-846) + 461a3247e6 ipsec: libreswan: Try to bring non-active connections up. + d8856e9c14 ipsec: libreswan: Reconcile missing connections periodically. + 6df9627305 ipsec: libreswan: Fix regexp for connections waiting on child SA. + eb30c1bbee ipsec: Add a helper function to run commands from the monitor. + + * Thu Oct 31 2024 Open vSwitch CI - 3.3.0-10 - Merging upstream branch-3.3 [RH git: abdbdd9326] Commit list: