pf: support basic L3 filtering in the Ethernet rules
Allow filtering based on the source or destination IP/IPv6 address in the Ethernet layer rules. Reviewed by: pauamma_gundo.com (man), debdrup (man) Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D34482
This commit is contained in:
parent
2814ba8ef1
commit
8a42005d1e
@ -598,6 +598,11 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
|
||||
pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "dst"),
|
||||
&rule->dst);
|
||||
|
||||
pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipsrc"),
|
||||
&rule->ipsrc);
|
||||
pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipdst"),
|
||||
&rule->ipdst);
|
||||
|
||||
rule->evaluations = nvlist_get_number(nvl, "evaluations");
|
||||
rule->packets[0] = nvlist_get_number(nvl, "packets-in");
|
||||
rule->packets[1] = nvlist_get_number(nvl, "packets-out");
|
||||
@ -659,7 +664,7 @@ pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
|
||||
const char *path, struct pfctl_eth_rule *rule, bool clear,
|
||||
char *anchor_call)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
uint8_t buf[2048];
|
||||
struct pfioc_nv nv;
|
||||
nvlist_t *nvl;
|
||||
void *data;
|
||||
@ -738,6 +743,9 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
|
||||
nvlist_add_nvlist(nvl, "dst", addr);
|
||||
nvlist_destroy(addr);
|
||||
|
||||
pfctl_nv_add_rule_addr(nvl, "ipsrc", &r->ipsrc);
|
||||
pfctl_nv_add_rule_addr(nvl, "ipdst", &r->ipdst);
|
||||
|
||||
nvlist_add_string(nvl, "qname", r->qname);
|
||||
nvlist_add_string(nvl, "tagname", r->tagname);
|
||||
nvlist_add_number(nvl, "dnpipe", r->dnpipe);
|
||||
|
@ -89,6 +89,7 @@ struct pfctl_eth_rule {
|
||||
uint8_t direction;
|
||||
uint16_t proto;
|
||||
struct pfctl_eth_addr src, dst;
|
||||
struct pf_rule_addr ipsrc, ipdst;
|
||||
|
||||
/* Stats */
|
||||
uint64_t evaluations;
|
||||
|
@ -350,7 +350,8 @@ void expand_label_nr(const char *, char *, size_t,
|
||||
struct pfctl_rule *);
|
||||
void expand_eth_rule(struct pfctl_eth_rule *,
|
||||
struct node_if *, struct node_etherproto *,
|
||||
struct node_mac *, struct node_mac *, const char *);
|
||||
struct node_mac *, struct node_mac *,
|
||||
struct node_host *, struct node_host *, 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 *,
|
||||
@ -492,7 +493,7 @@ int parseport(char *, struct range *r, int);
|
||||
%token REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
|
||||
%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY FAILPOLICY
|
||||
%token RANDOMID REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
|
||||
%token ANTISPOOF FOR INCLUDE KEEPCOUNTERS SYNCOOKIES
|
||||
%token ANTISPOOF FOR INCLUDE KEEPCOUNTERS SYNCOOKIES L3
|
||||
%token ETHER
|
||||
%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET
|
||||
%token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME
|
||||
@ -523,7 +524,7 @@ int parseport(char *, struct range *r, int);
|
||||
%type <v.icmp> icmp_list icmp_item
|
||||
%type <v.icmp> icmp6_list icmp6_item
|
||||
%type <v.number> reticmpspec reticmp6spec
|
||||
%type <v.fromto> fromto
|
||||
%type <v.fromto> fromto l3fromto
|
||||
%type <v.peer> ipportspec from to
|
||||
%type <v.host> ipspec toipspec xhost host dynaddr host_list
|
||||
%type <v.host> redir_host_list redirspec
|
||||
@ -1182,7 +1183,7 @@ scrubaction : no SCRUB {
|
||||
}
|
||||
;
|
||||
|
||||
etherrule : ETHER action dir quick interface etherproto etherfromto etherfilter_opts
|
||||
etherrule : ETHER action dir quick interface etherproto etherfromto l3fromto etherfilter_opts
|
||||
{
|
||||
struct pfctl_eth_rule r;
|
||||
|
||||
@ -1194,14 +1195,15 @@ etherrule : ETHER action dir quick interface etherproto etherfromto etherfilter_
|
||||
r.action = $2.b1;
|
||||
r.direction = $3;
|
||||
r.quick = $4.quick;
|
||||
if ($8.tag != NULL)
|
||||
memcpy(&r.tagname, $8.tag, sizeof(r.tagname));
|
||||
if ($8.queues.qname != NULL)
|
||||
memcpy(&r.qname, $8.queues.qname, sizeof(r.qname));
|
||||
r.dnpipe = $8.dnpipe;
|
||||
r.dnflags = $8.free_flags;
|
||||
if ($9.tag != NULL)
|
||||
memcpy(&r.tagname, $9.tag, sizeof(r.tagname));
|
||||
if ($9.queues.qname != NULL)
|
||||
memcpy(&r.qname, $9.queues.qname, sizeof(r.qname));
|
||||
r.dnpipe = $9.dnpipe;
|
||||
r.dnflags = $9.free_flags;
|
||||
|
||||
expand_eth_rule(&r, $5, $6, $7.src, $7.dst, "");
|
||||
expand_eth_rule(&r, $5, $6, $7.src, $7.dst,
|
||||
$8.src.host, $8.dst.host, "");
|
||||
}
|
||||
;
|
||||
|
||||
@ -1236,7 +1238,7 @@ etherpfa_anchor : '{'
|
||||
| /* empty */
|
||||
;
|
||||
|
||||
etheranchorrule : ETHER ANCHOR anchorname dir quick interface etherproto etherfromto etherpfa_anchor
|
||||
etheranchorrule : ETHER ANCHOR anchorname dir quick interface etherproto etherfromto l3fromto etherpfa_anchor
|
||||
{
|
||||
struct pfctl_eth_rule r;
|
||||
|
||||
@ -1286,6 +1288,7 @@ etheranchorrule : ETHER ANCHOR anchorname dir quick interface etherproto etherfr
|
||||
r.quick = $5.quick;
|
||||
|
||||
expand_eth_rule(&r, $6, $7, $8.src, $8.dst,
|
||||
$9.src.host, $9.dst.host,
|
||||
pf->eastack[pf->asd + 1] ? pf->ealast->name : $3);
|
||||
|
||||
free($3);
|
||||
@ -3254,6 +3257,13 @@ protoval : STRING {
|
||||
}
|
||||
;
|
||||
|
||||
l3fromto : /* empty */ {
|
||||
bzero(&$$, sizeof($$));
|
||||
}
|
||||
| L3 fromto {
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
etherfromto : ALL {
|
||||
$$.src = NULL;
|
||||
$$.dst = NULL;
|
||||
@ -5733,23 +5743,45 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces,
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
pf_af_to_proto(sa_family_t af)
|
||||
{
|
||||
if (af == AF_INET)
|
||||
return (ETHERTYPE_IP);
|
||||
if (af == AF_INET6)
|
||||
return (ETHERTYPE_IPV6);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
expand_eth_rule(struct pfctl_eth_rule *r,
|
||||
struct node_if *interfaces, struct node_etherproto *protos,
|
||||
struct node_mac *srcs, struct node_mac *dsts, const char *anchor_call)
|
||||
struct node_mac *srcs, struct node_mac *dsts,
|
||||
struct node_host *ipsrcs, struct node_host *ipdsts, const char *anchor_call)
|
||||
{
|
||||
LOOP_THROUGH(struct node_if, interface, interfaces,
|
||||
LOOP_THROUGH(struct node_etherproto, proto, protos,
|
||||
LOOP_THROUGH(struct node_mac, src, srcs,
|
||||
LOOP_THROUGH(struct node_mac, dst, dsts,
|
||||
LOOP_THROUGH(struct node_host, ipsrc, ipsrcs,
|
||||
LOOP_THROUGH(struct node_host, ipdst, ipdsts,
|
||||
strlcpy(r->ifname, interface->ifname,
|
||||
sizeof(r->ifname));
|
||||
r->ifnot = interface->not;
|
||||
r->proto = proto->proto;
|
||||
if (!r->proto && ipsrc->af)
|
||||
r->proto = pf_af_to_proto(ipsrc->af);
|
||||
else if (!r->proto && ipdst->af)
|
||||
r->proto = pf_af_to_proto(ipdst->af);
|
||||
bcopy(src->mac, r->src.addr, ETHER_ADDR_LEN);
|
||||
bcopy(src->mask, r->src.mask, ETHER_ADDR_LEN);
|
||||
r->src.neg = src->neg;
|
||||
r->src.isset = src->isset;
|
||||
r->ipsrc.addr = ipsrc->addr;
|
||||
r->ipsrc.neg = ipsrc->not;
|
||||
r->ipdst.addr = ipdst->addr;
|
||||
r->ipdst.neg = ipdst->not;
|
||||
bcopy(dst->mac, r->dst.addr, ETHER_ADDR_LEN);
|
||||
bcopy(dst->mask, r->dst.mask, ETHER_ADDR_LEN);
|
||||
r->dst.neg = dst->neg;
|
||||
@ -5757,12 +5789,14 @@ expand_eth_rule(struct pfctl_eth_rule *r,
|
||||
r->nr = pf->eastack[pf->asd]->match++;
|
||||
|
||||
pfctl_append_eth_rule(pf, r, anchor_call);
|
||||
))));
|
||||
))))));
|
||||
|
||||
FREE_LIST(struct node_if, interfaces);
|
||||
FREE_LIST(struct node_etherproto, protos);
|
||||
FREE_LIST(struct node_mac, srcs);
|
||||
FREE_LIST(struct node_mac, dsts);
|
||||
FREE_LIST(struct node_host, ipsrcs);
|
||||
FREE_LIST(struct node_host, ipdsts);
|
||||
}
|
||||
|
||||
void
|
||||
@ -6052,6 +6086,7 @@ lookup(char *s)
|
||||
{ "interval", INTERVAL},
|
||||
{ "keep", KEEP},
|
||||
{ "keepcounters", KEEPCOUNTERS},
|
||||
{ "l3", L3},
|
||||
{ "label", LABEL},
|
||||
{ "limit", LIMIT},
|
||||
{ "linkshare", LINKSHARE},
|
||||
|
@ -782,7 +782,12 @@ print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
|
||||
printf(" to ");
|
||||
print_eth_addr(&r->dst);
|
||||
}
|
||||
|
||||
if (r->proto == ETHERTYPE_IP || r->proto == ETHERTYPE_IPV6) {
|
||||
printf(" l3");
|
||||
print_fromto(&r->ipsrc, PF_OSFP_ANY, &r->ipdst,
|
||||
r->proto == ETHERTYPE_IP ? AF_INET : AF_INET6, 0,
|
||||
0, 0);
|
||||
}
|
||||
if (r->qname[0])
|
||||
printf(" queue %s", r->qname);
|
||||
if (r->tagname[0])
|
||||
|
@ -28,7 +28,7 @@
|
||||
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
.\" POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd September 25, 2021
|
||||
.Dd March 9, 2022
|
||||
.Dt PF.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -3072,7 +3072,7 @@ option = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] |
|
||||
|
||||
ether-rule = "ether" etheraction [ ( "in" | "out" ) ]
|
||||
[ "quick" ] [ "on" ifspec ] [ etherprotospec ]
|
||||
etherhosts [ etherfilteropt-list ]
|
||||
etherhosts [ "l3" hosts ] [ etherfilteropt-list ]
|
||||
|
||||
pf-rule = action [ ( "in" | "out" ) ]
|
||||
[ "log" [ "(" logopts ")"] ] [ "quick" ]
|
||||
|
@ -646,6 +646,7 @@ struct pf_keth_rule {
|
||||
uint8_t direction;
|
||||
uint16_t proto;
|
||||
struct pf_keth_rule_addr src, dst;
|
||||
struct pf_rule_addr ipsrc, ipdst;
|
||||
|
||||
/* Stats */
|
||||
counter_u64_t evaluations;
|
||||
|
@ -279,7 +279,7 @@ static u_int32_t pf_tcp_iss(struct pf_pdesc *);
|
||||
void pf_rule_to_actions(struct pf_krule *,
|
||||
struct pf_rule_actions *);
|
||||
static int pf_test_eth_rule(int, struct pfi_kkif *,
|
||||
struct mbuf *);
|
||||
struct mbuf **);
|
||||
static int pf_test_rule(struct pf_krule **, struct pf_kstate **,
|
||||
int, struct pfi_kkif *, struct mbuf *, int,
|
||||
struct pf_pdesc *, struct pf_krule **,
|
||||
@ -3826,31 +3826,67 @@ pf_match_eth_addr(const uint8_t *a, const struct pf_keth_rule_addr *r)
|
||||
}
|
||||
|
||||
static int
|
||||
pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
|
||||
pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
||||
{
|
||||
struct mbuf *m = *m0;
|
||||
struct ether_header *e;
|
||||
struct pf_keth_rule *r, *rm, *a = NULL;
|
||||
struct pf_keth_ruleset *ruleset = NULL;
|
||||
struct pf_mtag *mtag;
|
||||
struct pf_keth_ruleq *rules;
|
||||
struct pf_addr *src, *dst;
|
||||
sa_family_t af = 0;
|
||||
uint16_t proto;
|
||||
int asd = 0, match = 0;
|
||||
uint8_t action;
|
||||
struct pf_keth_anchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE];
|
||||
|
||||
NET_EPOCH_ASSERT();
|
||||
|
||||
MPASS(kif->pfik_ifp->if_vnet == curvnet);
|
||||
NET_EPOCH_ASSERT();
|
||||
|
||||
SDT_PROBE3(pf, eth, test_rule, entry, dir, kif->pfik_ifp, m);
|
||||
|
||||
e = mtod(m, struct ether_header *);
|
||||
|
||||
ruleset = V_pf_keth;
|
||||
rules = ck_pr_load_ptr(&ruleset->active.rules);
|
||||
r = TAILQ_FIRST(rules);
|
||||
rm = NULL;
|
||||
|
||||
e = mtod(m, struct ether_header *);
|
||||
proto = ntohs(e->ether_type);
|
||||
|
||||
switch (proto) {
|
||||
case ETHERTYPE_IP: {
|
||||
struct ip *ip;
|
||||
m = m_pullup(m, sizeof(struct ether_header) +
|
||||
sizeof(struct ip));
|
||||
if (m == NULL) {
|
||||
*m0 = NULL;
|
||||
return (PF_DROP);
|
||||
}
|
||||
af = AF_INET;
|
||||
ip = mtodo(m, sizeof(struct ether_header));
|
||||
src = (struct pf_addr *)&ip->ip_src;
|
||||
dst = (struct pf_addr *)&ip->ip_dst;
|
||||
break;
|
||||
}
|
||||
case ETHERTYPE_IPV6: {
|
||||
struct ip6_hdr *ip6;
|
||||
m = m_pullup(m, sizeof(struct ether_header) +
|
||||
sizeof(struct ip6_hdr));
|
||||
if (m == NULL) {
|
||||
*m0 = NULL;
|
||||
return (PF_DROP);
|
||||
}
|
||||
af = AF_INET6;
|
||||
ip6 = mtodo(m, sizeof(struct ether_header));
|
||||
src = (struct pf_addr *)&ip6->ip6_src;
|
||||
dst = (struct pf_addr *)&ip6->ip6_dst;
|
||||
break;
|
||||
}
|
||||
}
|
||||
e = mtod(m, struct ether_header *);
|
||||
*m0 = m;
|
||||
|
||||
while (r != NULL) {
|
||||
counter_u64_add(r->evaluations, 1);
|
||||
SDT_PROBE2(pf, eth, test_rule, test, r->nr, r);
|
||||
@ -3865,7 +3901,7 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
|
||||
"dir");
|
||||
r = r->skip[PFE_SKIP_DIR].ptr;
|
||||
}
|
||||
else if (r->proto && r->proto != ntohs(e->ether_type)) {
|
||||
else if (r->proto && r->proto != proto) {
|
||||
SDT_PROBE3(pf, eth, test_rule, mismatch, r->nr, r,
|
||||
"proto");
|
||||
r = r->skip[PFE_SKIP_PROTO].ptr;
|
||||
@ -3880,6 +3916,18 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
|
||||
"dst");
|
||||
r = TAILQ_NEXT(r, entries);
|
||||
}
|
||||
else if (af != 0 && PF_MISMATCHAW(&r->ipsrc.addr, src, af,
|
||||
r->ipsrc.neg, kif, M_GETFIB(m))) {
|
||||
SDT_PROBE3(pf, eth, test_rule, mismatch, r->nr, r,
|
||||
"ip_src");
|
||||
r = TAILQ_NEXT(r, entries);
|
||||
}
|
||||
else if (af != 0 && PF_MISMATCHAW(&r->ipdst.addr, dst, af,
|
||||
r->ipdst.neg, kif, M_GETFIB(m))) {
|
||||
SDT_PROBE3(pf, eth, test_rule, mismatch, r->nr, r,
|
||||
"ip_dst");
|
||||
r = TAILQ_NEXT(r, entries);
|
||||
}
|
||||
else {
|
||||
if (r->anchor == NULL) {
|
||||
/* Rule matches */
|
||||
@ -6737,7 +6785,7 @@ pf_test_eth(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0,
|
||||
return (PF_PASS);
|
||||
|
||||
/* Stateless! */
|
||||
return (pf_test_eth_rule(dir, kif, m));
|
||||
return (pf_test_eth_rule(dir, kif, m0));
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
|
@ -1071,6 +1071,22 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
|
||||
}
|
||||
nvlist_add_nvlist(nvl, "dst", addr);
|
||||
|
||||
addr = pf_rule_addr_to_nvrule_addr(&krule->ipsrc);
|
||||
if (addr == NULL) {
|
||||
nvlist_destroy(nvl);
|
||||
return (NULL);
|
||||
}
|
||||
nvlist_add_nvlist(nvl, "ipsrc", addr);
|
||||
nvlist_destroy(addr);
|
||||
|
||||
addr = pf_rule_addr_to_nvrule_addr(&krule->ipdst);
|
||||
if (addr == NULL) {
|
||||
nvlist_destroy(nvl);
|
||||
return (NULL);
|
||||
}
|
||||
nvlist_add_nvlist(nvl, "ipdst", addr);
|
||||
nvlist_destroy(addr);
|
||||
|
||||
nvlist_add_number(nvl, "evaluations",
|
||||
counter_u64_fetch(krule->evaluations));
|
||||
nvlist_add_number(nvl, "packets-in",
|
||||
@ -1125,6 +1141,20 @@ pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (nvlist_exists_nvlist(nvl, "ipsrc")) {
|
||||
error = pf_nvrule_addr_to_rule_addr(
|
||||
nvlist_get_nvlist(nvl, "ipsrc"), &krule->ipsrc);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (nvlist_exists_nvlist(nvl, "ipdst")) {
|
||||
error = pf_nvrule_addr_to_rule_addr(
|
||||
nvlist_get_nvlist(nvl, "ipdst"), &krule->ipdst);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
|
||||
PFNV_CHK(pf_nvstring(nvl, "qname", krule->qname, sizeof(krule->qname)));
|
||||
PFNV_CHK(pf_nvstring(nvl, "tagname", krule->tagname,
|
||||
sizeof(krule->tagname)));
|
||||
|
Loading…
Reference in New Issue
Block a user