@@ -8233,6 +8233,70 @@ add_l2only_flood_all(struct ovn_port *p, struct hmap *lflows)
82338233 ds_destroy(&match);
82348234}
82358235
8236+ /* Allow VIF traffic if and only if eth.src matches the port MAC.
8237+ * This preserves MAC anti-spoofing while realxing IP/ARP restrictions.
8238+ */
8239+ static void
8240+ add_l2only_mac_spoofing_prevention(struct ovn_port *p, struct hmap *lflows, const char* src_mac){
8241+ if (!p || !p->od || !p->nbsp) {
8242+ VLOG_DBG("port is null, skipping...");
8243+ return;
8244+ }
8245+
8246+ if (!port_is_l2_only_port(p)) {
8247+ return;
8248+ }
8249+
8250+ struct ds match = DS_EMPTY_INITIALIZER;
8251+ struct ds action = DS_EMPTY_INITIALIZER;
8252+
8253+ /* Use src_mac as the allowed MAC set for this VIF. */
8254+ VLOG_DBG("Adding mac_spoofing prevention to port %s, mac_address: %s", p->key, src_mac);
8255+
8256+ struct eth_addr ea;
8257+ if (eth_addr_from_string(src_mac, &ea)) {
8258+ ds_clear(&match);
8259+ ds_clear(&action);
8260+ ds_put_format(&match, "inport == \"%s\" && eth.src != %s", p->key, src_mac);
8261+ ds_put_format(&action, "%s=1; next;", REGBIT_PORT_SEC_DROP);
8262+ ovn_lflow_add_with_hint(
8263+ lflows, p->od,
8264+ S_SWITCH_IN_CHECK_PORT_SEC,
8265+ 110, /* higher than minimal_portsec_bypass rules */
8266+ ds_cstr(&match),
8267+ ds_cstr(&action),
8268+ &p->nbsp->header_, NULL
8269+ );
8270+
8271+ /* ARP: ensure arp.sha matches MAC */
8272+ ds_clear(&match);
8273+ ds_put_format(&match, "inport == \"%s\" && eth.type == 0x0806 && arp.sha != %s", p->key, src_mac);
8274+ ovn_lflow_add_with_hint(lflows, p->od,
8275+ S_SWITCH_IN_CHECK_PORT_SEC,
8276+ 110, /* higher than minimal_portsec_bypass rules */
8277+ ds_cstr(&match),
8278+ ds_cstr(&action),
8279+ &p->nbsp->header_, NULL
8280+ );
8281+
8282+ /* Drop wherever REGBIT_PORT_SEC_DROP=1 with a higher priority than minimal_portsec_bypass */
8283+ ds_clear(&match);
8284+ ds_put_format(&match, "inport == \"%s\" && %s == 1", p->key, REGBIT_PORT_SEC_DROP);
8285+ ovn_lflow_add_with_hint(lflows, p->od,
8286+ S_SWITCH_IN_APPLY_PORT_SEC,
8287+ 110, /* higher than minimal_portsec_bypass rules */
8288+ ds_cstr(&match),
8289+ debug_drop_action(),
8290+ &p->nbsp->header_, NULL
8291+ );
8292+ }else {
8293+ VLOG_WARN("invalid mac_address: %s for port %s, skipping mac spoofing prevention...", src_mac, p->key);
8294+ }
8295+
8296+ ds_destroy(&match);
8297+ ds_destroy(&action);
8298+ }
8299+
82368300/* Minimal and scoped port-security bypass for L2-only VIFs.
82378301 * - Only affects this single VIF.
82388302 * - Keeps stage ordering intact (still runs later stages).
@@ -16097,7 +16161,6 @@ build_lswitch_and_lrouter_flows(
1609716161 const struct chassis_features *features,
1609816162 const char *svc_monitor_mac)
1609916163{
16100-
1610116164 char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
1610216165
1610316166 if (parallelization_state == STATE_USE_PARALLELIZATION) {
@@ -16211,18 +16274,30 @@ build_lswitch_and_lrouter_flows(
1621116274 *
1621216275 * Both are low/specific priority so the standard pipeline keeps
1621316276 * taking precedence when applicable. */
16277+ VLOG_DBG("processing l2_only ports...");
1621416278 HMAP_FOR_EACH (op, key_node, lsi.ls_ports) {
1621516279 if (!op || !op->od || !op->nbsp) {
16280+ VLOG_DBG("port is null, skipping.");
1621616281 continue;
1621716282 }
1621816283 if (!port_is_l2_only_port(op)) {
16284+ VLOG_DBG("port is not l2_only, skipping.");
1621916285 continue;
1622016286 }
1622116287
16222- VLOG_INFO ("L2-only VIF detected on %s; adding port-sec bypass + uu fallback",
16288+ VLOG_DBG ("L2-only VIF detected on %s; adding port-sec bypass + uu fallback",
1622316289 op->json_key);
16290+
16291+ bool allow_forged_mac = smap_get_bool(&op->nbsp->external_ids, "pf9-allow-mac-forged-transmits", false);
16292+ char *src_mac = smap_get_def(&op->nbsp->external_ids, "pf9-l2port-src-mac", "");
16293+
16294+ VLOG_DBG("port: %s; src_mac: %s; allow_forged_mac: %s", op->key, src_mac, allow_forged_mac ? "true" : "false");
16295+
1622416296 add_minimal_portsec_bypass(op, lsi.lflows);
1622516297 add_l2only_flood_all(op, lsi.lflows);
16298+ if (!allow_forged_mac && src_mac && src_mac[0]) {
16299+ add_l2only_mac_spoofing_prevention(op, lflows, src_mac);
16300+ }
1622616301 }
1622716302 stopwatch_stop(LFLOWS_PORTS_STOPWATCH_NAME, time_msec());
1622816303/* PF9 stop */
0 commit comments