mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 12:54:27 +00:00
pf: bridge-to
Allow pf (l2) to be used to redirect ethernet packets to a different interface. The intended use case is to send 802.1x challenges out to a side interface, to enable AT&T links to function with pfSense as a gateway, rather than the AT&T provided hardware. Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D37193
This commit is contained in:
parent
ccd9b49f20
commit
8a8af94240
|
@ -665,6 +665,9 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
|
||||||
rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
|
rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
|
||||||
rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard");
|
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");
|
rule->action = nvlist_get_number(nvl, "action");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,6 +815,8 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
|
||||||
nvlist_add_number(nvl, "dnpipe", r->dnpipe);
|
nvlist_add_number(nvl, "dnpipe", r->dnpipe);
|
||||||
nvlist_add_number(nvl, "dnflags", r->dnflags);
|
nvlist_add_number(nvl, "dnflags", r->dnflags);
|
||||||
|
|
||||||
|
nvlist_add_string(nvl, "bridge_to", r->bridge_to);
|
||||||
|
|
||||||
nvlist_add_number(nvl, "action", r->action);
|
nvlist_add_number(nvl, "action", r->action);
|
||||||
|
|
||||||
packed = nvlist_pack(nvl, &size);
|
packed = nvlist_pack(nvl, &size);
|
||||||
|
|
|
@ -110,6 +110,7 @@ struct pfctl_eth_rule {
|
||||||
char tagname[PF_TAG_NAME_SIZE];
|
char tagname[PF_TAG_NAME_SIZE];
|
||||||
uint16_t dnpipe;
|
uint16_t dnpipe;
|
||||||
uint32_t dnflags;
|
uint32_t dnflags;
|
||||||
|
char bridge_to[IFNAMSIZ];
|
||||||
uint8_t action;
|
uint8_t action;
|
||||||
|
|
||||||
struct pfctl_eth_anchor *anchor;
|
struct pfctl_eth_anchor *anchor;
|
||||||
|
|
|
@ -351,7 +351,8 @@ void expand_label_nr(const char *, char *, size_t,
|
||||||
void expand_eth_rule(struct pfctl_eth_rule *,
|
void expand_eth_rule(struct pfctl_eth_rule *,
|
||||||
struct node_if *, struct node_etherproto *,
|
struct node_if *, struct node_etherproto *,
|
||||||
struct node_mac *, struct node_mac *,
|
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 *,
|
void expand_rule(struct pfctl_rule *, struct node_if *,
|
||||||
struct node_host *, struct node_proto *, struct node_os *,
|
struct node_host *, struct node_proto *, struct node_os *,
|
||||||
struct node_host *, struct node_port *, struct node_host *,
|
struct node_host *, struct node_port *, struct node_host *,
|
||||||
|
@ -432,6 +433,7 @@ typedef struct {
|
||||||
struct {
|
struct {
|
||||||
struct node_mac *mac;
|
struct node_mac *mac;
|
||||||
} etheraddr;
|
} etheraddr;
|
||||||
|
char *bridge_to;
|
||||||
struct {
|
struct {
|
||||||
struct node_host *host;
|
struct node_host *host;
|
||||||
u_int8_t rt;
|
u_int8_t rt;
|
||||||
|
@ -503,7 +505,7 @@ int parseport(char *, struct range *r, int);
|
||||||
%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
|
%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
|
||||||
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
|
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
|
||||||
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
|
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
|
||||||
%token DIVERTTO DIVERTREPLY
|
%token DIVERTTO DIVERTREPLY BRIDGE_TO
|
||||||
%token <v.string> STRING
|
%token <v.string> STRING
|
||||||
%token <v.number> NUMBER
|
%token <v.number> NUMBER
|
||||||
%token <v.i> PORTBINARY
|
%token <v.i> PORTBINARY
|
||||||
|
@ -563,6 +565,7 @@ int parseport(char *, struct range *r, int);
|
||||||
%type <v.etherproto> etherproto etherproto_list etherproto_item
|
%type <v.etherproto> etherproto etherproto_list etherproto_item
|
||||||
%type <v.etherfromto> etherfromto
|
%type <v.etherfromto> etherfromto
|
||||||
%type <v.etheraddr> etherfrom etherto
|
%type <v.etheraddr> etherfrom etherto
|
||||||
|
%type <v.bridge_to> bridge
|
||||||
%type <v.mac> xmac mac mac_list macspec
|
%type <v.mac> xmac mac mac_list macspec
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
@ -1195,7 +1198,7 @@ scrubaction : no SCRUB {
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
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;
|
struct pfctl_eth_rule r;
|
||||||
|
|
||||||
|
@ -1207,23 +1210,23 @@ etherrule : ETHER action dir quick interface etherproto etherfromto l3fromto eth
|
||||||
r.action = $2.b1;
|
r.action = $2.b1;
|
||||||
r.direction = $3;
|
r.direction = $3;
|
||||||
r.quick = $4.quick;
|
r.quick = $4.quick;
|
||||||
if ($9.tag != NULL)
|
if ($10.tag != NULL)
|
||||||
memcpy(&r.tagname, $9.tag, sizeof(r.tagname));
|
memcpy(&r.tagname, $10.tag, sizeof(r.tagname));
|
||||||
if ($9.match_tag)
|
if ($10.match_tag)
|
||||||
if (strlcpy(r.match_tagname, $9.match_tag,
|
if (strlcpy(r.match_tagname, $10.match_tag,
|
||||||
PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
|
PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
|
||||||
yyerror("tag too long, max %u chars",
|
yyerror("tag too long, max %u chars",
|
||||||
PF_TAG_NAME_SIZE - 1);
|
PF_TAG_NAME_SIZE - 1);
|
||||||
YYERROR;
|
YYERROR;
|
||||||
}
|
}
|
||||||
r.match_tag_not = $9.match_tag_not;
|
r.match_tag_not = $10.match_tag_not;
|
||||||
if ($9.queues.qname != NULL)
|
if ($10.queues.qname != NULL)
|
||||||
memcpy(&r.qname, $9.queues.qname, sizeof(r.qname));
|
memcpy(&r.qname, $10.queues.qname, sizeof(r.qname));
|
||||||
r.dnpipe = $9.dnpipe;
|
r.dnpipe = $10.dnpipe;
|
||||||
r.dnflags = $9.free_flags;
|
r.dnflags = $10.free_flags;
|
||||||
|
|
||||||
expand_eth_rule(&r, $5, $6, $7.src, $7.dst,
|
expand_eth_rule(&r, $5, $7, $8.src, $8.dst,
|
||||||
$8.src.host, $8.dst.host, "");
|
$9.src.host, $9.dst.host, $6, "");
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1315,7 +1318,7 @@ etheranchorrule : ETHER ANCHOR anchorname dir quick interface etherproto etherfr
|
||||||
r.quick = $5.quick;
|
r.quick = $5.quick;
|
||||||
|
|
||||||
expand_eth_rule(&r, $6, $7, $8.src, $8.dst,
|
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);
|
pf->eastack[pf->asd + 1] ? pf->ealast->name : $3);
|
||||||
|
|
||||||
free($3);
|
free($3);
|
||||||
|
@ -1361,6 +1364,14 @@ etherfilter_opt : etherqname {
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
bridge : /* empty */ {
|
||||||
|
$$ = NULL;
|
||||||
|
}
|
||||||
|
| BRIDGE_TO STRING {
|
||||||
|
$$ = strdup($2);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts
|
scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts
|
||||||
{
|
{
|
||||||
struct pfctl_rule r;
|
struct pfctl_rule r;
|
||||||
|
@ -5801,7 +5812,8 @@ void
|
||||||
expand_eth_rule(struct pfctl_eth_rule *r,
|
expand_eth_rule(struct pfctl_eth_rule *r,
|
||||||
struct node_if *interfaces, struct node_etherproto *protos,
|
struct node_if *interfaces, struct node_etherproto *protos,
|
||||||
struct node_mac *srcs, struct node_mac *dsts,
|
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 tagname[PF_TAG_NAME_SIZE];
|
||||||
char match_tagname[PF_TAG_NAME_SIZE];
|
char match_tagname[PF_TAG_NAME_SIZE];
|
||||||
|
@ -5852,6 +5864,9 @@ expand_eth_rule(struct pfctl_eth_rule *r,
|
||||||
if (strlcpy(r->qname, qname, sizeof(r->qname)) >= sizeof(r->qname))
|
if (strlcpy(r->qname, qname, sizeof(r->qname)) >= sizeof(r->qname))
|
||||||
errx(1, "expand_eth_rule: 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);
|
pfctl_append_eth_rule(pf, r, anchor_call);
|
||||||
))))));
|
))))));
|
||||||
|
|
||||||
|
@ -6110,6 +6125,7 @@ lookup(char *s)
|
||||||
{ "bitmask", BITMASK},
|
{ "bitmask", BITMASK},
|
||||||
{ "block", BLOCK},
|
{ "block", BLOCK},
|
||||||
{ "block-policy", BLOCKPOLICY},
|
{ "block-policy", BLOCKPOLICY},
|
||||||
|
{ "bridge-to", BRIDGE_TO},
|
||||||
{ "buckets", BUCKETS},
|
{ "buckets", BUCKETS},
|
||||||
{ "cbq", CBQ},
|
{ "cbq", CBQ},
|
||||||
{ "code", CODE},
|
{ "code", CODE},
|
||||||
|
|
|
@ -774,6 +774,8 @@ print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
|
||||||
else
|
else
|
||||||
printf(" on %s", r->ifname);
|
printf(" on %s", r->ifname);
|
||||||
}
|
}
|
||||||
|
if (r->bridge_to[0])
|
||||||
|
printf(" bridge-to %s", r->bridge_to);
|
||||||
if (r->proto)
|
if (r->proto)
|
||||||
printf(" proto 0x%04x", r->proto);
|
printf(" proto 0x%04x", r->proto);
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
.\" POSSIBILITY OF SUCH DAMAGE.
|
.\" POSSIBILITY OF SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.Dd March 9, 2022
|
.Dd October 28, 2022
|
||||||
.Dt PF.CONF 5
|
.Dt PF.CONF 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -710,6 +710,9 @@ see the
|
||||||
.Ic group
|
.Ic group
|
||||||
keyword in
|
keyword in
|
||||||
.Xr ifconfig 8 .
|
.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
|
.It Ar proto Aq Ar protocol
|
||||||
This rule applies only to packets of this protocol.
|
This rule applies only to packets of this protocol.
|
||||||
Note that Ethernet protocol numbers are different from those used in
|
Note that Ethernet protocol numbers are different from those used in
|
||||||
|
@ -3076,8 +3079,9 @@ option = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] |
|
||||||
[ "keepcounters" ] )
|
[ "keepcounters" ] )
|
||||||
|
|
||||||
ether-rule = "ether" etheraction [ ( "in" | "out" ) ]
|
ether-rule = "ether" etheraction [ ( "in" | "out" ) ]
|
||||||
[ "quick" ] [ "on" ifspec ] [ etherprotospec ]
|
[ "quick" ] [ "on" ifspec ] [ "bridge-to" interface-name ]
|
||||||
etherhosts [ "l3" hosts ] [ etherfilteropt-list ]
|
[ etherprotospec ] etherhosts [ "l3" hosts ]
|
||||||
|
[ etherfilteropt-list ]
|
||||||
|
|
||||||
pf-rule = action [ ( "in" | "out" ) ]
|
pf-rule = action [ ( "in" | "out" ) ]
|
||||||
[ "log" [ "(" logopts ")"] ] [ "quick" ]
|
[ "log" [ "(" logopts ")"] ] [ "quick" ]
|
||||||
|
|
|
@ -690,6 +690,8 @@ struct pf_keth_rule {
|
||||||
int qid;
|
int qid;
|
||||||
char tagname[PF_TAG_NAME_SIZE];
|
char tagname[PF_TAG_NAME_SIZE];
|
||||||
uint16_t tag;
|
uint16_t tag;
|
||||||
|
char bridge_to_name[IFNAMSIZ];
|
||||||
|
struct pfi_kkif *bridge_to;
|
||||||
uint8_t action;
|
uint8_t action;
|
||||||
uint16_t dnpipe;
|
uint16_t dnpipe;
|
||||||
uint32_t dnflags;
|
uint32_t dnflags;
|
||||||
|
|
|
@ -3833,6 +3833,32 @@ pf_match_eth_tag(struct mbuf *m, struct pf_keth_rule *r, int *tag, int mtag)
|
||||||
(r->match_tag_not && r->match_tag != *tag));
|
(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
|
static int
|
||||||
pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
||||||
{
|
{
|
||||||
|
@ -4092,6 +4118,11 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
||||||
|
|
||||||
action = r->action;
|
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();
|
PF_RULES_RUNLOCK();
|
||||||
|
|
||||||
return (action);
|
return (action);
|
||||||
|
|
|
@ -522,6 +522,8 @@ pf_free_eth_rule(struct pf_keth_rule *rule)
|
||||||
pf_qid_unref(rule->qid);
|
pf_qid_unref(rule->qid);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (rule->bridge_to)
|
||||||
|
pfi_kkif_unref(rule->bridge_to);
|
||||||
if (rule->kif)
|
if (rule->kif)
|
||||||
pfi_kkif_unref(rule->kif);
|
pfi_kkif_unref(rule->kif);
|
||||||
|
|
||||||
|
@ -2809,7 +2811,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
|
||||||
void *nvlpacked = NULL;
|
void *nvlpacked = NULL;
|
||||||
struct pf_keth_rule *rule = NULL, *tail = NULL;
|
struct pf_keth_rule *rule = NULL, *tail = NULL;
|
||||||
struct pf_keth_ruleset *ruleset = 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 = "";
|
const char *anchor = "", *anchor_call = "";
|
||||||
|
|
||||||
#define ERROUT(x) ERROUT_IOCTL(DIOCADDETHRULE_error, x)
|
#define ERROUT(x) ERROUT_IOCTL(DIOCADDETHRULE_error, x)
|
||||||
|
@ -2861,6 +2863,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
|
||||||
|
|
||||||
if (rule->ifname[0])
|
if (rule->ifname[0])
|
||||||
kif = pf_kkif_create(M_WAITOK);
|
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);
|
rule->evaluations = counter_u64_alloc(M_WAITOK);
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
rule->packets[i] = counter_u64_alloc(M_WAITOK);
|
rule->packets[i] = counter_u64_alloc(M_WAITOK);
|
||||||
|
@ -2876,6 +2880,12 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
|
||||||
pfi_kkif_ref(rule->kif);
|
pfi_kkif_ref(rule->kif);
|
||||||
} else
|
} else
|
||||||
rule->kif = NULL;
|
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
|
#ifdef ALTQ
|
||||||
/* set queue IDs */
|
/* set queue IDs */
|
||||||
|
|
|
@ -1114,6 +1114,7 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
|
||||||
nvlist_add_number(nvl, "anchor_relative", krule->anchor_relative);
|
nvlist_add_number(nvl, "anchor_relative", krule->anchor_relative);
|
||||||
nvlist_add_number(nvl, "anchor_wildcard", krule->anchor_wildcard);
|
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);
|
nvlist_add_number(nvl, "action", krule->action);
|
||||||
|
|
||||||
return (nvl);
|
return (nvl);
|
||||||
|
@ -1182,6 +1183,8 @@ pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
|
||||||
|
|
||||||
PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &krule->dnpipe, 0));
|
PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &krule->dnpipe, 0));
|
||||||
PFNV_CHK(pf_nvuint32_opt(nvl, "dnflags", &krule->dnflags, 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));
|
PFNV_CHK(pf_nvuint8(nvl, "action", &krule->action));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue