pf: support dummynet on L2 rules

Allow packets to be tagged with dummynet information. Note that we do
not apply dummynet shaping on the L2 traffic, but instead mark it for
dummynet processing in the L3 code. This is the same approach as we take
for ALTQ.

Sponsored by:   Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D32222
This commit is contained in:
Kristof Provost 2021-09-27 14:50:30 +02:00
parent ab2886f088
commit fb330f3931
8 changed files with 66 additions and 3 deletions

View File

@ -603,6 +603,9 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"),
PF_TAG_NAME_SIZE);
rule->dnpipe = nvlist_get_number(nvl, "dnpipe");
rule->dnflags = nvlist_get_number(nvl, "dnflags");
rule->action = nvlist_get_number(nvl, "action");
}
@ -709,6 +712,9 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, uint32_t ticket)
nvlist_add_string(nvl, "qname", r->qname);
nvlist_add_string(nvl, "tagname", r->tagname);
nvlist_add_number(nvl, "dnpipe", r->dnpipe);
nvlist_add_number(nvl, "dnflags", r->dnflags);
nvlist_add_number(nvl, "action", r->action);
packed = nvlist_pack(nvl, &size);

View File

@ -96,6 +96,8 @@ struct pfctl_eth_rule {
/* Action */
char qname[PF_QNAME_SIZE];
char tagname[PF_TAG_NAME_SIZE];
uint16_t dnpipe;
uint32_t dnflags;
uint8_t action;
TAILQ_ENTRY(pfctl_eth_rule) entries;

View File

@ -1200,6 +1200,8 @@ etherrule : ETHER action dir quick interface etherproto etherfromto etherfilter_
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;
expand_eth_rule(&r, $5, $6);
}
@ -1229,6 +1231,14 @@ etherfilter_opt : etherqname {
| TAG string {
filter_opts.tag = $2;
}
| DNPIPE number {
filter_opts.dnpipe = $2;
filter_opts.free_flags |= PFRULE_DN_IS_PIPE;
}
| DNQUEUE number {
filter_opts.dnpipe = $2;
filter_opts.free_flags |= PFRULE_DN_IS_QUEUE;
}
;
scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts

View File

@ -747,6 +747,10 @@ print_eth_rule(struct pfctl_eth_rule *r, int rule_numbers)
printf(" queue %s", r->qname);
if (r->tagname[0])
printf(" tag %s", r->tagname);
if (r->dnpipe)
printf(" %s %d",
r->dnflags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue",
r->dnpipe);
}
void

View File

@ -617,6 +617,8 @@ struct pf_keth_rule {
char tagname[PF_TAG_NAME_SIZE];
uint16_t tag;
uint8_t action;
uint16_t dnpipe;
uint32_t dnflags;
};
TAILQ_HEAD(pf_keth_rules, pf_keth_rule);

View File

@ -3813,6 +3813,22 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
mtag->qid = r->qid;
}
/* Dummynet */
if (r->dnpipe) {
/** While dummynet supports handling Ethernet packets directly
* it still wants some L3/L4 information, and we're not set up
* to provide that here. Instead we'll do what we do for ALTQ
* and merely mark the packet with the dummynet queue/pipe number.
**/
mtag = pf_get_mtag(m);
if (mtag == NULL) {
counter_u64_add(V_pf_status.counters[PFRES_MEMORY], 1);
return (PF_DROP);
}
mtag->dnpipe = r->dnpipe;
mtag->dnflags = r->dnflags;
}
action = r->action;
return (action);
@ -6515,8 +6531,13 @@ pf_pdesc_to_dnflow(int dir, const struct pf_pdesc *pd,
{
int dndir = r->direction;
if (s && dndir == PF_INOUT)
if (s && dndir == PF_INOUT) {
dndir = s->direction;
} else if (dndir == PF_INOUT) {
/* Assume primary direction. Happens when we've set dnpipe in
* the ethernet level code. */
dndir = dir;
}
memset(dnflow, 0, sizeof(*dnflow));
@ -6541,7 +6562,7 @@ pf_pdesc_to_dnflow(int dir, const struct pf_pdesc *pd,
}
dnflow->rule.info |= IPFW_IS_DUMMYNET;
if (r->free_flags & PFRULE_DN_IS_PIPE)
if (r->free_flags & PFRULE_DN_IS_PIPE || pd->act.flags & PFRULE_DN_IS_PIPE)
dnflow->rule.info |= IPFW_IS_PIPE;
dnflow->f_id.proto = pd->proto;
@ -6635,6 +6656,11 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb *
memset(&pd, 0, sizeof(pd));
pd.pf_mtag = pf_find_mtag(m);
if (pd.pf_mtag && pd.pf_mtag->dnpipe) {
pd.act.dnpipe = pd.pf_mtag->dnpipe;
pd.act.flags = pd.pf_mtag->dnflags;
}
if (ip_dn_io_ptr != NULL && pd.pf_mtag != NULL &&
pd.pf_mtag->flags & PF_TAG_DUMMYNET) {
/* Dummynet re-injects packets after they've
@ -7134,6 +7160,11 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
memset(&pd, 0, sizeof(pd));
pd.pf_mtag = pf_find_mtag(m);
if (pd.pf_mtag && pd.pf_mtag->dnpipe) {
pd.act.dnpipe = pd.pf_mtag->dnpipe;
pd.act.flags = pd.pf_mtag->dnflags;
}
if (ip_dn_io_ptr != NULL && pd.pf_mtag != NULL &&
pd.pf_mtag->flags & PF_TAG_DUMMYNET) {
pd.pf_mtag->flags &= ~PF_TAG_DUMMYNET;

View File

@ -52,6 +52,8 @@ struct pf_mtag {
u_int16_t tag; /* tag id */
u_int8_t flags;
u_int8_t routed;
u_int16_t dnpipe;
u_int32_t dnflags;
};
static __inline struct pf_mtag *

View File

@ -1081,6 +1081,9 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
nvlist_add_string(nvl, "qname", krule->qname);
nvlist_add_string(nvl, "tagname", krule->tagname);
nvlist_add_number(nvl, "dnpipe", krule->dnpipe);
nvlist_add_number(nvl, "dnflags", krule->dnflags);
nvlist_add_number(nvl, "action", krule->action);
return (nvl);
@ -1090,7 +1093,7 @@ int
pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
struct pf_keth_rule *krule)
{
int error;
int error = 0;
bzero(krule, sizeof(*krule));
@ -1119,6 +1122,9 @@ pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
PFNV_CHK(pf_nvstring(nvl, "tagname", krule->tagname,
sizeof(krule->tagname)));
PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &krule->dnpipe, 0));
PFNV_CHK(pf_nvuint32_opt(nvl, "dnflags", &krule->dnflags, 0));
PFNV_CHK(pf_nvuint8(nvl, "action", &krule->action));
if (krule->action != PF_PASS && krule->action != PF_DROP)