diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -110,6 +110,7 @@ char tagname[PF_TAG_NAME_SIZE]; uint16_t dnpipe; uint32_t dnflags; + char bridge_to[IFNAMSIZ]; uint8_t action; struct pfctl_eth_anchor *anchor; diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -665,6 +665,9 @@ rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative"); rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard"); + strlcpy(rule->bridge_to, nvlist_get_string(nvl, "bridge_to"), + IFNAMSIZ); + rule->action = nvlist_get_number(nvl, "action"); } @@ -812,6 +815,8 @@ nvlist_add_number(nvl, "dnpipe", r->dnpipe); nvlist_add_number(nvl, "dnflags", r->dnflags); + nvlist_add_string(nvl, "bridge_to", r->bridge_to); + nvlist_add_number(nvl, "action", r->action); packed = nvlist_pack(nvl, &size); diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -351,7 +351,8 @@ void expand_eth_rule(struct pfctl_eth_rule *, struct node_if *, struct node_etherproto *, struct node_mac *, struct node_mac *, - struct node_host *, struct node_host *, const char *); + struct node_host *, struct node_host *, const char *, + const char *); void expand_rule(struct pfctl_rule *, struct node_if *, struct node_host *, struct node_proto *, struct node_os *, struct node_host *, struct node_port *, struct node_host *, @@ -432,6 +433,7 @@ struct { struct node_mac *mac; } etheraddr; + char *bridge_to; struct { struct node_host *host; u_int8_t rt; @@ -503,7 +505,7 @@ %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS -%token DIVERTTO DIVERTREPLY +%token DIVERTTO DIVERTREPLY BRIDGE_TO %token STRING %token NUMBER %token PORTBINARY @@ -563,6 +565,7 @@ %type etherproto etherproto_list etherproto_item %type etherfromto %type etherfrom etherto +%type bridge %type xmac mac mac_list macspec %% @@ -1195,7 +1198,7 @@ } ; -etherrule : ETHER action dir quick interface etherproto etherfromto l3fromto etherfilter_opts +etherrule : ETHER action dir quick interface bridge etherproto etherfromto l3fromto etherfilter_opts { struct pfctl_eth_rule r; @@ -1207,23 +1210,23 @@ r.action = $2.b1; r.direction = $3; r.quick = $4.quick; - if ($9.tag != NULL) - memcpy(&r.tagname, $9.tag, sizeof(r.tagname)); - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, + if ($10.tag != NULL) + memcpy(&r.tagname, $10.tag, sizeof(r.tagname)); + if ($10.match_tag) + if (strlcpy(r.match_tagname, $10.match_tag, PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { yyerror("tag too long, max %u chars", PF_TAG_NAME_SIZE - 1); YYERROR; } - r.match_tag_not = $9.match_tag_not; - if ($9.queues.qname != NULL) - memcpy(&r.qname, $9.queues.qname, sizeof(r.qname)); - r.dnpipe = $9.dnpipe; - r.dnflags = $9.free_flags; + r.match_tag_not = $10.match_tag_not; + if ($10.queues.qname != NULL) + memcpy(&r.qname, $10.queues.qname, sizeof(r.qname)); + r.dnpipe = $10.dnpipe; + r.dnflags = $10.free_flags; - expand_eth_rule(&r, $5, $6, $7.src, $7.dst, - $8.src.host, $8.dst.host, ""); + expand_eth_rule(&r, $5, $7, $8.src, $8.dst, + $9.src.host, $9.dst.host, $6, ""); } ; @@ -1315,7 +1318,7 @@ r.quick = $5.quick; expand_eth_rule(&r, $6, $7, $8.src, $8.dst, - $9.src.host, $9.dst.host, + $9.src.host, $9.dst.host, NULL, pf->eastack[pf->asd + 1] ? pf->ealast->name : $3); free($3); @@ -1361,6 +1364,14 @@ } ; +bridge : /* empty */ { + $$ = NULL; + } + | BRIDGE_TO STRING { + $$ = strdup($2); + } + ; + scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts { struct pfctl_rule r; @@ -5801,7 +5812,8 @@ expand_eth_rule(struct pfctl_eth_rule *r, struct node_if *interfaces, struct node_etherproto *protos, struct node_mac *srcs, struct node_mac *dsts, - struct node_host *ipsrcs, struct node_host *ipdsts, const char *anchor_call) + struct node_host *ipsrcs, struct node_host *ipdsts, + const char *bridge_to, const char *anchor_call) { char tagname[PF_TAG_NAME_SIZE]; char match_tagname[PF_TAG_NAME_SIZE]; @@ -5852,6 +5864,9 @@ if (strlcpy(r->qname, qname, sizeof(r->qname)) >= sizeof(r->qname)) errx(1, "expand_eth_rule: r->qname"); + if (bridge_to) + strlcpy(r->bridge_to, bridge_to, sizeof(r->bridge_to)); + pfctl_append_eth_rule(pf, r, anchor_call); )))))); @@ -6110,6 +6125,7 @@ { "bitmask", BITMASK}, { "block", BLOCK}, { "block-policy", BLOCKPOLICY}, + { "bridge-to", BRIDGE_TO}, { "buckets", BUCKETS}, { "cbq", CBQ}, { "code", CODE}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -774,6 +774,8 @@ else printf(" on %s", r->ifname); } + if (r->bridge_to[0]) + printf(" bridge-to %s", r->bridge_to); if (r->proto) printf(" proto 0x%04x", r->proto); diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5 --- a/share/man/man5/pf.conf.5 +++ b/share/man/man5/pf.conf.5 @@ -28,7 +28,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd March 9, 2022 +.Dd October 28, 2022 .Dt PF.CONF 5 .Os .Sh NAME @@ -710,6 +710,9 @@ .Ic group keyword in .Xr ifconfig 8 . +.It Ar bridge-to Aq interface +Packets matching this rule will be sent out of the specified interface without +futher processing. .It Ar proto Aq Ar protocol This rule applies only to packets of this protocol. Note that Ethernet protocol numbers are different from those used in @@ -3076,8 +3079,9 @@ [ "keepcounters" ] ) ether-rule = "ether" etheraction [ ( "in" | "out" ) ] - [ "quick" ] [ "on" ifspec ] [ etherprotospec ] - etherhosts [ "l3" hosts ] [ etherfilteropt-list ] + [ "quick" ] [ "on" ifspec ] [ "bridge-to" interface-name ] + [ etherprotospec ] etherhosts [ "l3" hosts ] + [ etherfilteropt-list ] pf-rule = action [ ( "in" | "out" ) ] [ "log" [ "(" logopts ")"] ] [ "quick" ] diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -690,6 +690,8 @@ int qid; char tagname[PF_TAG_NAME_SIZE]; uint16_t tag; + char bridge_to_name[IFNAMSIZ]; + struct pfi_kkif *bridge_to; uint8_t action; uint16_t dnpipe; uint32_t dnflags; diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -3833,6 +3833,32 @@ (r->match_tag_not && r->match_tag != *tag)); } +static void +pf_bridge_to(struct pfi_kkif *kif, struct mbuf *m) +{ + struct ifnet *ifp = kif->pfik_ifp; + + /* If we don't have the interface drop the packet. */ + if (ifp == NULL) { + m_freem(m); + return; + } + + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_XETHER: + case IFT_L2VLAN: + case IFT_BRIDGE: + case IFT_IEEE8023ADLAG: + break; + default: + m_freem(m); + return; + } + + kif->pfik_ifp->if_transmit(kif->pfik_ifp, m); +} + static int pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0) { @@ -4092,6 +4118,11 @@ action = r->action; + if (action == PF_PASS && r->bridge_to) { + pf_bridge_to(r->bridge_to, *m0); + *m0 = NULL; /* We've eaten the packet. */ + } + PF_RULES_RUNLOCK(); return (action); diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -522,6 +522,8 @@ pf_qid_unref(rule->qid); #endif + if (rule->bridge_to) + pfi_kkif_unref(rule->bridge_to); if (rule->kif) pfi_kkif_unref(rule->kif); @@ -2809,7 +2811,7 @@ void *nvlpacked = NULL; struct pf_keth_rule *rule = NULL, *tail = NULL; struct pf_keth_ruleset *ruleset = NULL; - struct pfi_kkif *kif = NULL; + struct pfi_kkif *kif = NULL, *bridge_to_kif = NULL; const char *anchor = "", *anchor_call = ""; #define ERROUT(x) ERROUT_IOCTL(DIOCADDETHRULE_error, x) @@ -2861,6 +2863,8 @@ if (rule->ifname[0]) kif = pf_kkif_create(M_WAITOK); + if (rule->bridge_to_name[0]) + bridge_to_kif = pf_kkif_create(M_WAITOK); rule->evaluations = counter_u64_alloc(M_WAITOK); for (int i = 0; i < 2; i++) { rule->packets[i] = counter_u64_alloc(M_WAITOK); @@ -2876,6 +2880,12 @@ pfi_kkif_ref(rule->kif); } else rule->kif = NULL; + if (rule->bridge_to_name[0]) { + rule->bridge_to = pfi_kkif_attach(bridge_to_kif, + rule->bridge_to_name); + pfi_kkif_ref(rule->bridge_to); + } else + rule->bridge_to = NULL; #ifdef ALTQ /* set queue IDs */ diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c --- a/sys/netpfil/pf/pf_nv.c +++ b/sys/netpfil/pf/pf_nv.c @@ -1114,6 +1114,7 @@ nvlist_add_number(nvl, "anchor_relative", krule->anchor_relative); nvlist_add_number(nvl, "anchor_wildcard", krule->anchor_wildcard); + nvlist_add_string(nvl, "bridge_to", krule->bridge_to_name); nvlist_add_number(nvl, "action", krule->action); return (nvl); @@ -1182,6 +1183,8 @@ PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &krule->dnpipe, 0)); PFNV_CHK(pf_nvuint32_opt(nvl, "dnflags", &krule->dnflags, 0)); + PFNV_CHK(pf_nvstring(nvl, "bridge_to", krule->bridge_to_name, + sizeof(krule->bridge_to_name))); PFNV_CHK(pf_nvuint8(nvl, "action", &krule->action));