The RFC defines a Virtual Router Redundancy Protocol [0], in order for that protocol to work the workload might "spoof" MAC address within ARP or ND request/response. This wasn't allowed as the port security is specifically designed against spoofing and checks if the port security MAC address is the same for source of ARP/ND and the inner source/target address. To make the port security compliant add an option which when enabled will add extra flows that match on the MAC specified by the option (within the range) or any MACs.
[0] https://datatracker.ietf.org/doc/html/rfc5798 Reported-at: https://issues.redhat.com/browse/FDP-2979 Signed-off-by: Ales Musil <[email protected]> --- v2: Rebase on top of latest main. Add missing checks in the test. Rename the option to "port-security-allow-vrrpv3-arp-nd". Allow the list of MACs to be specified in the option. --- NEWS | 4 + controller/lflow.c | 208 ++++++++++++++++++++++++++++++----------- ovn-nb.xml | 11 +++ tests/ovn.at | 227 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 394 insertions(+), 56 deletions(-) diff --git a/NEWS b/NEWS index b7268c835..a72258610 100644 --- a/NEWS +++ b/NEWS @@ -93,6 +93,10 @@ Post v25.09.0 other_config column. - Introduce the capability to specify multiple ips for ovn-evpn-local-ip option. + - Add an LSP option called "port-security-allow-vrrpv3-arp-nd", this + allows VRRP v3 (RFC 5798) to work with port security enabled. The option + accepts list of MACs within the range specified by the RFC 5798 or + value "any" to allow the full range. OVN v25.09.0 - xxx xx xxxx -------------------------- diff --git a/controller/lflow.c b/controller/lflow.c index 915b24269..61dea42b6 100644 --- a/controller/lflow.c +++ b/controller/lflow.c @@ -2539,10 +2539,96 @@ build_in_port_sec_ip4_flows(const struct sbrec_port_binding *pb, &pb->header_.uuid); } +struct masked_eth_addr { + struct eth_addr addr; + struct eth_addr mask; +}; + +static void +extract_port_sec_arp_nd_inner_macs(const char *vrrp_option, + struct vector *arp_inner, + struct vector *nd_inner) +{ + static const struct masked_eth_addr maddr_zero = { + .addr = eth_addr_zero, + .mask = eth_addr_exact + }; + static const struct masked_eth_addr maddr_any_vrrp4 = { + .addr = ETH_ADDR_C(00,00,5e,00,01,00), + .mask = ETH_ADDR_C(ff,ff,ff,ff,ff,00) + }; + static const struct masked_eth_addr maddr_any_vrrp6 = { + .addr = ETH_ADDR_C(00,00,5e,00,02,00), + .mask = ETH_ADDR_C(ff,ff,ff,ff,ff,00) + }; + + /* Placeholder for the port security MAC. */ + vector_push(arp_inner, &maddr_zero); + vector_push(nd_inner, &maddr_zero); + /* The all zero SLL/TLL. */ + vector_push(nd_inner, &maddr_zero); + + if (!vrrp_option) { + return; + } + + if (!strcmp(vrrp_option, "any")) { + vector_push(arp_inner, &maddr_any_vrrp4); + vector_push(nd_inner, &maddr_any_vrrp6); + return; + } + + char *tokstr = xstrdup(vrrp_option); + char *save_ptr = NULL; + for (char *token = strtok_r(tokstr, ",", &save_ptr); + token != NULL; + token = strtok_r(NULL, ",", &save_ptr)) { + + struct masked_eth_addr maddr = { + .addr = eth_addr_zero, + .mask = eth_addr_exact + }; + eth_addr_from_string(token, &maddr.addr); + + if (!eth_addr_equals(maddr.addr, maddr_any_vrrp4.addr) && + eth_addr_equal_except(maddr.addr, maddr_any_vrrp4.addr, + maddr_any_vrrp4.mask)) { + + vector_push(arp_inner, &maddr); + } else if (!eth_addr_equals(maddr.addr, maddr_any_vrrp6.addr) && + eth_addr_equal_except(maddr.addr, maddr_any_vrrp6.addr, + maddr_any_vrrp6.mask)) { + vector_push(nd_inner, &maddr); + } else { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "Invalid VRRPv3 MAC address specified: %s", + token); + } + } + + free(tokstr); +} + +static void +port_sec_inner_mac_replace(struct vector *arp_inner, struct vector *nd_inner, + struct eth_addr mac) +{ + struct masked_eth_addr *maddr; + ovs_assert(vector_len(arp_inner)); + ovs_assert(vector_len(nd_inner)); + + maddr = vector_get_ptr(arp_inner, 0); + maddr->addr = mac; + + maddr = vector_get_ptr(nd_inner, 0); + maddr->addr = mac; +} + /* Adds the OF rules to allow ARP packets in 'in_port_sec_nd' table. */ static void build_in_port_sec_arp_flows(const struct sbrec_port_binding *pb, - struct lport_addresses *ps_addr, + const struct lport_addresses *ps_addr, + const struct vector *arp_inner, struct match *m, struct ofpbuf *ofpacts, struct ovn_desired_flow_table *flow_table) { @@ -2550,7 +2636,6 @@ build_in_port_sec_arp_flows(const struct sbrec_port_binding *pb, /* No ARP is allowed as only IPv6 addresses are configured. */ return; } - build_port_sec_allow_action(ofpacts); if (!ps_addr->n_ipv4_addrs) { @@ -2559,23 +2644,28 @@ build_in_port_sec_arp_flows(const struct sbrec_port_binding *pb, * table. * priority: 90 * match - "inport == pb->port && eth.src == ps_addr.ea && - * arp && arp.sha == ps_addr.ea" + * arp && arp.sha == {arp_inner[0], ..., arp_inner[n]}" * action - "port_sec_failed = 0;" */ reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); match_set_dl_src(m, ps_addr->ea); match_set_dl_type(m, htons(ETH_TYPE_ARP)); - match_set_arp_sha(m, ps_addr->ea); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); + + struct masked_eth_addr *maddr; + VECTOR_FOR_EACH_PTR (arp_inner, maddr) { + match_set_arp_sha_masked(m, maddr->addr, maddr->mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } } /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' * table. * priority: 90 * match - "inport == pb->port && eth.src == ps_addr.ea && - * arp && arp.sha == ps_addr.ea && arp.spa == {ps_addr.ipv4_addrs}" + * arp && arp.sha == ps_addr.ea && + * arp.sha == {arp_inner[0], ..., arp_inner[n]}" * action - "port_sec_failed = 0;" */ for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) { @@ -2591,9 +2681,14 @@ build_in_port_sec_arp_flows(const struct sbrec_port_binding *pb, } else { match_set_nw_src_masked(m, ps_addr->ipv4_addrs[j].addr, mask); } - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); + + struct masked_eth_addr *maddr; + VECTOR_FOR_EACH_PTR (arp_inner, maddr) { + match_set_arp_sha_masked(m, maddr->addr, maddr->mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } } } @@ -2697,7 +2792,8 @@ build_in_port_sec_ip6_flows(const struct sbrec_port_binding *pb, * 'in_port_sec_nd' table. */ static void build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, - struct lport_addresses *ps_addr, + const struct lport_addresses *ps_addr, + const struct vector *nd_inner, struct match *m, struct ofpbuf *ofpacts, struct ovn_desired_flow_table *flow_table) { @@ -2708,7 +2804,7 @@ build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, * priority: 90 * match - "inport == pb->port && eth.src == ps_addr.ea && * icmp6 && icmp6.code == 135 && icmp6.type == 0 && - * ip6.tll == 255 && nd.sll == {00:00:00:00:00:00, ps_addr.ea}" + * ip6.tll == 255 && nd.sll == {nd_inner[0], ..., nd_inner[n]}" * action - "port_sec_failed = 0;" */ reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); @@ -2718,15 +2814,13 @@ build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, match_set_icmp_type(m, 135); match_set_icmp_code(m, 0); - match_set_arp_sha(m, eth_addr_zero); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); - - match_set_arp_sha(m, ps_addr->ea); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); + struct masked_eth_addr *maddr; + VECTOR_FOR_EACH_PTR (nd_inner, maddr) { + match_set_arp_sha_masked(m, maddr->addr, maddr->mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } match_set_icmp_type(m, 136); match_set_icmp_code(m, 0); @@ -2736,23 +2830,20 @@ build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, * priority: 90 * match - "inport == pb->port && eth.src == ps_addr.ea && icmp6 && * icmp6.code == 136 && icmp6.type == 0 && ip6.tll == 255 && - * nd.tll == {00:00:00:00:00:00, ps_addr.ea} && + * nd.tll == {nd_inner[0], ..., nd_inner[n]} && * nd.target == {ps_addr.ipv6_addrs, lla}" * action - "port_sec_failed = 0;" */ struct in6_addr lla; in6_generate_lla(ps_addr->ea, &lla); - match_set_arp_tha(m, eth_addr_zero); - match_set_nd_target(m, &lla); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); - match_set_arp_tha(m, ps_addr->ea); - match_set_nd_target(m, &lla); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); + + VECTOR_FOR_EACH_PTR (nd_inner, maddr) { + match_set_arp_tha_masked(m, maddr->addr, maddr->mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) { reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); @@ -2762,7 +2853,6 @@ build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, match_set_nw_ttl(m, 255); match_set_icmp_type(m, 136); match_set_icmp_code(m, 0); - match_set_arp_tha(m, eth_addr_zero); if (ps_addr->ipv6_addrs[j].plen == 128 || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr, @@ -2773,14 +2863,12 @@ build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, &ps_addr->ipv6_addrs[j].mask); } - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); - - match_set_arp_tha(m, ps_addr->ea); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); + VECTOR_FOR_EACH_PTR (nd_inner, maddr) { + match_set_arp_tha_masked(m, maddr->addr, maddr->mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } } } else { /* Add the below logical flow equivalent OF rules in 'in_port_sec_nd' @@ -2788,18 +2876,15 @@ build_in_port_sec_nd_flows(const struct sbrec_port_binding *pb, * priority: 90 * match - "inport == pb->port && eth.src == ps_addr.ea && icmp6 && * icmp6.code == 136 && icmp6.type == 0 && ip6.tll == 255 && - * nd.tll == {00:00:00:00:00:00, ps_addr.ea}" + * nd.tll == {nd_inner[0], ..., nd_inner[n]}" * action - "port_sec_failed = 0;" */ - match_set_arp_tha(m, eth_addr_zero); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); - - match_set_arp_tha(m, ps_addr->ea); - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, - pb->header_.uuid.parts[0], m, ofpacts, - &pb->header_.uuid); + VECTOR_FOR_EACH_PTR (nd_inner, maddr) { + match_set_arp_tha_masked(m, maddr->addr, maddr->mask); + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, + pb->header_.uuid.parts[0], m, ofpacts, + &pb->header_.uuid); + } } } @@ -3032,6 +3117,13 @@ consider_port_sec_flows(const struct sbrec_port_binding *pb, return; } + struct vector arp_inner = VECTOR_EMPTY_INITIALIZER(struct masked_eth_addr); + struct vector nd_inner = VECTOR_EMPTY_INITIALIZER(struct masked_eth_addr); + + const char *vrrp3_opt = smap_get(&pb->options, + "port-security-allow-vrrpv3-arp-nd"); + extract_port_sec_arp_nd_inner_macs(vrrp3_opt, &arp_inner, &nd_inner); + struct match match = MATCH_CATCHALL_INITIALIZER; uint64_t stub[1024 / 8]; struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); @@ -3039,18 +3131,22 @@ consider_port_sec_flows(const struct sbrec_port_binding *pb, build_in_port_sec_default_flows(pb, &match, &ofpacts, flow_table); for (size_t i = 0; i < n_ps_addrs; i++) { + port_sec_inner_mac_replace(&arp_inner, &nd_inner, ps_addrs[i].ea); build_in_port_sec_no_ip_flows(pb, &ps_addrs[i], &match, &ofpacts, flow_table); build_in_port_sec_ip4_flows(pb, &ps_addrs[i], &match, &ofpacts, flow_table); - build_in_port_sec_arp_flows(pb, &ps_addrs[i], &match, &ofpacts, - flow_table); + build_in_port_sec_arp_flows(pb, &ps_addrs[i], &arp_inner, &match, + &ofpacts, flow_table); build_in_port_sec_ip6_flows(pb, &ps_addrs[i], &match, &ofpacts, flow_table); - build_in_port_sec_nd_flows(pb, &ps_addrs[i], &match, &ofpacts, - flow_table); + build_in_port_sec_nd_flows(pb, &ps_addrs[i], &nd_inner, &match, + &ofpacts, flow_table); } + vector_destroy(&arp_inner); + vector_destroy(&nd_inner); + /* Out port security. */ /* Add the below logical flow equivalent OF rules in 'out_port_sec_nd' diff --git a/ovn-nb.xml b/ovn-nb.xml index e9ca27413..5c30250f3 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1674,6 +1674,17 @@ <ref table="Interface" db="vswitch"/> table. This in turn will make OVS vswitchd update the MTU of the linked interface. </column> + + <column name="options" key="port-security-allow-vrrpv3-arp-nd" + type='{"type": "string"}'> + Allows the inner ARP SHA or ND TLL/SLL to be within the range of + MAC addresses specified by RFC 5798. The option accepts a list of + MACs or literal value "any", in that case the full range + is allowed. + + This option is only applicable if <ref column="port_security"/> + is populated. + </column> </group> </group> diff --git a/tests/ovn.at b/tests/ovn.at index 4d15d4a62..a7d7a0ef5 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -44187,3 +44187,230 @@ check ovn-nbctl --wait=hv lsp-set-type down_ext localnet OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Port security - VRRPv3 ARP/ND]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +ovn_start +net_add n1 +sim_add hv1 +as hv1 +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 + +check ovn-nbctl ls-add ls + +check ovn-nbctl lsp-add ls lsp1 +check ovn-nbctl lsp-set-addresses lsp1 "00:00:00:00:10:01 192.168.10.1 fd10::1" + +check ovn-nbctl lsp-add ls lsp2 +check ovn-nbctl lsp-set-addresses lsp2 "00:00:00:00:10:02 192.168.10.2 fd10::2" + +check ovs-vsctl -- add-port br-int vif1 -- \ + set interface vif1 external-ids:iface-id=lsp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap + +check ovs-vsctl -- add-port br-int vif2 -- \ + set interface vif2 external-ids:iface-id=lsp2 \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap + +wait_for_ports_up + +test_arp() { + local dropped=$1 + + packet=$(fmt_pkt " + Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:01') / + ARP(op=1, hwsrc='00:00:5e:00:01:05', hwdst='ff:ff:ff:ff:ff:ff', psrc='192.168.10.1', pdst='192.168.10.2') + ") + check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet + + packet=$(fmt_pkt " + Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:10:01') / + ARP(op=2, hwsrc='00:00:5e:00:01:05', hwdst='ff:ff:ff:ff:ff:ff', psrc='192.168.10.1', pdst='192.168.10.1') + ") + check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet + + if [[ "$dropped" != "yes" ]]; then + echo $packet >> vif2.expected + packet=$(fmt_pkt " + Ether(dst='00:00:00:00:10:01', src='00:00:00:00:10:02') / + ARP(op=2, hwsrc='00:00:00:00:10:02', hwdst='00:00:5e:00:01:05', psrc='192.168.10.2', pdst='192.168.10.1') + ") + echo $packet >> vif1.expected + fi +} +test_nd() { + local dropped=$1 + + packet=$(fmt_pkt " + Ether(dst='33:33:ff:00:00:01', src='00:00:00:00:10:01') / + IPv6(src='fd10::1', dst='ff02::1:ff00:2') / + ICMPv6ND_NS(tgt='fd10::2') / + ICMPv6NDOptSrcLLAddr(lladdr='00:00:5e:00:02:05') + ") + check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet + + packet=$(fmt_pkt " + Ether(dst='33:33:ff:00:00:01', src='00:00:00:00:10:01') / + IPv6(src='fd10::1', dst='ff02::1:ff00:1') / + ICMPv6ND_NA(tgt='fd10::1') / + ICMPv6NDOptDstLLAddr(lladdr='00:00:5e:00:02:05') + ") + check as hv1 ovs-appctl netdev-dummy/receive vif1 $packet + + if [[ "$dropped" != "yes" ]]; then + echo $packet >> vif2.expected + + packet=$(fmt_pkt " + Ether(dst='00:00:00:00:10:01', src='00:00:00:00:10:02') / + IPv6(src='fd10::2', dst='fd10::1') / + ICMPv6ND_NA(tgt='fd10::2', R=0, S=1, O=1) / + ICMPv6NDOptDstLLAddr(lladdr='00:00:00:00:10:02') + ") + echo $packet >> vif1.expected + fi +} + +reset_pcap_and_expected() { + reset_pcap_file vif1 hv1/vif1 + reset_pcap_file vif2 hv1/vif2 + + : > vif1.expected + : > vif2.expected +} + +AS_BOX([Without port security]) +reset_pcap_and_expected + +test_arp no +test_nd no + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC only port security]) +reset_pcap_and_expected +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" + +test_arp yes +test_nd yes + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC + IP port security]) +reset_pcap_and_expected +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 192.168.10.1 fd10::1" + +test_arp yes +test_nd yes + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC only port security, VRRPv3=any]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd=any +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" + +test_arp no +test_nd no + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC + IP port security, VRRPv3=any]) +reset_pcap_and_expected +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 192.168.10.1 fd10::1" + +test_arp no +test_nd no + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC only port security, VRRPv3 valid IPv4]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd="00:00:5e:00:01:05,00:00:5e:00:02:00" +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" + +test_arp no +test_nd yes + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC + IP only port security, VRRPv3 valid IPv4]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd="00:00:5e:00:01:05,00:00:5e:00:02:00" +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 192.168.10.1 fd10::1" + +test_arp no +test_nd yes + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC only port security, VRRPv3 valid IPv6]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd="00:00:5e:00:01:00,00:00:5e:00:02:05" +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" + +test_arp yes +test_nd no + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC + IP only port security, VRRPv3 valid IPv6]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd="00:00:5e:00:01:00,00:00:5e:00:02:05" +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 192.168.10.1 fd10::1" + +test_arp yes +test_nd no + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC only port security, VRRPv3 valid IPv4 + IPv6]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd="00:00:5e:00:01:05,00:00:5e:00:02:05" +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01" + +test_arp no +test_nd no + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC + IP only port security, VRRPv3 valid IPv4 + IPv6]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd="00:00:5e:00:01:05,00:00:5e:00:02:05" +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 192.168.10.1 fd10::1" + +test_arp no +test_nd no + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +AS_BOX([With MAC + IP only port security, VRRPv3 valid but different IPv4 + IPv6]) +reset_pcap_and_expected +check ovn-nbctl lsp-set-options lsp1 port-security-allow-vrrpv3-arp-nd="00:00:5e:00:01:01,00:00:5e:00:02:01" +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 "00:00:00:00:10:01 192.168.10.1 fd10::1" + +test_arp yes +test_nd yes + +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) +OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [vif2.expected]) + +OVN_CLEANUP([hv1 +/Invalid VRRPv3 MAC address specified/d]) + +AT_CLEANUP +]) -- 2.52.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
