On Fri, Feb 6, 2026 at 11:21 PM Ilya Maximets <[email protected]> wrote:
> On 2/6/26 10:54 AM, Ales Musil via dev wrote: > > 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 a special literal which when specified will allow > > user to add any/all MAC addresses defined by VRRPv3. The traffic > > from and to those additional MAC addresses will be allowed as > > well as permutations of ARP/ND inner MACs combined with the > > physical MAC as a source. > > > > [0] https://datatracker.ietf.org/doc/html/rfc9568 > > Reported-at: https://issues.redhat.com/browse/FDP-2979 > > Signed-off-by: Ales Musil <[email protected]> > > --- > > v4: Rebase on top of latest main. > > Update the RFC url. > > Add Jacob's ack. > > v5: Rebase on top of latest main. > > Address nits pointed out by Dumitru. > > Add extra test for invalid VRRPv3 MAC. > > Update the wording in documentation. > > Remove acks as the code changed. > > Do not populate flows for physical MAC when VRRP is specified. > > Thanks, Ales! Functionally this version looks good to me for the > most part, see some small comments below. > Thank you for the review Ilya, > > > --- > > NEWS | 3 + > > controller/lflow.c | 932 +++++++++++++++++++++++++++++---------------- > > ovn-nb.xml | 55 +++ > > tests/ovn.at | 779 +++++++++++++++++++++++++++++++++++++ > > 4 files changed, 1446 insertions(+), 323 deletions(-) > > > > diff --git a/NEWS b/NEWS > > index 2a2b5e12d..7c7458224 100644 > > --- a/NEWS > > +++ b/NEWS > > @@ -98,6 +98,9 @@ Post v25.09.0 > > reserving an unused IP from the backend's subnet. This change > allows > > using LRP IPs directly, eliminating the need to reserve additional > IPs > > per backend port. > > + - Add support for special port_security prefix "VRRPv3". This prefix > allows > > + CMS to specify one physical MAC and multiple VRRPv3 MAC addresses. > > + The VRRPv3 MAC also accepts masked format. > > nit: It feels like this record doesn't contain enough info to start using > the feature, which is fine, but it also doesn't contain enough info to > explain what it is for. > > Maybe replace the last two sentences with something like "This prefix > allows CMS to allow all required traffic for a VRRPv3 virtual router > behind LSP. See <man page reference> for more details." ? > Ack. > > > > > OVN v25.09.0 - xxx xx xxxx > > -------------------------- > > diff --git a/controller/lflow.c b/controller/lflow.c > > index 94fd8807c..904de8ff0 100644 > > --- a/controller/lflow.c > > +++ b/controller/lflow.c > > @@ -2340,6 +2340,157 @@ add_port_sec_flows(const struct shash > *binding_lports, > > } > > } > > > > +struct masked_ip4_addr { > > + ovs_be32 addr; > > + ovs_be32 mask; > > + ovs_be32 bcast; > > +}; > > + > > +struct masked_ip6_addr { > > + struct in6_addr addr; > > + struct in6_addr mask; > > +}; > > + > > +struct masked_eth_addr { > > + struct eth_addr addr; > > + struct eth_addr mask; > > +}; > > + > > +struct port_security_addresses { > > + struct eth_addr phys_addr; > > + /* Vector of 'struct masked_eth_addr'. */ > > + struct vector vrrp4; > > + /* Vector of 'struct masked_eth_addr'. */ > > + struct vector vrrp6; > > + /* Vector of 'struct masked_ip4_addr' .*/ > > + struct vector ip4; > > + /* Vector of 'struct masked_ip6_addr' .*/ > > + struct vector ip6; > > +}; > > + > > +static void > > +port_security_addresses_init(struct port_security_addresses *ps_addr) > > +{ > > + *ps_addr = (struct port_security_addresses) { > > + .phys_addr = eth_addr_zero, > > + .vrrp4 = VECTOR_EMPTY_INITIALIZER(struct masked_eth_addr), > > + .vrrp6 = VECTOR_EMPTY_INITIALIZER(struct masked_eth_addr), > > + .ip4 = VECTOR_EMPTY_INITIALIZER(struct masked_ip4_addr), > > + .ip6 = VECTOR_EMPTY_INITIALIZER(struct masked_ip6_addr), > > + }; > > +} > > + > > +static void > > +port_security_addresses_clear(struct port_security_addresses *ps_addr) > > +{ > > + vector_clear(&ps_addr->vrrp4); > > + vector_clear(&ps_addr->vrrp6); > > + vector_clear(&ps_addr->ip4); > > + vector_clear(&ps_addr->ip6); > > +} > > + > > +static void > > +port_security_addresses_destroy(struct port_security_addresses *ps_addr) > > +{ > > + vector_destroy(&ps_addr->vrrp4); > > + vector_destroy(&ps_addr->vrrp6); > > + vector_destroy(&ps_addr->ip4); > > + vector_destroy(&ps_addr->ip6); > > +} > > + > > +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) > > nit: trailing comma? Other structure initializers have it and ones for > this > structure inside the functions as well. > > > +}; > > + > > +static bool > > +port_security_addresses_add_vrrp_mac(struct port_security_addresses > *ps_addr, > > + struct eth_addr mac, unsigned int > plen) > > +{ > > + /* Only the last byte contains ID for VRRPv3. */ > > + if (plen < 40) { > > + return false; > > + } > > + > > + /* If the masked portion is non-zero, the host can only use > > + * the specified MAC address. If zero, the host is allowed > > + * to use any MAC address within the mask. > > This looks a little strange to me. I'd not expect 00:00:5e:00:01:42/40 to > only allow 00:00:5e:00:01:42 address. We should probbaly just fail here > if the masked part is not zero. > > The format where the masked part is not zero makes sense for the IPs in the > regular port security record, because it means IP within the subnet + > subnet > prefix length, and not the whole subnet. This kind of configuration is > needed, because we need to create a broadcast flow, and so we have to know > the prefix, even if we want to allow a single IP. > > For the VRRP MAC addresses though this doesn't make a lot of sense, we > don't > need to create any broadcast flows, we just need to know the subnet. So, > we either clear the masked bits or fail if they are present. Failing seems > less confusing from the user's perspective. > > WDYT? > Makes sense, let's reject masked MACs that have non-zero masked portion. > > Either way this behavior is documented for IPs, but not for MACs. But it > will be hard to explain in the docs why only the specified IP is used, when > there is no use for the prefix. > > > + */ > > + struct eth_addr mask = eth_addr_create_mask(plen); > > + struct masked_eth_addr maddr = (struct masked_eth_addr) { > > nit: shouldn't need to cast, it can be an initializer. Compliler probbaly > doesn't care, but semantially it's a copy of an anonymous structure vs > designated initializer. > We are using this style all over OVN, so I would rather remain consistent. > > + .addr = mac, > > + .mask = (eth_addr_to_uint64(mac) & ~eth_addr_to_uint64(mask)) > > + ? eth_addr_exact : mask, > > nit: should probably be 2 spaces to the right. > > > + }; > > + > > + /* The exact match on VRRPv3 MAC ending with zero is not allowed, > the > > + * id is starting from 1. */ > > + if (plen == 48) { > > + if (eth_addr_equals(mac, maddr_any_vrrp4.addr) || > > + eth_addr_equals(mac, maddr_any_vrrp6.addr)) { > > + return false; > > + } > > + } > > + > > + if (eth_addr_equal_except(maddr_any_vrrp4.addr, mac, > > + maddr_any_vrrp4.mask)) { > > + vector_push(&ps_addr->vrrp4, &maddr); > > + return true; > > + } > > + > > + if (eth_addr_equal_except(maddr_any_vrrp6.addr, mac, > > + maddr_any_vrrp6.mask)) { > > + vector_push(&ps_addr->vrrp6, &maddr); > > + return true; > > + } > > + > > + return false; > > +} > > + > > +static bool > > +port_security_addresses_add_ip(struct port_security_addresses *ps_addr, > > + struct in6_addr ip, unsigned int plen) > > +{ > > + /* When the netmask is applied, if the host portion is > > + * non-zero, the host can only use the specified > > + * address. If zero, the host is allowed to use any > > + * address in the subnet. Also add broadcast in the special case > > + * of matching only the specified address. > > nit: Line lengths seem inconsistent. Also single vs double spaces. > Ack. > > > + */ > > + if (IN6_IS_ADDR_V4MAPPED(&ip)) { > > + if (plen > 32) { > > + return false; > > + } > > + > > + ovs_be32 addr = in6_addr_get_mapped_ipv4(&ip); > > + ovs_be32 mask = be32_prefix_mask(plen); > > + > > + struct masked_ip4_addr maddr = (struct masked_ip4_addr) { > > nit: cast. > > > + .addr = addr, > > + .mask = (addr & ~mask) ? OVS_BE32_MAX : mask, > > + .bcast = (addr & ~mask) ? (addr | ~mask) : htonl(0), > > + }; > > + vector_push(&ps_addr->ip4, &maddr); > > + } else { > > + if (plen > 128) { > > + return false; > > + } > > + > > + struct in6_addr mask = ipv6_create_mask(plen); > > + struct masked_ip6_addr maddr = (struct masked_ip6_addr) { > > nit: cast. > > > + .addr = ip, > > + .mask = !ipv6_addr_is_host_zero(&ip, &mask) ? in6addr_exact > : mask, > > + }; > > + vector_push(&ps_addr->ip6, &maddr); > > + } > > + > > + return true; > > +} > > + > > static void > > reset_match_for_port_sec_flows(const struct sbrec_port_binding *pb, > > enum mf_field_id reg_id, struct match > *match) > > @@ -2446,24 +2597,39 @@ build_in_port_sec_default_flows(const struct > sbrec_port_binding *pb, > > &pb->header_.uuid); > > } > > > > +static void > > +build_out_port_sec_default_flows(const struct sbrec_port_binding *pb, > > + struct match *m, struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table > *flow_table) > > +{ > > + /* Add the below logical flow equivalent OF rules in > 'out_port_sec_nd' > > + * table. > > + * priority: 80 > > + * match - "outport == pb->logical_port" > > + * action - "port_sec_failed = 1;" > > + * descrption: "Drop all traffic" > > + */ > > + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > + build_port_sec_deny_action(ofpacts); > > + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 80, > > + pb->header_.uuid.parts[0], m, ofpacts, > > + &pb->header_.uuid); > > +} > > + > > static void > > build_in_port_sec_no_ip_flows(const struct sbrec_port_binding *pb, > > - struct lport_addresses *ps_addr, > > + struct eth_addr mac, struct eth_addr mask, > > struct match *m, struct ofpbuf *ofpacts, > > struct ovn_desired_flow_table *flow_table) > > { > > - if (ps_addr->n_ipv4_addrs || ps_addr->n_ipv6_addrs) { > > - return; > > - } > > - > > /* Add the below logical flow equivalent OF rules in 'in_port_sec' > table. > > * priority: 90 > > - * match - "inport == pb->logical_port && eth.src == ps_addr.ea" > > + * match - "inport == pb->logical_port && eth.src == mac/mask" > > * action - "next;" > > * description: "Advance the packet for ARP/ND check" > > */ > > reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); > > - match_set_dl_src(m, ps_addr->ea); > > + match_set_dl_src_masked(m, mac, mask); > > build_port_sec_adv_nd_check(ofpacts); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, > > pb->header_.uuid.parts[0], m, ofpacts, > > @@ -2472,43 +2638,26 @@ build_in_port_sec_no_ip_flows(const struct > sbrec_port_binding *pb, > > > > static void > > build_in_port_sec_ip4_flows(const struct sbrec_port_binding *pb, > > - struct lport_addresses *ps_addr, > > - struct match *m, struct ofpbuf *ofpacts, > > - struct ovn_desired_flow_table *flow_table) > > + struct eth_addr mac, struct eth_addr mask, > > + const struct vector *ip4_addrs, > > + struct match *m, struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table *flow_table) > > { > > - if (!ps_addr->n_ipv4_addrs) { > > - /* If no IPv4 addresses, then 'pb' is not allowed to send IPv4 > traffic. > > - * build_in_port_sec_default_flows() takes care of this > scenario. */ > > - return; > > - } > > - > > /* Advance all traffic from the port security eth address for ND > check. */ > > build_port_sec_allow_action(ofpacts); > > + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); > > + match_set_dl_src_masked(m, mac, mask); > > + match_set_dl_type(m, htons(ETH_TYPE_IP)); > > > > /* Add the below logical flow equivalent OF rules in in_port_sec. > > * priority: 90 > > - * match - "inport == pb->port && eth.src == ps_addr.ea && > > - * ip4.src == {ps_addr.ipv4_addrs}" > > + * match - "inport == pb->port && eth.src == mac/mask && > > + * ip4.src == {ip4}" > > * action - "port_sec_failed = 0;" > > */ > > - for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) { > > - 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_IP)); > > - > > - ovs_be32 mask = ps_addr->ipv4_addrs[j].mask; > > - /* When the netmask is applied, if the host portion is > > - * non-zero, the host can only use the specified > > - * address. If zero, the host is allowed to use any > > - * address in the subnet. > > - */ > > - if (ps_addr->ipv4_addrs[j].plen == 32 || > > - ps_addr->ipv4_addrs[j].addr & ~mask) { > > - match_set_nw_src(m, ps_addr->ipv4_addrs[j].addr); > > - } else { > > - match_set_nw_src_masked(m, ps_addr->ipv4_addrs[j].addr, > mask); > > - } > > - > > + const struct masked_ip4_addr *ip; > > + VECTOR_FOR_EACH_PTR (ip4_addrs, ip) { > > + match_set_nw_src_masked(m, ip->addr, ip->mask); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > @@ -2516,20 +2665,14 @@ build_in_port_sec_ip4_flows(const struct > sbrec_port_binding *pb, > > > > /* Add the below logical flow equivalent OF rules in in_port_sec. > > * priority: 90 > > - * match - "inport == pb->port && eth.src == ps_addr.ea && > > + * match - "inport == pb->port && eth.src == mac/mask && > > * ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && > > * udp.src == 67 && udp.dst == 68" > > * action - "port_sec_failed = 0;" > > * description: "Allow the DHCP requests." > > */ > > - 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_IP)); > > - > > - ovs_be32 ip4 = htonl(0); > > - match_set_nw_src(m, ip4); > > - ip4 = htonl(0xffffffff); > > - match_set_nw_dst(m, ip4); > > + match_set_nw_src(m, htonl(0)); > > + match_set_nw_dst(m, htonl(0xffffffff)); > > match_set_nw_proto(m, IPPROTO_UDP); > > match_set_tp_src(m, htons(68)); > > match_set_tp_dst(m, htons(67)); > > @@ -2542,33 +2685,42 @@ build_in_port_sec_ip4_flows(const struct > sbrec_port_binding *pb, > > /* 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, > > - struct match *m, struct ofpbuf *ofpacts, > > - struct ovn_desired_flow_table *flow_table) > > + struct eth_addr phys_mac, > > + const struct vector *ip4_addrs, > > + const struct vector *vrrp4_addrs, > > + bool is_vrrp, struct match *m, > > + struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table *flow_table) > > { > > - if (!ps_addr->n_ipv4_addrs && ps_addr->n_ipv6_addrs) { > > - /* No ARP is allowed as only IPv6 addresses are configured. */ > > - return; > > - } > > - > > build_port_sec_allow_action(ofpacts); > > + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); > > + match_set_dl_src(m, phys_mac); > > + match_set_dl_type(m, htons(ETH_TYPE_ARP)); > > > > - if (!ps_addr->n_ipv4_addrs) { > > + if (vector_is_empty(ip4_addrs)) { > > /* No IPv4 addresses. > > * 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" > > + * match - "inport == pb->port && eth.src == phys_mac && > > + * arp && arp.sha == {phys_mac, vrrp4_addrs}" > > * 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); > > + > > + if (!is_vrrp) { > > + match_set_arp_sha(m, phys_mac); > > + 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 *mmac; > > + VECTOR_FOR_EACH_PTR (vrrp4_addrs, mmac) { > > + match_set_arp_sha_masked(m, mmac->addr, mmac->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' > > @@ -2578,74 +2730,62 @@ build_in_port_sec_arp_flows(const struct > sbrec_port_binding *pb, > > * arp && arp.sha == ps_addr.ea && arp.spa == > {ps_addr.ipv4_addrs}" > > * action - "port_sec_failed = 0;" > > */ > > - for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) { > > - 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); > > - > > - ovs_be32 mask = ps_addr->ipv4_addrs[j].mask; > > - if (ps_addr->ipv4_addrs[j].plen == 32 || > > - ps_addr->ipv4_addrs[j].addr & ~mask) { > > - match_set_nw_src(m, ps_addr->ipv4_addrs[j].addr); > > - } else { > > - match_set_nw_src_masked(m, ps_addr->ipv4_addrs[j].addr, > mask); > > + const struct masked_ip4_addr *ip; > > + VECTOR_FOR_EACH_PTR (ip4_addrs, ip) { > > + match_set_nw_src_masked(m, ip->addr, ip->mask); > > + > > + if (!is_vrrp) { > > + match_set_arp_sha(m, phys_mac); > > + 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 *mmac; > > + VECTOR_FOR_EACH_PTR (vrrp4_addrs, mmac) { > > + match_set_arp_sha_masked(m, mmac->addr, mmac->mask); > > + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, > > + pb->header_.uuid.parts[0], m, ofpacts, > > + &pb->header_.uuid); > > } > > - ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, > > - pb->header_.uuid.parts[0], m, ofpacts, > > - &pb->header_.uuid); > > } > > } > > > > static void > > build_in_port_sec_ip6_flows(const struct sbrec_port_binding *pb, > > - struct lport_addresses *ps_addr, > > - struct match *m, struct ofpbuf *ofpacts, > > - struct ovn_desired_flow_table *flow_table) > > + struct eth_addr mac, struct eth_addr mask, > > + const struct vector *ip6_addrs, > > + struct match *m, struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table *flow_table) > > { > > - if (!ps_addr->n_ipv6_addrs) { > > - /* If no IPv6 addresses, then 'pb' is not allowed to send IPv6 > traffic. > > - * build_in_port_sec_default_flows() takes care of this > scenario. */ > > - return; > > - } > > - > > /* 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 && > > + * match - "inport == pb->port && eth.src == mac/mask && > > * ip6.src == {ps_addr.ipv6_addrs, lla}" > > * action - "next;" > > * description - Advance the packet for Neighbor Solicit/Adv check. > > */ > > build_port_sec_adv_nd_check(ofpacts); > > + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); > > + match_set_dl_src_masked(m, mac, mask); > > + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); > > > > - for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) { > > - 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_IPV6)); > > - > > - if (ps_addr->ipv6_addrs[j].plen == 128 > > - || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr, > > - &ps_addr->ipv6_addrs[j].mask)) { > > - match_set_ipv6_src(m, &ps_addr->ipv6_addrs[j].addr); > > - } else { > > - match_set_ipv6_src_masked(m, > &ps_addr->ipv6_addrs[j].network, > > - &ps_addr->ipv6_addrs[j].mask); > > - } > > - > > + const struct masked_ip6_addr *ip; > > + VECTOR_FOR_EACH_PTR (ip6_addrs, ip) { > > + match_set_ipv6_src_masked(m, &ip->addr, &ip->mask); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > } > > > > - 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_IPV6)); > > > > struct in6_addr lla; > > - in6_generate_lla(ps_addr->ea, &lla); > > - match_set_ipv6_src(m, &lla); > > + in6_generate_lla(mac, &lla); > > + unsigned int plen = 128 - 48 + eth_addr_get_prefix_len(mask); > > + struct in6_addr lla_mask = ipv6_create_mask(plen); > > > > + match_set_ipv6_src_masked(m, &lla, &lla_mask); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC, 90, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > @@ -2660,11 +2800,12 @@ build_in_port_sec_ip6_flows(const struct > sbrec_port_binding *pb, > > */ > > build_port_sec_allow_action(ofpacts); > > match_set_ipv6_src(m, &in6addr_any); > > - struct in6_addr ip6, mask; > > - char *err = ipv6_parse_masked("ff02::/16", &ip6, &mask); > > + > > + struct in6_addr ip6, ip_mask; > > + char *err = ipv6_parse_masked("ff02::/16", &ip6, &ip_mask); > > ovs_assert(!err); > > > > - match_set_ipv6_dst_masked(m, &ip6, &mask); > > + match_set_ipv6_dst_masked(m, &ip6, &ip_mask); > > match_set_nw_proto(m, IPPROTO_ICMPV6); > > match_set_icmp_type(m, 131); > > match_set_icmp_code(m, 0); > > @@ -2697,99 +2838,118 @@ 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, > > - struct match *m, struct ofpbuf *ofpacts, > > + struct eth_addr phys_mac, > > + const struct vector *ip6_addrs, > > + const struct vector *vrrp6_addrs, > > + bool is_vrrp, struct match *m, > > + struct ofpbuf *ofpacts, > > struct ovn_desired_flow_table *flow_table) > > { > > build_port_sec_allow_action(ofpacts); > > + reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); > > + match_set_dl_src(m, phys_mac); > > + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); > > + match_set_nw_proto(m, IPPROTO_ICMPV6); > > + match_set_icmp_code(m, 0); > > + match_set_nw_ttl(m, 255); > > > > /* 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 && > > - * icmp6 && icmp6.code == 135 && icmp6.type == 0 && > > - * ip6.tll == 255 && nd.sll == {00:00:00:00:00:00, > ps_addr.ea}" > > + * match - "inport == pb->port && eth.src == phys_mac && > > + * icmp6 && icmp6.type == 135 && icmp6.code == 0 && > > + * ip6.tll == 255 && > > + * nd.sll == {00:00:00:00:00:00, phys_mac, vrrp6_addrs}" > > * action - "port_sec_failed = 0;" > > */ > > - reset_match_for_port_sec_flows(pb, MFF_LOG_INPORT, m); > > - match_set_dl_type(m, htons(ETH_TYPE_IPV6)); > > - match_set_nw_proto(m, IPPROTO_ICMPV6); > > - match_set_dl_src(m, ps_addr->ea); > > - match_set_nw_ttl(m, 255); > > + > > 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); > > + if (!is_vrrp) { > > + match_set_arp_sha(m, phys_mac); > > + 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 *mmac; > > + VECTOR_FOR_EACH_PTR (vrrp6_addrs, mmac) { > > + match_set_arp_sha_masked(m, mmac->addr, mmac->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_sha_masked(m, eth_addr_zero, eth_addr_zero); > > match_set_icmp_type(m, 136); > > - match_set_icmp_code(m, 0); > > - if (ps_addr->n_ipv6_addrs) { > > + if (!vector_is_empty(ip6_addrs)) { > > /* Add the below logical flow equivalent OF rules in > 'in_port_sec_nd' > > * table if IPv6 addresses are configured. > > * 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.target == {ps_addr.ipv6_addrs, lla}" > > + * match - "inport == pb->port && eth.src == phys_mac && icmp6 > && > > + * icmp6.type == 136 && icmp6.code == 0 && ip6.tll == > 255 && > > + * nd.tll == {00:00:00:00:00:00, phys_mac, > vrrp6_addrs} && > > + * nd.target == {lla, ip6_addrs}" > > * 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); > > + in6_generate_lla(phys_mac, &lla); > > match_set_nd_target(m, &lla); > > + > > + 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); > > > > - for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) { > > - 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_IPV6)); > > - match_set_nw_proto(m, IPPROTO_ICMPV6); > > - 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, > > - > &ps_addr->ipv6_addrs[j].mask)) { > > - match_set_nd_target(m, &ps_addr->ipv6_addrs[j].addr); > > - } else { > > - match_set_nd_target_masked(m, > &ps_addr->ipv6_addrs[j].network, > > - > &ps_addr->ipv6_addrs[j].mask); > > - } > > + if (!is_vrrp) { > > + match_set_arp_tha(m, phys_mac); > > + 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 (vrrp6_addrs, mmac) { > > + match_set_arp_tha_masked(m, mmac->addr, mmac->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); > > + const struct masked_ip6_addr *ip; > > + VECTOR_FOR_EACH_PTR (ip6_addrs, ip) { > > + match_set_nd_target_masked(m, &ip->addr, &ip->mask); > > + > > + 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); > > + > > + if (!is_vrrp) { > > + match_set_arp_tha(m, phys_mac); > > + 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 (vrrp6_addrs, mmac) { > > + match_set_arp_tha_masked(m, mmac->addr, mmac->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' > > * table if no IPv6 addresses are configured. > > * priority: 90 > > - * match - "inport == pb->port && eth.src == ps_addr.ea && > icmp6 && > > + * match - "inport == pb->port && eth.src == phys_mac && icmp6 > && > > * icmp6.code == 136 && icmp6.type == 0 && ip6.tll == > 255 && > > - * nd.tll == {00:00:00:00:00:00, ps_addr.ea}" > > + * nd.tll == {00:00:00:00:00:00, phys_mac, > vrrp6_addrs}" > > * action - "port_sec_failed = 0;" > > */ > > match_set_arp_tha(m, eth_addr_zero); > > @@ -2797,27 +2957,36 @@ build_in_port_sec_nd_flows(const struct > sbrec_port_binding *pb, > > 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); > > + if (!is_vrrp) { > > + match_set_arp_tha(m, phys_mac); > > + 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 (vrrp6_addrs, mmac) { > > + match_set_arp_tha_masked(m, mmac->addr, mmac->mask); > > + ofctrl_add_flow(flow_table, OFTABLE_CHK_IN_PORT_SEC_ND, 90, > > + pb->header_.uuid.parts[0], m, ofpacts, > > + &pb->header_.uuid); > > + } > > } > > } > > > > static void > > build_out_port_sec_no_ip_flows(const struct sbrec_port_binding *pb, > > - struct lport_addresses *ps_addr, > > + struct eth_addr mac, struct eth_addr > mask, > > struct match *m, struct ofpbuf *ofpacts, > > struct ovn_desired_flow_table > *flow_table) > > { > > /* Add the below logical flow equivalent OF rules in 'out_port_sec' > table. > > * priority: 85 > > - * match - "outport == pb->logical_port && eth.dst == ps_addr.ea" > > + * match - "outport == pb->logical_port && eth.dst == mac/mask" > > * action - "port_sec_failed = 0;" > > * description: "Allow the packet if eth.dst matches." > > */ > > reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > - match_set_dl_dst(m, ps_addr->ea); > > + match_set_dl_dst_masked(m, mac, mask); > > build_port_sec_allow_action(ofpacts); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 85, > > pb->header_.uuid.parts[0], m, ofpacts, > > @@ -2826,97 +2995,70 @@ build_out_port_sec_no_ip_flows(const struct > sbrec_port_binding *pb, > > > > static void > > build_out_port_sec_ip4_flows(const struct sbrec_port_binding *pb, > > - struct lport_addresses *ps_addr, > > - struct match *m, struct ofpbuf *ofpacts, > > - struct ovn_desired_flow_table *flow_table) > > + struct eth_addr mac, struct eth_addr mask, > > + const struct vector *ip4_addrs, > > + struct match *m, struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table *flow_table) > > { > > - if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) { > > - /* No IPv4 and no IPv6 addresses in the port security. > > - * Both IPv4 and IPv6 traffic should be delivered to the > > - * lport. build_out_port_sec_no_ip_flows() takes care of > > - * adding the required flow(s) to allow. */ > > - return; > > - } > > + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > + match_set_dl_dst_masked(m, mac, mask); > > + match_set_dl_type(m, htons(ETH_TYPE_IP)); > > > > /* Add the below logical flow equivalent OF rules in 'out_port_sec' > table. > > * priority: 90 > > - * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && > ip4" > > + * match - "outport == pb->logical_port && eth.dst == mac/mask && > ip4" > > * action - "port_sec_failed = 1;" > > * description: Default drop IPv4 packets. If IPv4 addresses are > > * configured, then higher priority flows are added > > * to allow specific IPv4 packets. > > */ > > - reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > - match_set_dl_dst(m, ps_addr->ea); > > - match_set_dl_type(m, htons(ETH_TYPE_IP)); > > + > > build_port_sec_deny_action(ofpacts); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 90, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > > > - if (!ps_addr->n_ipv4_addrs) { > > + if (vector_is_empty(ip4_addrs)) { > > return; > > } > > > > + build_port_sec_allow_action(ofpacts); > > /* Add the below logical flow equivalent OF rules in 'out_port_sec' > table. > > * priority: 95 > > - * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && > > - * ip4.dst == {ps_addr.ipv4_addrs, 255.255.255.255, > 224.0.0.0/4}," > > + * match - "outport == pb->logical_port && eth.dst == mac/mask && > > + * ip4.dst == {ip4_addrs, 255.255.255.255, 224.0.0.0/4}," > > * action - "port_sec_failed = 0;" > > */ > > - build_port_sec_allow_action(ofpacts); > > - for (size_t j = 0; j < ps_addr->n_ipv4_addrs; j++) { > > - reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > - match_set_dl_dst(m, ps_addr->ea); > > - match_set_dl_type(m, htons(ETH_TYPE_IP)); > > - ovs_be32 mask = ps_addr->ipv4_addrs[j].mask; > > - if (ps_addr->ipv4_addrs[j].plen == 32 > > - || ps_addr->ipv4_addrs[j].addr & ~mask) { > > - > > - if (ps_addr->ipv4_addrs[j].plen != 32) { > > - /* Special case to allow bcast traffic. > > - * Eg. If ps_addr is 10.0.0.4/24, then add the below > flow > > - * priority: 95 > > - * match - "outport == pb->logical_port && > > - * eth.dst == ps_addr.ea && > > - * ip4.dst == 10.0.0.255" > > - * action - "port_sec_failed = 0;" > > - */ > > - ovs_be32 bcast_addr; > > - ovs_assert(ip_parse(ps_addr->ipv4_addrs[j].bcast_s, > > - &bcast_addr)); > > - match_set_nw_dst(m, bcast_addr); > > - ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, > 95, > > - pb->header_.uuid.parts[0], m, ofpacts, > > - &pb->header_.uuid); > > - } > > - > > - match_set_nw_dst(m, ps_addr->ipv4_addrs[j].addr); > > - } else { > > - /* host portion is zero */ > > - match_set_nw_dst_masked(m, ps_addr->ipv4_addrs[j].addr, > > - mask); > > - } > > - > > + const struct masked_ip4_addr *ip; > > + VECTOR_FOR_EACH_PTR (ip4_addrs, ip) { > > + match_set_nw_dst_masked(m, ip->addr, ip->mask); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > - } > > > > - reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > - match_set_dl_dst(m, ps_addr->ea); > > - match_set_dl_type(m, htons(ETH_TYPE_IP)); > > + if (ip->bcast) { > > + /* Special case to allow bcast traffic. > > + * Eg. If address is 10.0.0.4/24, then add the below flow > > + * priority: 95 > > + * match - "outport == pb->logical_port && > > + * eth.dst == ps_addr.ea && > > + * ip4.dst == 10.0.0.255" > > + * action - "port_sec_failed = 0;" > > + */ > > + match_set_nw_dst(m, ip->bcast); > > + ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, > > + pb->header_.uuid.parts[0], m, ofpacts, > > + &pb->header_.uuid); > > + } > > + } > > > > - ovs_be32 ip4 = htonl(0xffffffff); > > - match_set_nw_dst(m, ip4); > > + match_set_nw_dst(m, htonl(0xffffffff)); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > > > /* Allow 224.0.0.0/4 traffic. */ > > - ip4 = htonl(0xe0000000); > > - ovs_be32 mask = htonl(0xf0000000); > > - match_set_nw_dst_masked(m, ip4, mask); > > + match_set_nw_dst_masked(m, htonl(0xe0000000), htonl(0xf0000000)); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > @@ -2924,112 +3066,270 @@ build_out_port_sec_ip4_flows(const struct > sbrec_port_binding *pb, > > > > static void > > build_out_port_sec_ip6_flows(const struct sbrec_port_binding *pb, > > - struct lport_addresses *ps_addr, > > - struct match *m, struct ofpbuf *ofpacts, > > - struct ovn_desired_flow_table *flow_table) > > + struct eth_addr mac, struct eth_addr mask, > > + const struct vector *ip6_addrs, > > + struct match *m, struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table *flow_table) > > { > > - if (!ps_addr->n_ipv4_addrs && !ps_addr->n_ipv6_addrs) { > > - /* No IPv4 and no IPv6 addresses in the port security. > > - * Both IPv4 and IPv6 traffic should be delivered to the > > - * lport. build_out_port_sec_no_ip_flows() takes care of > > - * adding the required flow(s) to allow. */ > > - return; > > - } > > + reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > + match_set_dl_dst_masked(m, mac, mask); > > + match_set_dl_type(m, htons(ETH_TYPE_IPV6)); > > > > /* Add the below logical flow equivalent OF rules in 'out_port_sec' > table. > > * priority: 90 > > - * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && > ip6" > > + * match - "outport == pb->logical_port && eth.dst == mac/mask && > ip6" > > * action - "port_sec_failed = 1;" > > * description: Default drop IPv6 packets. If IPv6 addresses are > > * configured, then higher priority flows are added > > * to allow specific IPv6 packets. > > */ > > - reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > - match_set_dl_dst(m, ps_addr->ea); > > - match_set_dl_type(m, htons(ETH_TYPE_IPV6)); > > build_port_sec_deny_action(ofpacts); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 90, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > > > - if (!ps_addr->n_ipv6_addrs) { > > + if (vector_is_empty(ip6_addrs)) { > > return; > > } > > > > + build_port_sec_allow_action(ofpacts); > > /* Add the below logical flow equivalent OF rules in 'out_port_sec' > table. > > * priority: 95 > > - * match - "outport == pb->logical_port && eth.dst == ps_addr.ea && > > - * ip6.dst == {ps_addr.ipv6_addrs, lla, ff00::/8}," > > + * match - "outport == pb->logical_port && eth.dst == mac/mask && > > + * ip6.dst == {mac/mask, ip6_addrs, lla, ff00::/8}," > > * action - "port_sec_failed = 0;" > > */ > > - build_port_sec_allow_action(ofpacts); > > - for (size_t j = 0; j < ps_addr->n_ipv6_addrs; j++) { > > - reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > - match_set_dl_dst(m, ps_addr->ea); > > - match_set_dl_type(m, htons(ETH_TYPE_IPV6)); > > - > > - if (ps_addr->ipv6_addrs[j].plen == 128 > > - || !ipv6_addr_is_host_zero(&ps_addr->ipv6_addrs[j].addr, > > - &ps_addr->ipv6_addrs[j].mask)) { > > - match_set_ipv6_dst(m, &ps_addr->ipv6_addrs[j].addr); > > - } else { > > - match_set_ipv6_dst_masked(m, > &ps_addr->ipv6_addrs[j].network, > > - &ps_addr->ipv6_addrs[j].mask); > > - } > > - > > + const struct masked_ip6_addr *ip; > > + VECTOR_FOR_EACH_PTR (ip6_addrs, ip) { > > + match_set_ipv6_dst_masked(m, &ip->addr, &ip->mask); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > } > > > > struct in6_addr lla; > > - in6_generate_lla(ps_addr->ea, &lla); > > + in6_generate_lla(mac, &lla); > > + unsigned int plen = 128 - 48 + eth_addr_get_prefix_len(mask); > > + struct in6_addr lla_mask = ipv6_create_mask(plen); > > > > - reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, m); > > - match_set_dl_dst(m, ps_addr->ea); > > - match_set_dl_type(m, htons(ETH_TYPE_IPV6)); > > - match_set_ipv6_dst(m, &lla); > > + match_set_ipv6_dst_masked(m, &lla, &lla_mask); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > > > - struct in6_addr ip6, mask; > > - char *err = ipv6_parse_masked("ff00::/8", &ip6, &mask); > > + struct in6_addr ip6, ip_mask; > > + char *err = ipv6_parse_masked("ff00::/8", &ip6, &ip_mask); > > ovs_assert(!err); > > > > - match_set_ipv6_dst_masked(m, &ip6, &mask); > > + match_set_ipv6_dst_masked(m, &ip6, &ip_mask); > > ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 95, > > pb->header_.uuid.parts[0], m, ofpacts, > > &pb->header_.uuid); > > } > > > > static void > > -consider_port_sec_flows(const struct sbrec_port_binding *pb, > > - struct ovn_desired_flow_table *flow_table) > > +build_port_sec_entry_flows(const struct sbrec_port_binding *pb, > > + const struct port_security_addresses > *ps_addr, > > + struct match *m, struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table *flow_table) > > { > > - if (!pb->n_port_security) { > > - return; > > + /* Input no-ip flows. */ > > + if (vector_is_empty(&ps_addr->ip4) && > vector_is_empty(&ps_addr->ip6)) { > > + build_in_port_sec_no_ip_flows(pb, ps_addr->phys_addr, > eth_addr_exact, > > + m, ofpacts, flow_table); > > } > > > > - struct lport_addresses *ps_addrs; /* Port security addresses. */ > > - size_t n_ps_addrs = 0; > > + /* Input IPv4 flows. */ > > + if (!vector_is_empty(&ps_addr->ip4)) { > > + build_in_port_sec_ip4_flows(pb, ps_addr->phys_addr, > eth_addr_exact, > > + &ps_addr->ip4, m, ofpacts, > flow_table); > > + } > > > > - ps_addrs = xmalloc(sizeof *ps_addrs * pb->n_port_security); > > - for (size_t i = 0; i < pb->n_port_security; i++) { > > - if (!extract_lsp_addresses(pb->port_security[i], > > - &ps_addrs[n_ps_addrs])) { > > - static struct vlog_rate_limit rl > > - = VLOG_RATE_LIMIT_INIT(1, 1); > > - VLOG_WARN_RL(&rl, "invalid syntax '%s' in port " > > - "security. No MAC address found", > > - pb->port_security[i]); > > - continue; > > + /* Input ARP flows. */ > > + if (!vector_is_empty(&ps_addr->ip4) || > vector_is_empty(&ps_addr->ip6)) { > > + build_in_port_sec_arp_flows(pb, ps_addr->phys_addr, > &ps_addr->ip4, > > + &ps_addr->vrrp4, false, m, ofpacts, > > + flow_table); > > + } > > + > > + /* Input Ipv6 flows. */ > > + if (!vector_is_empty(&ps_addr->ip6)) { > > + build_in_port_sec_ip6_flows(pb, ps_addr->phys_addr, > eth_addr_exact, > > + &ps_addr->ip6, m, ofpacts, > flow_table); > > + } > > + > > + /* Input ND flows. */ > > + build_in_port_sec_nd_flows(pb, ps_addr->phys_addr, &ps_addr->ip6, > > + &ps_addr->vrrp6, false, m, ofpacts, > > + flow_table); > > + > > + /* Output no-ip flows. */ > > + build_out_port_sec_no_ip_flows(pb, ps_addr->phys_addr, > eth_addr_exact, > > + m, ofpacts, flow_table); > > + > > + /* Output IPv4 flows. */ > > + if (!vector_is_empty(&ps_addr->ip4) || > !vector_is_empty(&ps_addr->ip6)) { > > + build_out_port_sec_ip4_flows(pb, ps_addr->phys_addr, > eth_addr_exact, > > + &ps_addr->ip4, m, ofpacts, > flow_table); > > + } > > + > > + /* Output Ipv6 flows. */ > > + if (!vector_is_empty(&ps_addr->ip4) || > !vector_is_empty(&ps_addr->ip6)) { > > + build_out_port_sec_ip6_flows(pb, ps_addr->phys_addr, > eth_addr_exact, > > + &ps_addr->ip6, m, ofpacts, > flow_table); > > + } > > +} > > + > > +static void > > +build_port_sec_entry_vrrp_flows(const struct sbrec_port_binding *pb, > > + const struct port_security_addresses > *ps_addr, > > + struct match *m, struct ofpbuf *ofpacts, > > + struct ovn_desired_flow_table > *flow_table) > > +{ > > + const struct masked_eth_addr *maddr; > > + > > + /* Input no-ip flows. */ > > + if (vector_is_empty(&ps_addr->ip4) && > vector_is_empty(&ps_addr->ip6)) { > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) { > > + build_in_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask, > > + m, ofpacts, flow_table); > > + } > > + > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) { > > + build_in_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask, > > + m, ofpacts, flow_table); > > } > > - n_ps_addrs++; > > } > > > > - if (!n_ps_addrs) { > > - free(ps_addrs); > > + /* Input IPv4 flows. */ > > + if (!vector_is_empty(&ps_addr->ip4)) { > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) { > > + build_in_port_sec_ip4_flows(pb, maddr->addr, maddr->mask, > > + &ps_addr->ip4, m, ofpacts, > flow_table); > > + } > > + } > > + > > + /* Input ARP flows. */ > > + if (!vector_is_empty(&ps_addr->ip4) || > vector_is_empty(&ps_addr->ip6)) { > > + build_in_port_sec_arp_flows(pb, ps_addr->phys_addr, > &ps_addr->ip4, > > + &ps_addr->vrrp4, true, m, ofpacts, > > + flow_table); > > + } > > + > > + /* Input Ipv6 flows. */ > > + if (!vector_is_empty(&ps_addr->ip6)) { > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) { > > + build_in_port_sec_ip6_flows(pb, maddr->addr, maddr->mask, > > + &ps_addr->ip6, m, ofpacts, > flow_table); > > + } > > + } > > + > > + /* Input ND flows. */ > > + build_in_port_sec_nd_flows(pb, ps_addr->phys_addr, &ps_addr->ip6, > > + &ps_addr->vrrp6, true, m, ofpacts, > > + flow_table); > > + > > + /* Output no-ip flows. */ > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) { > > + build_out_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask, > > + m, ofpacts, flow_table); > > + } > > + > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) { > > + build_out_port_sec_no_ip_flows(pb, maddr->addr, maddr->mask, > > + m, ofpacts, flow_table); > > + } > > + > > + /* Output IPv4 flows. */ > > + if (!vector_is_empty(&ps_addr->ip4) || > !vector_is_empty(&ps_addr->ip6)) { > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp4, maddr) { > > + build_out_port_sec_ip4_flows(pb, maddr->addr, maddr->mask, > > + &ps_addr->ip4, m, ofpacts, > > + flow_table); > > + } > > + } > > + > > + /* Output Ipv6 flows. */ > > + if (!vector_is_empty(&ps_addr->ip4) || > !vector_is_empty(&ps_addr->ip6)) { > > + VECTOR_FOR_EACH_PTR (&ps_addr->vrrp6, maddr) { > > + build_out_port_sec_ip6_flows(pb, maddr->addr, maddr->mask, > > + &ps_addr->ip6, m, ofpacts, > > + flow_table); > > + } > > + } > > +} > > + > > +static bool > > +port_security_addresses_parse_entry(const char *entry, const char *lsp, > > + struct port_security_addresses > *ps_addr) > > +{ > > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > > + > > + bool vrrpv3 = !strncmp(entry, "VRRPv3", 6); > > + int n = vrrpv3 ? 7 : 0; > > + > > + if (!ovs_scan_len(entry, &n, ETH_ADDR_SCAN_FMT, > > + ETH_ADDR_SCAN_ARGS(ps_addr->phys_addr))) { > > + VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security for LSP > %s: " > > + "No MAC address found", entry, lsp); > > + return false; > > + } > > + > > + bool ok = true; > > + > > + /* Only MAC address is provided. */ > > + if (!entry[n]) { > > + goto vrrp_check; > > + } > > + > > + char *save_ptr = NULL; > > + char *tokstr = xstrdup(entry + n); > > + for (char *token = strtok_r(tokstr, " ", &save_ptr); > > + token != NULL; > > + token = strtok_r(NULL, " ", &save_ptr)) { > > + struct eth_addr mac; > > + struct in6_addr ip; > > + unsigned int plen; > > + > > + if (vrrpv3 && eth_addr_parse_masked(token, &mac, &plen)) { > > + if (!port_security_addresses_add_vrrp_mac(ps_addr, mac, > plen)) { > > + VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security > for" > > + " LSP %s: Invalid VRRPv3 MAC", token, lsp); > > + ok = false; > > + break; > > + } > > + } else if (ip46_parse_cidr(token, &ip, &plen)) { > > + if (!port_security_addresses_add_ip(ps_addr, ip, plen)) { > > + VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security > for" > > + " LSP %s: Invalid IP", token, lsp); > > + ok = false; > > + break; > > + } > > + } else { > > + VLOG_WARN_RL(&rl, "invalid syntax '%s' in port security for" > > + " LSP %s: Invalid IP or MAC", token, lsp); > > + ok = false; > > + break; > > + } > > + } > > + > > + free(tokstr); > > + > > +vrrp_check: > > + if (vrrpv3 && vector_is_empty(&ps_addr->vrrp4) && > > + vector_is_empty(&ps_addr->vrrp6)) { > > + vector_push(&ps_addr->vrrp4, &maddr_any_vrrp4); > > + vector_push(&ps_addr->vrrp6, &maddr_any_vrrp6); > > + } > > + > > + return ok; > > +} > > + > > +static void > > +consider_port_sec_flows(const struct sbrec_port_binding *pb, > > + struct ovn_desired_flow_table *flow_table) > > +{ > > + if (!pb->n_port_security) { > > return; > > } > > > > @@ -3037,48 +3337,34 @@ consider_port_sec_flows(const struct > sbrec_port_binding *pb, > > uint64_t stub[1024 / 8]; > > struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); > > > > - build_in_port_sec_default_flows(pb, &match, &ofpacts, flow_table); > > + bool flows_installed = false; > > + struct port_security_addresses ps_addr; > > + port_security_addresses_init(&ps_addr); > > > > - for (size_t i = 0; i < n_ps_addrs; i++) { > > - 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_ip6_flows(pb, &ps_addrs[i], &match, &ofpacts, > > - flow_table); > > - build_in_port_sec_nd_flows(pb, &ps_addrs[i], &match, &ofpacts, > > - flow_table); > > - } > > + for (size_t i = 0; i < pb->n_port_security; i++) { > > + if (port_security_addresses_parse_entry(pb->port_security[i], > > + pb->logical_port, > &ps_addr)) { > > + if (vector_is_empty(&ps_addr.vrrp4) && > > + vector_is_empty(&ps_addr.vrrp6)) { > > + build_port_sec_entry_flows(pb, &ps_addr, &match, > > + &ofpacts, flow_table); > > > > - /* Out port security. */ > > + } else { > > + build_port_sec_entry_vrrp_flows(pb, &ps_addr, &match, > > + &ofpacts, flow_table); > > + } > > > > - /* Add the below logical flow equivalent OF rules in > 'out_port_sec_nd' > > - * table. > > - * priority: 80 > > - * match - "outport == pb->logical_port" > > - * action - "port_sec_failed = 1;" > > - * descrption: "Drop all traffic" > > - */ > > - reset_match_for_port_sec_flows(pb, MFF_LOG_OUTPORT, &match); > > - build_port_sec_deny_action(&ofpacts); > > - ofctrl_add_flow(flow_table, OFTABLE_CHK_OUT_PORT_SEC, 80, > > - pb->header_.uuid.parts[0], &match, &ofpacts, > > - &pb->header_.uuid); > > + flows_installed = true; > > + } > > > > - for (size_t i = 0; i < n_ps_addrs; i++) { > > - build_out_port_sec_no_ip_flows(pb, &ps_addrs[i], &match, > &ofpacts, > > - flow_table); > > - build_out_port_sec_ip4_flows(pb, &ps_addrs[i], &match, &ofpacts, > > - flow_table); > > - build_out_port_sec_ip6_flows(pb, &ps_addrs[i], &match, &ofpacts, > > - flow_table); > > + port_security_addresses_clear(&ps_addr); > > } > > > > - ofpbuf_uninit(&ofpacts); > > - for (size_t i = 0; i < n_ps_addrs; i++) { > > - destroy_lport_addresses(&ps_addrs[i]); > > + if (flows_installed) { > > + build_in_port_sec_default_flows(pb, &match, &ofpacts, > flow_table); > > + build_out_port_sec_default_flows(pb, &match, &ofpacts, > flow_table); > > } > > - free(ps_addrs); > > + > > + ofpbuf_uninit(&ofpacts); > > + port_security_addresses_destroy(&ps_addr); > > } > > diff --git a/ovn-nb.xml b/ovn-nb.xml > > index 1acbf202b..a9c68e1a4 100644 > > --- a/ovn-nb.xml > > +++ b/ovn-nb.xml > > @@ -2057,6 +2057,21 @@ > > addresses within an element may be space or comma separated. > > </p> > > > > + <p> > > + This also supports the special prefix "VRRPv3" that allows > > + specification of single physical MAC and multiple VRRPv3 MAC > > + addresses. As for non VRRPv3 entries, multiple IP addresses > can be > > + associated with the specified MACs. "VRRPv3" with a single > > + physical MAC translates to allowing traffic for the whole > "VRRPv3" > > + range of MACs. See more in the examples. > > + > > + When the port security configuration entry contains the > VRRPv3 label, > > + the behavior for physical MAC (the first specified) is > different. > > + The installed flows will allow traffic from to VRRP MACs + > IPs. > > from/to ? > > > + The physical MAC is there to properly allow ARP/ND with given > > + VRRP MACs. > > Maybe mention here that to allow traffic that is not related to a virtual > router, e.g. IP traffic with a physical MAC as a source, a regular port > security entry should be added separately. > > > + </p> > > + > > <p> > > This column is provided as a convenience to cloud management > systems, > > but all of the features that it implements can be implemented > as ACLs > > @@ -2102,6 +2117,46 @@ > > 255.255.255.255, and any address in 224.0.0.0/4. The host > may not > > send or receive any IPv6 (including IPv6 Neighbor > Discovery) traffic. > > </dd> > > + > > + <dt><code>"VRRPv3 <PHYSICAL_MAC>"</code></dt> > > + <dd> > > + The host may also send ARP/ND packets with physical > > + MAC as a source and the inner SHA/TLL/SLL all the VRRPv3 > > + MACs, 00:00:5e:00:01:{VRID}/40 for IPv4 and > > + 00:00:5E:00:02:{VRID}/40 for IPv6. > > + </dd> > > + > > + <dt><code>"VRRPv3 <PHYSICAL_MAC> > > + <VRRPV3_MACv4_1>/[<MASK1>] > > + <VRRPV3_MACv4_N> > > + <VRRPV3_MACv6_1>/[<MASK2>] > > + <VRRPV3_MACv6_N>"</code></dt> > > + <dd> > > + The host may also send ARP/ND packets with physical > > + MAC as a source and the inner SHA/TLL/SLL all the VRRPv3 > > + MACs, 00:00:5e:00:01:{VRID} for IPv4 and > > + 00:00:5E:00:02:{VRID} for IPv6. The VRRPv3 MACs can be > provided > > + with a mask with prefix between /40 and /48. > > + </dd> > > + > > + <dt><code>"VRRPv3 <PHYSICAL_MAC> > > + <VRRPV3_MACv4_1>/[<MASK1>] > > + <VRRPV3_MACv4_N> > > + <VRRPV3_MACv6_1>/[<MASK2>] > > + <VRRPV3_MACv6_N> > > + <VRRPV3_IPv4_1>/[<MASK1>] > > + <VRRPV3_IPv4_N> > > + <VRRPV3_IPv6_1>/[<MASK2>] > > + <VRRPV3_IPv6_N>"</code></dt> > > + <dd> > > + The host may also send ARP/ND packets with physical > > + MAC as a source and the inner SHA/TLL/SLL all the VRRPv3 > > + MACs, 00:00:5e:00:01:{VRID} for IPv4 and > > + 00:00:5E:00:02:{VRID} for IPv6. The VRRPv3 MACs can be > provided > > + with a mask with prefix between /40 and /48. The provided IP > > + addresses further restrict the inner IP for ARP/ND. As well > > + as traffic to/from given VRRPv3 MACs and the provided IPs. > > 'and the provided IPs' seems redundant here. > > > + </dd> > > </dl> > > </column> > > </group> > > diff --git a/tests/ovn.at b/tests/ovn.at > > index 961b6ecf7..28f53a491 100644 > > --- a/tests/ovn.at > > +++ b/tests/ovn.at > > @@ -44611,3 +44611,782 @@ 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 \ > > + -- set logical_switch ls other-config:requested-tnl-key=1 > > + > > +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" \ > > + -- set logical_switch_port lsp1 options:requested-tnl-key=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" \ > > + -- set logical_switch_port lsp2 options:requested-tnl-key=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') > > nit: Indentation still inconsistent here. Should be 4 spaces to the left. > > > + ") > > + 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]) > > + > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | > ofctl_strip_all | sort | grep -v NXST_FLOW], [1]) > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND > | ofctl_strip_all | sort | grep -v NXST_FLOW], [1]) > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | > ofctl_strip_all | sort | grep -v NXST_FLOW], [1]) > > + > > +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]) > > + > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | > ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl > > + table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01 > actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND) > > + table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 > actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND) > > +]) > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND > | ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=80,arp,reg14=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_sha=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:00:00 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_tll=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > +]) > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | > ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl > > + table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > +]) > > + > > +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]) > > + > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC | > ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl > > + table=OFTABLE_CHK_IN_PORT_SEC, priority=80,reg14=0x1,metadata=0x1 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=131,icmp_code=0 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=135,icmp_code=0 > actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND) > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=::,ipv6_dst=ff02::/16,icmp_type=143,icmp_code=0 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,ip,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=192.168.10.1 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fd10::1 > actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND) > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,ipv6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,ipv6_src=fe80::200:ff:fe00:1001 > actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND) > > + table=OFTABLE_CHK_IN_PORT_SEC, > priority=90,udp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_src=0.0.0.0,nw_dst=255.255.255.255,tp_src=68,tp_dst=67 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC, priority=95,arp,reg14=0x1,metadata=0x1 > actions=resubmit(,OFTABLE_CHK_IN_PORT_SEC_ND) > > +]) > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_IN_PORT_SEC_ND > | ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=80,arp,reg14=0x1,metadata=0x1 actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=135 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=80,icmp6,reg14=0x1,metadata=0x1,nw_ttl=255,icmp_type=136 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,arp,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,arp_spa=192.168.10.1,arp_sha=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:00:00 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=135,icmp_code=0,nd_sll=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:00:00 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fd10::1,nd_tll=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:00:00 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_IN_PORT_SEC_ND, > priority=90,icmp6,reg14=0x1,metadata=0x1,dl_src=00:00:00:00:10:01,nw_ttl=255,icmp_type=136,icmp_code=0,nd_target=fe80::200:ff:fe00:1001,nd_tll=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > +]) > > +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_CHK_OUT_PORT_SEC | > ofctl_strip_all | sort | grep -v NXST_FLOW], [0], [dnl > > + table=OFTABLE_CHK_OUT_PORT_SEC, priority=80,reg15=0x1,metadata=0x1 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=85,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=90,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=90,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01 > actions=load:0x1->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=192.168.10.1 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst= > 224.0.0.0/4 actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=95,ip,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,nw_dst=255.255.255.255 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fd10::1 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=fe80::200:ff:fe00:1001 > actions=load:0->NXM_NX_REG10[[12]] > > + table=OFTABLE_CHK_OUT_PORT_SEC, > priority=95,ipv6,reg15=0x1,metadata=0x1,dl_dst=00:00:00:00:10:01,ipv6_dst=ff00::/8 > actions=load:0->NXM_NX_REG10[[12]] > > +]) > > + > > + > > +AS_BOX([With MAC only port security, VRRPv3=any]) > > +reset_pcap_and_expected > > +check ovn-nbctl --wait=hv lsp-set-port-security lsp1 > "00:00:00:00:10:01" "VRRPv3 00:00:00:00:10:01" > > It's good that we have all these combined tests, as that's how the feature > will most likely be used, but we now lost all the coverage for VRRP-only > cases. We need at least a few, otherwise we're not checking from which > of these entries the flows are coming from. > Ack, I have added a couple of checks for flows without the physical port security in v6. > > Best regards, Ilya Maximets. > > Thanks, Ales _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
