After some more digging I realized that ip length in the packet I was sending was shorter than the data, which made vswitchd cut it when calculating the packet length without padding. The issue is solved, working on the first revision for review now.
On Mon, Oct 17, 2022 at 5:05 PM Ihar Hrachyshka <[email protected]> wrote: > > When multichassis ports attached to localnet switches are forced to use > tunneling to communicate with other ports, MTU of the path between ports > may effectively change (to accommodate tunnel headers or otherwise). > > In this case, it may be helpful to inform TCP sessions about the change, > so that peers can adjust. > > This patch adds new flows as follows: > > - table 30: store check_pkt_len(mtu)->reg9[1] > - table 31: if reg9[1] is set, then send the packet to controller. > > The controller action is effectively as follows: > > icmp_error { > REGBIT_PKT_LARGER = 0 > REGBIT_EGRESS_LOOPBACK = 1 > eth.dst = eth.src = %s > ip.dst = ip.src = %s > ip.ttl = 255 > icmp4.type = 3 > icmp4.code = 4 > icmp4.frag_mtu = %d > resubmit(,8) > } > > Note that icmp4.frag_mtu is an OVN action itself, so there's an action > called from inside another action. > > The test case attached creates a "migrator" port (multichassis) and then > sends ICMP requests from this port to two ports - one local ('first') > and another remote ('second'). For each peer port, two packets are sent > - one "regular sized" (mtu 1400+) and one "oversized" (mtu 3000+). > > When "regular sized" packets are sent, both packets are (correctly) > delivered to 'first' and 'second' ports. > > When "oversized" packets are sent, for 'second' port, a ICMP > Fragmentation Needed error packet is generated and delivered back to > 'migrator' port. (The 'second' port doesn't receive the original ICMP > reply.) Which is correct. > > For 'first' (local) port, the controller() action in table=31 is *not* > triggered; instead the packet is delivered to the 'first' port, > regardless of the packet size. Which is incorrect. (Or at the very least > not what's intended.) > > I'm not sure why the controller action is not triggered for 'first'. > Here's some output that may be of relevance. > > ofctl dump-flows: > > cookie=0xa24ab06c, duration=1.678s, table=30, n_packets=8, n_bytes=5696, > idle_age=0, priority=110,metadata=0x1 > actions=check_pkt_larger(1514)->NXM_NX_REG9[1],resubmit(,31) > cookie=0xa24ab06c, duration=1.678s, table=31, n_packets=5, n_bytes=5570, > idle_age=0, priority=1000,ip,reg9=0x2/0x2,metadata=0x1 > actions=controller(userdata=00.00.00.0e.00.00.00.00.00.19.00.10.00.01.13.08.00.00.00.00.00.00.00.02.00.19.00.10.00.01.13.08.00.00.00.00.00.00.00.00.00.19.00.10.80.00.06.06.00.00.00.00.00.ff.00.00.00.19.00.10.80.00.08.06.00.00.00.00.00.ff.00.00.00.19.00.10.80.00.18.04.0a.00.00.64.00.00.00.00.00.19.00.10.80.00.16.04.0a.00.00.64.00.00.00.00.00.17.00.08.ff.00.00.00.00.19.00.10.80.00.26.01.03.00.00.00.00.00.00.00.00.19.00.10.80.00.28.01.04.00.00.00.00.00.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.0e.00.00.00.0d.00.00.00.00.05.ea.00.00.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.08.00.00.00,pause) > > appctl dp-flows: > recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=00:00:00:00:00:ff,dst=00:00:00:00:00:01),eth_type(0x0800),ipv4(frag=no), > packets:1, bytes:3034, used:0.075s, > actions:check_pkt_len(size=1514,gt(userspace(pid=0,controller(reason=1,dont_send=0,continuation=1,recirc_id=1,rule_cookie=0xa24ab06c,controller_id=0,max_len=65535))),le(3)) > recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=00:00:00:00:00:ff/01:00:00:00:00:00,dst=00:00:00:00:00:02),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), > packets:1, bytes:3034, used:0.037s, > actions:check_pkt_len(size=1514,gt(userspace(pid=0,controller(reason=1,dont_send=0,continuation=1,recirc_id=1,rule_cookie=0xa24ab06c,controller_id=0,max_len=65535))),le(tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=12:c4:c2:f7:d1:4f,src=72:5b:77:66:5e:46,dl_type=0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0xffff),geneve(crit,vni=0x1,options({class=0x102,type=0x80,len=4,0x30002}))),out_port(1)),101,check_pkt_len(size=1514,gt(userspace(pid=0,controller(reason=1,dont_send=0,continuation=1,recirc_id=2,rule_cookie=0xa24ab06c,controller_id=0,max_len=65535))),le(drop)))) > recirc_id(0),in_port(101),packet_type(ns=0,id=0),eth(src=00:00:00:00:00:02,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=10.0.0.2,tip=10.0.0.2,op=1), > packets:0, bytes:0, used:never, > actions:1,check_pkt_len(size=1514,gt(4,3),le(4,3)) > > dpctl/show: > system@ovs-system: > lookups: hit:2 missed:3 lost:0 > flows: 3 > port 0: ovs-system (dummy-internal) > port 1: br-phys (dummy-internal) > port 2: br-int (dummy-internal) > port 3: first > port 4: migrator > port 101: br-phys_n1 > port 6081: genev_sys_6081 (geneve: packet_type=ptap) > > The dp-flow for 'first' is > > recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=00:00:00:00:00:ff,dst=00:00:00:00:00:01),eth_type(0x0800),ipv4(frag=no), > packets:1, bytes:3034, used:0.075s, > actions:check_pkt_len(size=1514,gt(userspace(pid=0,controller(reason=1,dont_send=0,continuation=1,recirc_id=1,rule_cookie=0xa24ab06c,controller_id=0,max_len=65535))),le(3)) > > Since the packet is sized at 3000+, I would expect it to always trigger > gt() part of the equation, which is - calling to controller. But clearly > it doesn't (I can see it by putting debug logs inside the icmp4_error > pinctrl handler - it is not triggered). > > When I trace the oversized packet sent to 'second' (remote) port, then > the pinctrl handler is triggered. Here's the relevant dp-flow: > > recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=00:00:00:00:00:ff/01:00:00:00:00:00,dst=00:00:00:00:00:02),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), > packets:1, bytes:3034, used:0.037s, > actions:check_pkt_len(size=1514,gt(userspace(pid=0,controller(reason=1,dont_send=0,continuation=1,recirc_id=1,rule_cookie=0xa24ab06c,controller_id=0,max_len=65535))),le(tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=12:c4:c2:f7:d1:4f,src=72:5b:77:66:5e:46,dl_type=0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0xffff),geneve(crit,vni=0x1,options({class=0x102,type=0x80,len=4,0x30002}))),out_port(1)),101,check_pkt_len(size=1514,gt(userspace(pid=0,controller(reason=1,dont_send=0,continuation=1,recirc_id=2,rule_cookie=0xa24ab06c,controller_id=0,max_len=65535))),le(drop)))) > > I am not 100% sure I read it correctly. But it looks like if size>1514, > then the packet is sent to controller. Otherwise (le), it's prepared for > tunneling, then it's sent to br-phys (out_port=1) and then to br-phys_n1 > (101). I am not sure how to interpret the call to check_pkt_len in the > 'le' section, but I guess it's just an artifact of some sort since at > this point the packet will probably not ever trigger gt() branch, since > it was checked before, so it will always execute le(drop). Is this > reading correct? > > Any ideas why controller is not called for the local dp-flow? > > === > > One other thing I've noticed that I am curious about is - the packet > that is - in case of remote port - being handled to controller already > has the tunnel metadata set (probably because it's set in table=0), also > ethernet src/dst, ip src/dst etc. overridden. Then this geneve-wrapped > packet is being wrapped into the ICMPv4 error to return back to the > caller. I would think that the original packet should be embedded into > ICMP error, without geneve or other changes. Is there a way to access > the original packet as produced by the port inside pinctrl controller > action handler? What's the right way to handle it? (I am not yet sure if > this problem affects other existing uses of the icmp4_error - routers, > for one). > > Signed-off-by: Ihar Hrachyshka <[email protected]> > --- > controller/physical.c | 169 ++++++++++++++++++++++++++++++++++++++++++ > tests/ovn.at | 151 +++++++++++++++++++++++++++++++++++++ > 2 files changed, 320 insertions(+) > > diff --git a/controller/physical.c b/controller/physical.c > index 705146316..540b85e6c 100644 > --- a/controller/physical.c > +++ b/controller/physical.c > @@ -1072,6 +1072,36 @@ setup_activation_strategy(const struct > sbrec_port_binding *binding, > } > } > > +static size_t > +encode_start_controller_op(enum action_opcode opcode, bool pause, > + uint32_t meter_id, struct ofpbuf *ofpacts) > +{ > + size_t ofs = ofpacts->size; > + > + struct ofpact_controller *oc = ofpact_put_CONTROLLER(ofpacts); > + oc->max_len = UINT16_MAX; > + oc->reason = OFPR_ACTION; > + oc->pause = pause; > + if (!ovs_feature_is_supported(OVS_DP_METER_SUPPORT)) { > + meter_id = NX_CTLR_NO_METER; > + } > + oc->meter_id = meter_id; > + > + struct action_header ah = { .opcode = htonl(opcode) }; > + ofpbuf_put(ofpacts, &ah, sizeof ah); > + > + return ofs; > +} > + > +static void > +encode_finish_controller_op(size_t ofs, struct ofpbuf *ofpacts) > +{ > + struct ofpact_controller *oc = ofpbuf_at_assert(ofpacts, ofs, sizeof > *oc); > + ofpacts->header = oc; > + oc->userdata_len = ofpacts->size - (ofs + sizeof *oc); > + ofpact_finish_CONTROLLER(ofpacts, &oc); > +} > + > static void > enforce_tunneling_for_multichassis_ports( > struct local_datapath *ld, > @@ -1124,6 +1154,145 @@ enforce_tunneling_for_multichassis_ports( > binding->header_.uuid.parts[0], &match, &ofpacts, > &binding->header_.uuid); > ofpbuf_uninit(&ofpacts); > + > + /* Store packet too large flag in reg9[1]. */ > + match_init_catchall(&match); > + match_set_metadata(&match, htonll(dp_key)); > + ofpbuf_init(&ofpacts, 0); > + // todo: get mtu from interface of the tunnel > + uint16_t frag_mtu = 14 + 20 + 8 + 1472; > + struct ofpact_check_pkt_larger *pkt_larger = > + ofpact_put_CHECK_PKT_LARGER(&ofpacts); > + pkt_larger->pkt_len = frag_mtu; > + pkt_larger->dst.field = mf_from_id(MFF_REG9); > + pkt_larger->dst.ofs = 1; > + > + put_resubmit(31, &ofpacts); > + ofctrl_add_flow(flow_table, 30, 110, > + binding->header_.uuid.parts[0], &match, &ofpacts, > + &binding->header_.uuid); > + ofpbuf_uninit(&ofpacts); > + > + /* Generate ICMP Fragmentation needed for IP packets that are too > large > + * (reg9[1] == 1) */ > + match_set_dl_type(&match, htons(ETH_TYPE_IP)); > + match_set_reg_masked(&match, MFF_REG9 - MFF_REG0, 1 << 1, 1 << 1); > + > + /* Return ICMP error with a part of the original IP packet included. > */ > + ofpbuf_init(&ofpacts, 0); > + // TODO: handle IPv6 packets > + size_t oc_offset = encode_start_controller_op( > + ACTION_OPCODE_ICMP4_ERROR, true, NX_CTLR_NO_METER, &ofpacts); > + > + /* Before sending the ICMP error packet back to the pipeline, set a > + * number of fields. */ > + struct ofpbuf inner_ofpacts; > + ofpbuf_init(&inner_ofpacts, 0); > + > + // The new error packet is no longer too large > + // REGBIT_PKT_LARGER = 0 > + ovs_be32 value = htonl(0); > + ovs_be32 mask = htonl(1 << 1); > + ofpact_put_set_field( > + &inner_ofpacts, mf_from_id(MFF_REG9), &value, &mask); > + > + // The new error packet is delivered locally > + // REGBIT_EGRESS_LOOPBACK = 1 > + value = htonl(MLF_ALLOW_LOOPBACK_BIT); > + mask = htonl(MLF_ALLOW_LOOPBACK_BIT); > + ofpact_put_set_field( > + &inner_ofpacts, mf_from_id(MFF_REG9), &value, &mask); > + > + // Deliver the error back to the originating port MAC address > + // eth.dst = %s > + for (int i = 0; i < mcp->n_mac; i++) { > + struct eth_addr peer_mac; > + // TODO: handle errors > + if (!str_to_mac(mcp->mac[i], &peer_mac)) { > + struct ofpact_mac *dst_mac = > + ofpact_put_SET_ETH_DST(&inner_ofpacts); > + dst_mac->mac = peer_mac; > + > + struct ofpact_mac *src_mac = > + ofpact_put_SET_ETH_SRC(&inner_ofpacts); > + src_mac->mac = peer_mac; > + // TODO: is it possible for a port to have multiple mac > + // addresses configured? > + break; > + } > + } > + > + // Deliver the error back to the originating port IP address > + // ip.dst = %s > + for (int i = 0; i < mcp->n_mac; i++) { > + bool found = false; > + struct lport_addresses laddrs; > + extract_lsp_addresses(mcp->mac[i], &laddrs); > + for (size_t k = 0; k < laddrs.n_ipv4_addrs; k++) { > + ovs_be32 peer_ip; > + // TODO: handle errors > + if (!str_to_ip(laddrs.ipv4_addrs[k].addr_s, &peer_ip)) { > + struct ofpact_ipv4 *dst_ip = > + ofpact_put_SET_IPV4_DST(&inner_ofpacts); > + dst_ip->ipv4 = peer_ip; > + > + struct ofpact_ipv4 *src_ip = > + ofpact_put_SET_IPV4_SRC(&inner_ofpacts); > + src_ip->ipv4 = peer_ip; > + > + found = true; > + } > + } > + // TODO: is it possible for a port to have multiple IP addresses > + // configured? > + if (found) { > + break; > + } > + } > + > + // ip.ttl = 255 > + struct ofpact_ip_ttl *ip_ttl = ofpact_put_SET_IP_TTL(&inner_ofpacts); > + ip_ttl->ttl = 255; > + > + // Destination Unreachable > + // icmp4.type = 3 > + uint8_t icmp_type = 3; > + ofpact_put_set_field( > + &inner_ofpacts, mf_from_id(MFF_ICMPV4_TYPE), &icmp_type, NULL); > + > + // Fragmentation Needed and DF was Set > + // TODO: was DF set though?.. > + // icmp4.code = 4 > + uint8_t icmp_code = 4; > + ofpact_put_set_field( > + &inner_ofpacts, mf_from_id(MFF_ICMPV4_CODE), &icmp_code, NULL); > + > + // Set desired fragment MTU > + // icmp4.frag_mtu = %d > + size_t frag_mtu_oc_offset = encode_start_controller_op( > + ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, > + NX_CTLR_NO_METER, &inner_ofpacts); > + // TODO: is it the value to set in ICMP error? > + ovs_be16 frag_mtu_ovs = htons(frag_mtu); > + ofpbuf_put(&inner_ofpacts, &frag_mtu_ovs, sizeof(frag_mtu_ovs)); > + encode_finish_controller_op(frag_mtu_oc_offset, &inner_ofpacts); > + > + // Finally, submit the ICMP error back to the ingress pipeline > + put_resubmit(8, &inner_ofpacts); > + > + // Attach nested actions to ICMP error controller handler > + ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size, > + &ofpacts, OFP15_VERSION); > + > + // Finalize the ICMP error controller handler > + encode_finish_controller_op(oc_offset, &ofpacts); > + > + ofctrl_add_flow(flow_table, 31, 1000, > + binding->header_.uuid.parts[0], &match, &ofpacts, > + &binding->header_.uuid); > + > + ofpbuf_uninit(&inner_ofpacts); > + ofpbuf_uninit(&ofpacts); > } > > struct tunnel *tun_elem; > diff --git a/tests/ovn.at b/tests/ovn.at > index f8b8db4df..aac45920d 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -14887,6 +14887,157 @@ OVN_CLEANUP([hv1],[hv2],[hv3]) > AT_CLEANUP > ]) > > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([localnet connectivity with multiple requested-chassis, max mtu]) > +AT_KEYWORDS([ovntest]) > +ovn_start > + > +net_add n1 > +for i in 1 2; do > + sim_add hv$i > + as hv$i > + check ovs-vsctl add-br br-phys > + ovn_attach n1 br-phys 192.168.0.$i > + check ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys > +done > + > +check ovn-nbctl ls-add ls0 -- add Logical_Switch ls0 other_config > vlan-passthru=true > +check ovn-nbctl lsp-add ls0 first > +check ovn-nbctl lsp-add ls0 second > +check ovn-nbctl lsp-add ls0 migrator > +check ovn-nbctl lsp-set-addresses first "00:00:00:00:00:01 10.0.0.1" > +check ovn-nbctl lsp-set-addresses second "00:00:00:00:00:02 10.0.0.2" > +check ovn-nbctl lsp-set-addresses migrator "00:00:00:00:00:ff 10.0.0.100" > + > +check ovn-nbctl lsp-add ls0 public > +check ovn-nbctl lsp-set-type public localnet > +check ovn-nbctl lsp-set-addresses public unknown > +check ovn-nbctl lsp-set-options public network_name=phys > + > +check ovn-nbctl lsp-set-options first requested-chassis=hv1 > vif-plug-mtu-request=1500 > +check ovn-nbctl lsp-set-options second requested-chassis=hv2 > vif-plug-mtu-request=1500 > +check ovn-nbctl lsp-set-options migrator requested-chassis=hv1,hv2 > vif-plug-mtu-request=1500 > + > +as hv1 check ovs-vsctl -- add-port br-int first -- \ > + set Interface first external-ids:iface-id=first \ > + options:tx_pcap=hv1/first-tx.pcap \ > + options:rxq_pcap=hv1/first-rx.pcap \ > + ofport-request=1 > +as hv2 check ovs-vsctl -- add-port br-int second -- \ > + set Interface second external-ids:iface-id=second \ > + options:tx_pcap=hv2/second-tx.pcap \ > + options:rxq_pcap=hv2/second-rx.pcap \ > + ofport-request=2 > + > +# Create Migrator interfaces on both hv1 and hv2 > +for hv in hv1 hv2; do > + as $hv check ovs-vsctl -- add-port br-int migrator -- \ > + set Interface migrator external-ids:iface-id=migrator \ > + options:tx_pcap=$hv/migrator-tx.pcap \ > + options:rxq_pcap=$hv/migrator-rx.pcap \ > + ofport-request=100 > +done > + > +send_icmp_packet() { > + local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 > ip_chksum=$7 data=$8 > + shift 8 > + > + local ip_ttl=ff > + local ip_len=001c > + local > packet=${eth_dst}${eth_src}08004500${ip_len}00004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${data} > + as hv$hv ovs-appctl netdev-dummy/receive $inport $packet > + echo $packet > +} > + > +reset_env() { > + as hv1 reset_pcap_file first hv1/first > + as hv2 reset_pcap_file second hv2/second > + as hv1 reset_pcap_file migrator hv1/migrator > + as hv2 reset_pcap_file migrator hv2/migrator > + > + for port in hv1/migrator hv2/migrator hv1/first hv2/second; do > + : > $port.expected > + done > +} > + > +check ovn-nbctl lsp-set-addresses first "00:00:00:00:00:01 10.0.0.1" > +check ovn-nbctl lsp-set-addresses second "00:00:00:00:00:02 10.0.0.2" > +check ovn-nbctl lsp-set-addresses migrator "00:00:00:00:00:ff 10.0.0.100" > + > +first_mac=000000000001 > +second_mac=000000000002 > +migrator_mac=0000000000ff > +first_ip=$(ip_to_hex 10 0 0 1) > +second_ip=$(ip_to_hex 10 0 0 2) > +migrator_ip=$(ip_to_hex 10 0 0 100) > + > +OVN_POPULATE_ARP > + > +reset_env > + > +# Check that packets of proper size are delivered to the intended ports > + > +len=1400 > +data=$(xxd -l $len -c $len -p < /dev/zero) > +packet=$(send_icmp_packet migrator 1 $migrator_mac $first_mac $migrator_ip > $first_ip 0000 $data) > +echo $packet >> hv1/first.expected > + > +data=$(xxd -l $len -c $len -p < /dev/zero) > +packet=$(send_icmp_packet migrator 1 $migrator_mac $second_mac $migrator_ip > $second_ip 0000 $data) > +echo $packet >> hv2/second.expected > + > +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv1/first-tx.pcap], [hv1/first.expected]) > +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv2/second-tx.pcap], > [hv2/second.expected]) > +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv1/migrator-tx.pcap], > [hv1/migrator.expected]) > +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv2/migrator-tx.pcap], > [hv2/migrator.expected]) > + > +reset_env > + > +# Now check that oversized packets are not delivered to the intended ports, > and > +# that the originating port receives an ICMP error suggesting fragmentation > + > +len=3000 > +data=$(xxd -l $len -c $len -p < /dev/zero) > +packet=$(send_icmp_packet migrator 1 $migrator_mac $first_mac $migrator_ip > $first_ip 0000 $data) > +echo $packet >> hv1/migrator.expected > + > +data=$(xxd -l $len -c $len -p < /dev/zero) > +packet=$(send_icmp_packet migrator 1 $migrator_mac $second_mac $migrator_ip > $second_ip 0000 $data) > +echo $packet >> hv1/migrator.expected > + > +as hv1 ovs-ofctl dump-flows br-int | grep -v n_packets=0 > +as hv1 ovs-appctl dpctl/dump-flows > +as hv1 ovs-appctl dpctl/dump-dps > +as hv1 ovs-appctl dpctl/show system@ovs-system > + > +# todo: uncomment when icmp error is generated for the local 'first' port > +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv1/first-tx.pcap], [hv1/first.expected]) > +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv2/second-tx.pcap], > [hv2/second.expected]) > + > +# it's harder to construct the original packet that includes geneve headers > +# etc. so check manually > +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/migrator-tx.pcap > > hv1/migrator.packets > +sed -i '/ffffffffffff/d' hv1/migrator.packets > + > +# todo: change to 2 when both packets generate icmp error > +AT_CHECK([wc -l hv1/migrator.packets | cut -c 1], [0], [2 > +]) > + > +for line in $(cat hv1/migrator.expected); do > + trimmed=$(echo $line | cut -c 1-100) > + echo "checking if packet $trimmed... generated an icmp error" > + AT_CHECK([grep ${trimmed} hv1/migrator.packets | wc -l | cut -c 1], [0], > [1 > +]) > +done > + > +# todo: check hv2/migrator too > +# todo: implement and test the opposite direction (from a regular port TO > multichassis) > + > +OVN_CLEANUP([hv1],[hv2]) > + > +AT_CLEANUP > +]) > + > OVN_FOR_EACH_NORTHD([ > AT_SETUP([options:activation-strategy for logical port]) > AT_KEYWORDS([multi-chassis]) > -- > 2.34.3 > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
