mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 12:54:27 +00:00
pf: support matching on tags for Ethernet rules
Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D35362
This commit is contained in:
parent
e417249016
commit
1f61367f8d
|
@ -629,6 +629,10 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
|
|||
rule->ifnot = nvlist_get_bool(nvl, "ifnot");
|
||||
rule->direction = nvlist_get_number(nvl, "direction");
|
||||
rule->proto = nvlist_get_number(nvl, "proto");
|
||||
strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"),
|
||||
PF_TAG_NAME_SIZE);
|
||||
rule->match_tag = nvlist_get_number(nvl, "match_tag");
|
||||
rule->match_tag_not = nvlist_get_bool(nvl, "match_tag_not");
|
||||
|
||||
pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "src"),
|
||||
&rule->src);
|
||||
|
@ -780,6 +784,8 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
|
|||
nvlist_add_bool(nvl, "ifnot", r->ifnot);
|
||||
nvlist_add_number(nvl, "direction", r->direction);
|
||||
nvlist_add_number(nvl, "proto", r->proto);
|
||||
nvlist_add_string(nvl, "match_tagname", r->match_tagname);
|
||||
nvlist_add_bool(nvl, "match_tag_not", r->match_tag_not);
|
||||
|
||||
addr = pfctl_eth_addr_to_nveth_addr(&r->src);
|
||||
if (addr == NULL) {
|
||||
|
|
|
@ -94,6 +94,9 @@ struct pfctl_eth_rule {
|
|||
uint16_t proto;
|
||||
struct pfctl_eth_addr src, dst;
|
||||
struct pf_rule_addr ipsrc, ipdst;
|
||||
char match_tagname[PF_TAG_NAME_SIZE];
|
||||
uint16_t match_tag;
|
||||
bool match_tag_not;
|
||||
|
||||
/* Stats */
|
||||
uint64_t evaluations;
|
||||
|
|
|
@ -1197,6 +1197,14 @@ etherrule : ETHER action dir quick interface etherproto etherfromto l3fromto eth
|
|||
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,
|
||||
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;
|
||||
|
@ -1320,6 +1328,10 @@ etherfilter_opt : etherqname {
|
|||
| TAG string {
|
||||
filter_opts.tag = $2;
|
||||
}
|
||||
| not TAGGED string {
|
||||
filter_opts.match_tag = $3;
|
||||
filter_opts.match_tag_not = $1;
|
||||
}
|
||||
| DNPIPE number {
|
||||
filter_opts.dnpipe = $2;
|
||||
filter_opts.free_flags |= PFRULE_DN_IS_PIPE;
|
||||
|
@ -5772,6 +5784,18 @@ expand_eth_rule(struct pfctl_eth_rule *r,
|
|||
struct node_mac *srcs, struct node_mac *dsts,
|
||||
struct node_host *ipsrcs, struct node_host *ipdsts, const char *anchor_call)
|
||||
{
|
||||
char tagname[PF_TAG_NAME_SIZE];
|
||||
char match_tagname[PF_TAG_NAME_SIZE];
|
||||
char qname[PF_QNAME_SIZE];
|
||||
|
||||
if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname))
|
||||
errx(1, "expand_eth_rule: tagname");
|
||||
if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >=
|
||||
sizeof(match_tagname))
|
||||
errx(1, "expand_eth_rule: match_tagname");
|
||||
if (strlcpy(qname, r->qname, sizeof(qname)) >= sizeof(qname))
|
||||
errx(1, "expand_eth_rule: qname");
|
||||
|
||||
LOOP_THROUGH(struct node_if, interface, interfaces,
|
||||
LOOP_THROUGH(struct node_etherproto, proto, protos,
|
||||
LOOP_THROUGH(struct node_mac, src, srcs,
|
||||
|
@ -5800,6 +5824,15 @@ expand_eth_rule(struct pfctl_eth_rule *r,
|
|||
r->dst.isset = dst->isset;
|
||||
r->nr = pf->eastack[pf->asd]->match++;
|
||||
|
||||
if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >=
|
||||
sizeof(r->tagname))
|
||||
errx(1, "expand_eth_rule: r->tagname");
|
||||
if (strlcpy(r->match_tagname, match_tagname,
|
||||
sizeof(r->match_tagname)) >= sizeof(r->match_tagname))
|
||||
errx(1, "expand_eth_rule: r->match_tagname");
|
||||
if (strlcpy(r->qname, qname, sizeof(r->qname)) >= sizeof(r->qname))
|
||||
errx(1, "expand_eth_rule: r->qname");
|
||||
|
||||
pfctl_append_eth_rule(pf, r, anchor_call);
|
||||
))))));
|
||||
|
||||
|
|
|
@ -791,6 +791,11 @@ print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
|
|||
printf(" queue %s", r->qname);
|
||||
if (r->tagname[0])
|
||||
printf(" tag %s", r->tagname);
|
||||
if (r->match_tagname[0]) {
|
||||
if (r->match_tag_not)
|
||||
printf(" !");
|
||||
printf(" tagged %s", r->match_tagname);
|
||||
}
|
||||
if (r->dnpipe)
|
||||
printf(" %s %d",
|
||||
r->dnflags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue",
|
||||
|
|
|
@ -744,6 +744,11 @@ is not the last matching rule.
|
|||
Further matching rules can replace the tag with a
|
||||
new one but will not remove a previously applied tag.
|
||||
A packet is only ever assigned one tag at a time.
|
||||
.It Ar tagged Aq Ar string
|
||||
Used to specify that packets must already be tagged with the given tag in order
|
||||
to match the rule.
|
||||
Inverse tag matching can also be done by specifying the ! operator before the
|
||||
tagged keyword.
|
||||
.Sh TRAFFIC NORMALIZATION
|
||||
Traffic normalization is used to sanitize packet content in such
|
||||
a way that there are no ambiguities in packet interpretation on
|
||||
|
@ -3083,7 +3088,7 @@ logopts = logopt [ "," logopts ]
|
|||
logopt = "all" | "user" | "to" interface-name
|
||||
|
||||
etherfilteropt-list = etherfilteropt-list etherfilteropt | etherfilteropt
|
||||
etherfilteropt = "tag" string | "queue" ( string )
|
||||
etherfilteropt = "tag" string | "tagged" string | "queue" ( string )
|
||||
|
||||
filteropt-list = filteropt-list filteropt | filteropt
|
||||
filteropt = user | group | flags | icmp-type | icmp6-type | "tos" tos |
|
||||
|
|
|
@ -672,6 +672,10 @@ struct pf_keth_rule {
|
|||
uint16_t proto;
|
||||
struct pf_keth_rule_addr src, dst;
|
||||
struct pf_rule_addr ipsrc, ipdst;
|
||||
char match_tagname[PF_TAG_NAME_SIZE];
|
||||
uint16_t match_tag;
|
||||
bool match_tag_not;
|
||||
|
||||
|
||||
/* Stats */
|
||||
counter_u64_t evaluations;
|
||||
|
|
|
@ -3835,6 +3835,16 @@ pf_match_eth_addr(const uint8_t *a, const struct pf_keth_rule_addr *r)
|
|||
return (match ^ r->neg);
|
||||
}
|
||||
|
||||
static int
|
||||
pf_match_eth_tag(struct mbuf *m, struct pf_keth_rule *r, int *tag, int mtag)
|
||||
{
|
||||
if (*tag == -1)
|
||||
*tag = mtag;
|
||||
|
||||
return ((!r->match_tag_not && r->match_tag == *tag) ||
|
||||
(r->match_tag_not && r->match_tag != *tag));
|
||||
}
|
||||
|
||||
static int
|
||||
pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
||||
{
|
||||
|
@ -3848,6 +3858,7 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
|||
sa_family_t af = 0;
|
||||
uint16_t proto;
|
||||
int asd = 0, match = 0;
|
||||
int tag = -1;
|
||||
uint8_t action;
|
||||
struct pf_keth_anchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE];
|
||||
|
||||
|
@ -3959,7 +3970,15 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
|||
"ip_dst");
|
||||
r = TAILQ_NEXT(r, entries);
|
||||
}
|
||||
else if (r->match_tag && !pf_match_eth_tag(m, r, &tag,
|
||||
mtag ? mtag->tag : 0)) {
|
||||
SDT_PROBE3(pf, eth, test_rule, mismatch, r->nr, r,
|
||||
"match_tag");
|
||||
r = TAILQ_NEXT(r, entries);
|
||||
}
|
||||
else {
|
||||
if (r->tag)
|
||||
tag = r->tag;
|
||||
if (r->anchor == NULL) {
|
||||
/* Rule matches */
|
||||
rm = r;
|
||||
|
@ -4001,7 +4020,7 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
|||
return (PF_DROP);
|
||||
}
|
||||
|
||||
if (r->tag > 0) {
|
||||
if (tag > 0) {
|
||||
if (mtag == NULL)
|
||||
mtag = pf_get_mtag(m);
|
||||
if (mtag == NULL) {
|
||||
|
@ -4009,7 +4028,7 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
|
|||
counter_u64_add(V_pf_status.counters[PFRES_MEMORY], 1);
|
||||
return (PF_DROP);
|
||||
}
|
||||
mtag->tag = r->tag;
|
||||
mtag->tag = tag;
|
||||
}
|
||||
|
||||
if (r->qid != 0) {
|
||||
|
|
|
@ -515,6 +515,8 @@ pf_free_eth_rule(struct pf_keth_rule *rule)
|
|||
|
||||
if (rule->tag)
|
||||
tag_unref(&V_pf_tags, rule->tag);
|
||||
if (rule->match_tag)
|
||||
tag_unref(&V_pf_tags, rule->match_tag);
|
||||
#ifdef ALTQ
|
||||
pf_qid_unref(rule->qid);
|
||||
#endif
|
||||
|
@ -2891,6 +2893,10 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
|
|||
if (rule->tagname[0])
|
||||
if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0)
|
||||
error = EBUSY;
|
||||
if (rule->match_tagname[0])
|
||||
if ((rule->match_tag = pf_tagname2tag(
|
||||
rule->match_tagname)) == 0)
|
||||
error = EBUSY;
|
||||
|
||||
if (error == 0 && rule->ipdst.addr.type == PF_ADDR_TABLE)
|
||||
error = pf_eth_addr_setup(ruleset, &rule->ipdst.addr);
|
||||
|
|
|
@ -1057,6 +1057,9 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
|
|||
nvlist_add_bool(nvl, "ifnot", krule->ifnot);
|
||||
nvlist_add_number(nvl, "direction", krule->direction);
|
||||
nvlist_add_number(nvl, "proto", krule->proto);
|
||||
nvlist_add_string(nvl, "match_tagname", krule->match_tagname);
|
||||
nvlist_add_number(nvl, "match_tag", krule->match_tag);
|
||||
nvlist_add_bool(nvl, "match_tag_not", krule->match_tag_not);
|
||||
|
||||
addr = pf_keth_rule_addr_to_nveth_rule_addr(&krule->src);
|
||||
if (addr == NULL) {
|
||||
|
@ -1165,6 +1168,12 @@ pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
|
|||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (nvlist_exists_string(nvl, "match_tagname")) {
|
||||
PFNV_CHK(pf_nvstring(nvl, "match_tagname", krule->match_tagname,
|
||||
sizeof(krule->match_tagname)));
|
||||
PFNV_CHK(pf_nvbool(nvl, "match_tag_not", &krule->match_tag_not));
|
||||
}
|
||||
|
||||
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 a new issue