pf: add anchor support for ether rules

Support anchors in ether rules.

Sponsored by:	Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D32482
This commit is contained in:
Kristof Provost 2021-10-01 19:05:50 +02:00
parent 87a89d6e14
commit c5131afee3
13 changed files with 1247 additions and 167 deletions

View file

@ -606,20 +606,34 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
rule->dnpipe = nvlist_get_number(nvl, "dnpipe");
rule->dnflags = nvlist_get_number(nvl, "dnflags");
rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard");
rule->action = nvlist_get_number(nvl, "action");
}
int
pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules)
pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
const char *path)
{
uint8_t buf[1024];
struct pfioc_nv nv;
nvlist_t *nvl;
void *packed;
size_t len;
bzero(rules, sizeof(*rules));
nvl = nvlist_create(0);
nvlist_add_string(nvl, "anchor", path);
packed = nvlist_pack(nvl, &len);
memcpy(buf, packed, len);
free(packed);
nvlist_destroy(nvl);
nv.data = buf;
nv.len = nv.size = sizeof(buf);
nv.len = len;
nv.size = sizeof(buf);
if (ioctl(dev, DIOCGETETHRULES, &nv) != 0)
return (errno);
@ -637,7 +651,8 @@ pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules)
int
pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
struct pfctl_eth_rule *rule, bool clear)
const char *path, struct pfctl_eth_rule *rule, bool clear,
char *anchor_call)
{
uint8_t buf[1024];
struct pfioc_nv nv;
@ -647,6 +662,7 @@ pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
nvl = nvlist_create(0);
nvlist_add_string(nvl, "anchor", path);
nvlist_add_number(nvl, "ticket", ticket);
nvlist_add_number(nvl, "nr", nr);
nvlist_add_bool(nvl, "clear", clear);
@ -670,12 +686,17 @@ pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
pfctl_nveth_rule_to_eth_rule(nvl, rule);
if (anchor_call)
strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"),
MAXPATHLEN);
nvlist_destroy(nvl);
return (0);
}
int
pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, uint32_t ticket)
pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
const char *anchor_call, uint32_t ticket)
{
struct pfioc_nv nv;
nvlist_t *nvl, *addr;
@ -686,6 +707,8 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, uint32_t ticket)
nvl = nvlist_create(0);
nvlist_add_number(nvl, "ticket", ticket);
nvlist_add_string(nvl, "anchor", anchor);
nvlist_add_string(nvl, "anchor_call", anchor_call);
nvlist_add_number(nvl, "nr", r->nr);
nvlist_add_bool(nvl, "quick", r->quick);

View file

@ -37,6 +37,7 @@
#include <netpfil/pf/pf.h>
struct pfctl_anchor;
struct pfctl_eth_anchor;
struct pfctl_status_counter {
uint64_t id;
@ -100,11 +101,28 @@ struct pfctl_eth_rule {
uint32_t dnflags;
uint8_t action;
struct pfctl_eth_anchor *anchor;
uint8_t anchor_relative;
uint8_t anchor_wildcard;
TAILQ_ENTRY(pfctl_eth_rule) entries;
};
TAILQ_HEAD(pfctl_eth_rules, pfctl_eth_rule);
struct pfctl_eth_ruleset {
struct pfctl_eth_rules rules;
struct pfctl_eth_anchor *anchor;
};
struct pfctl_eth_anchor {
struct pfctl_eth_anchor *parent;
char name[PF_ANCHOR_NAME_SIZE];
char path[MAXPATHLEN];
struct pfctl_eth_ruleset ruleset;
int refcnt; /* anchor rules */
int match; /* XXX: used for pfctl black magic */
};
struct pfctl_pool {
struct pf_palist list;
struct pf_pooladdr *cur;
@ -331,11 +349,13 @@ struct pfctl_syncookies {
struct pfctl_status* pfctl_get_status(int dev);
void pfctl_free_status(struct pfctl_status *status);
int pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules);
int pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
const char *path);
int pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
struct pfctl_eth_rule *rule, bool clear);
const char *path, struct pfctl_eth_rule *rule, bool clear,
char *anchor_call);
int pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r,
uint32_t ticket);
const char *anchor, const char *anchor_call, uint32_t ticket);
int pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket,
const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
char *anchor_call);

View file

@ -350,7 +350,7 @@ 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 *);
struct node_mac *, struct node_mac *, 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 *,
@ -370,6 +370,7 @@ int rule_label(struct pfctl_rule *, char *s[PF_RULE_MAX_LABEL_COUNT]);
int rt_tableid_max(void);
void mv_rules(struct pfctl_ruleset *, struct pfctl_ruleset *);
void mv_eth_rules(struct pfctl_eth_ruleset *, struct pfctl_eth_ruleset *);
void decide_address_family(struct node_host *, sa_family_t *);
void remove_invalid_hosts(struct node_host **, sa_family_t *);
int invalid_redirect(struct node_host *, sa_family_t);
@ -566,6 +567,7 @@ ruleset : /* empty */
| ruleset '\n'
| ruleset option '\n'
| ruleset etherrule '\n'
| ruleset etheranchorrule '\n'
| ruleset scrubrule '\n'
| ruleset natrule '\n'
| ruleset binatrule '\n'
@ -1196,7 +1198,95 @@ etherrule : ETHER action dir quick interface etherproto etherfromto etherfilter_
r.dnpipe = $8.dnpipe;
r.dnflags = $8.free_flags;
expand_eth_rule(&r, $5, $6, $7.src, $7.dst);
expand_eth_rule(&r, $5, $6, $7.src, $7.dst, "");
}
;
etherpfa_anchorlist : /* empty */
| etherpfa_anchorlist '\n'
| etherpfa_anchorlist etherrule '\n'
| etherpfa_anchorlist etheranchorrule '\n'
;
etherpfa_anchor : '{'
{
char ta[PF_ANCHOR_NAME_SIZE];
struct pfctl_eth_ruleset *rs;
/* steping into a brace anchor */
pf->asd++;
pf->bn++;
/* create a holding ruleset in the root */
snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn);
rs = pf_find_or_create_eth_ruleset(ta);
if (rs == NULL)
err(1, "etherpfa_anchor: pf_find_or_create_eth_ruleset");
pf->eastack[pf->asd] = rs->anchor;
pf->eanchor = rs->anchor;
} '\n' etherpfa_anchorlist '}'
{
pf->ealast = pf->eanchor;
pf->asd--;
pf->eanchor = pf->eastack[pf->asd];
}
| /* empty */
;
etheranchorrule : ETHER ANCHOR anchorname dir quick interface etherproto etherfromto etherpfa_anchor
{
struct pfctl_eth_rule r;
if (check_rulestate(PFCTL_STATE_ETHER)) {
free($3);
YYERROR;
}
if ($3 && ($3[0] == '_' || strstr($3, "/_") != NULL)) {
free($3);
yyerror("anchor names beginning with '_' "
"are reserved for internal use");
YYERROR;
}
memset(&r, 0, sizeof(r));
if (pf->eastack[pf->asd + 1]) {
/* move inline rules into relative location */
pfctl_eth_anchor_setup(pf, &r,
&pf->eastack[pf->asd]->ruleset,
$3 ? $3 : pf->ealast->name);
if (r.anchor == NULL)
err(1, "etheranchorrule: unable to "
"create ruleset");
if (pf->ealast != r.anchor) {
if (r.anchor->match) {
yyerror("inline anchor '%s' "
"already exists",
r.anchor->name);
YYERROR;
}
mv_eth_rules(&pf->ealast->ruleset,
&r.anchor->ruleset);
}
pf_remove_if_empty_eth_ruleset(&pf->ealast->ruleset);
pf->ealast = r.anchor;
} else {
if (!$3) {
yyerror("anchors without explicit "
"rules must specify a name");
YYERROR;
}
}
r.direction = $4;
r.quick = $5.quick;
expand_eth_rule(&r, $6, $7, $8.src, $8.dst,
pf->eastack[pf->asd + 1] ? pf->ealast->name : $3);
free($3);
pf->eastack[pf->asd + 1] = NULL;
}
;
@ -5640,15 +5730,12 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces,
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)
struct node_mac *srcs, struct node_mac *dsts, const char *anchor_call)
{
struct pfctl_eth_rule *rule;
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,
r->nr = pf->eth_nr++;
strlcpy(r->ifname, interface->ifname,
sizeof(r->ifname));
r->ifnot = interface->not;
@ -5657,12 +5744,9 @@ expand_eth_rule(struct pfctl_eth_rule *r,
r->src.neg = src->neg;
bcopy(dst->mac, r->dst.addr, ETHER_ADDR_LEN);
r->dst.neg = dst->neg;
r->nr = pf->eastack[pf->asd]->match++;
if ((rule = calloc(1, sizeof(*rule))) == NULL)
err(1, "calloc");
bcopy(r, rule, sizeof(*rule));
TAILQ_INSERT_TAIL(&pf->eth_rules, rule, entries);
pfctl_append_eth_rule(pf, r, anchor_call);
))));
FREE_LIST(struct node_if, interfaces);
@ -6525,6 +6609,19 @@ mv_rules(struct pfctl_ruleset *src, struct pfctl_ruleset *dst)
}
}
void
mv_eth_rules(struct pfctl_eth_ruleset *src, struct pfctl_eth_ruleset *dst)
{
struct pfctl_eth_rule *r;
while ((r = TAILQ_FIRST(&src->rules)) != NULL) {
TAILQ_REMOVE(&src->rules, r, entries);
TAILQ_INSERT_TAIL(&dst->rules, r, entries);
dst->anchor->match++;
}
src->anchor->match = 0;
}
void
decide_address_family(struct node_host *n, sa_family_t *af)
{

View file

@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$");
#define rs_free(x) free(x)
#include "pfctl.h"
#include "pfctl_parser.h"
#ifdef PFDEBUG
#include <sys/stdarg.h>
@ -74,7 +75,8 @@ __FBSDID("$FreeBSD$");
#endif /* PFDEBUG */
struct pfctl_anchor_global pf_anchors;
struct pfctl_anchor pf_main_anchor;
extern struct pfctl_anchor pf_main_anchor;
extern struct pfctl_eth_anchor pf_eth_main_anchor;
#undef V_pf_anchors
#define V_pf_anchors pf_anchors
#undef pf_main_ruleset
@ -290,6 +292,148 @@ pf_remove_if_empty_ruleset(struct pfctl_ruleset *ruleset)
ruleset = &parent->ruleset;
}
}
void
pf_remove_if_empty_eth_ruleset(struct pfctl_eth_ruleset *ruleset)
{
struct pfctl_eth_anchor *parent;
return;
while (ruleset != NULL) {
if (ruleset == &pf_eth_main_anchor.ruleset ||
ruleset->anchor == NULL || ruleset->anchor->refcnt > 0)
return;
if (!TAILQ_EMPTY(&ruleset->rules))
return;
rs_free(ruleset->anchor);
if (parent == NULL)
return;
ruleset = &parent->ruleset;
}
}
void
pf_init_eth_ruleset(struct pfctl_eth_ruleset *ruleset)
{
memset(ruleset, 0, sizeof(*ruleset));
TAILQ_INIT(&ruleset->rules);
}
static struct pfctl_eth_anchor*
_pf_find_eth_anchor(struct pfctl_eth_anchor *anchor, const char *path)
{
struct pfctl_eth_rule *r;
struct pfctl_eth_anchor *a;
if (strcmp(path, anchor->path) == 0)
return (anchor);
TAILQ_FOREACH(r, &anchor->ruleset.rules, entries) {
if (! r->anchor)
continue;
/* Step into anchor */
a = _pf_find_eth_anchor(r->anchor, path);
if (a)
return (a);
}
return (NULL);
}
static struct pfctl_eth_anchor*
pf_find_eth_anchor(const char *path)
{
return (_pf_find_eth_anchor(&pf_eth_main_anchor, path));
}
static struct pfctl_eth_ruleset*
pf_find_eth_ruleset(const char *path)
{
struct pfctl_eth_anchor *anchor;
while (*path == '/')
path++;
if (!*path)
return (&pf_eth_main_anchor.ruleset);
anchor = pf_find_eth_anchor(path);
if (anchor == NULL)
return (NULL);
else
return (&anchor->ruleset);
}
struct pfctl_eth_ruleset *
pf_find_or_create_eth_ruleset(const char *path)
{
char *p, *q, *r;
struct pfctl_eth_ruleset *ruleset;
struct pfctl_eth_anchor *anchor = NULL, *parent = NULL;
if (path[0] == 0)
return (&pf_eth_main_anchor.ruleset);
while (*path == '/')
path++;
ruleset = pf_find_eth_ruleset(path);
if (ruleset != NULL)
return (ruleset);
p = (char *)rs_malloc(MAXPATHLEN);
if (p == NULL)
return (NULL);
strlcpy(p, path, MAXPATHLEN);
while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
*q = 0;
if ((ruleset = pf_find_eth_ruleset(p)) != NULL) {
parent = ruleset->anchor;
break;
}
}
if (q == NULL)
q = p;
else
q++;
strlcpy(p, path, MAXPATHLEN);
if (!*q) {
rs_free(p);
return (NULL);
}
while ((r = strchr(q, '/')) != NULL || *q) {
if (r != NULL)
*r = 0;
if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
(parent != NULL && strlen(parent->path) >=
MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
rs_free(p);
return (NULL);
}
anchor = (struct pfctl_eth_anchor *)rs_malloc(sizeof(*anchor));
if (anchor == NULL) {
rs_free(p);
return (NULL);
}
strlcpy(anchor->name, q, sizeof(anchor->name));
if (parent != NULL) {
strlcpy(anchor->path, parent->path,
sizeof(anchor->path));
strlcat(anchor->path, "/", sizeof(anchor->path));
}
strlcat(anchor->path, anchor->name, sizeof(anchor->path));
if (parent != NULL)
anchor->parent = parent;
pf_init_eth_ruleset(&anchor->ruleset);
anchor->ruleset.anchor = anchor;
parent = anchor;
if (r != NULL)
q = r + 1;
else
*q = 0;
}
rs_free(p);
return (&anchor->ruleset);
}
int
pfctl_anchor_setup(struct pfctl_rule *r, const struct pfctl_ruleset *s,
const char *name)
@ -345,3 +489,54 @@ pfctl_anchor_setup(struct pfctl_rule *r, const struct pfctl_ruleset *s,
r->anchor->refcnt++;
return (0);
}
int
pfctl_eth_anchor_setup(struct pfctl *pf, struct pfctl_eth_rule *r,
const struct pfctl_eth_ruleset *s, const char *name)
{
char *p, *path;
struct pfctl_eth_ruleset *ruleset;
r->anchor = NULL;
if (!name[0])
return (0);
path = (char *)rs_malloc(MAXPATHLEN);
if (path == NULL)
return (1);
if (name[0] == '/')
strlcpy(path, name + 1, MAXPATHLEN);
else {
/* relative path */
if (s->anchor == NULL || !s->anchor->path[0])
path[0] = 0;
else
strlcpy(path, s->anchor->path, MAXPATHLEN);
while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
if (!path[0]) {
printf("%s: .. beyond root\n", __func__);
rs_free(path);
return (1);
}
if ((p = strrchr(path, '/')) != NULL)
*p = 0;
else
path[0] = 0;
name += 3;
}
if (path[0])
strlcat(path, "/", MAXPATHLEN);
strlcat(path, name, MAXPATHLEN);
}
if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
*p = 0;
}
ruleset = pf_find_or_create_eth_ruleset(path);
rs_free(path);
if (ruleset == NULL || ruleset->anchor == NULL) {
printf("%s: ruleset\n", __func__);
return (1);
}
r->anchor = ruleset->anchor;
r->anchor->refcnt++;
return (0);
}

View file

@ -98,7 +98,7 @@ int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int,
char *);
void pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int);
void pfctl_print_rule_counters(struct pfctl_rule *, int);
int pfctl_show_eth_rules(int, int, enum pfctl_show);
int pfctl_show_eth_rules(int, char *, int, enum pfctl_show, char *, int);
int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int);
int pfctl_show_nat(int, int, char *);
int pfctl_show_src_nodes(int, int);
@ -110,15 +110,21 @@ int pfctl_show_limits(int, int);
void pfctl_debug(int, u_int32_t, int);
int pfctl_test_altqsupport(int, int);
int pfctl_show_anchors(int, int, char *);
int pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *);
int pfctl_load_eth_ruleset(struct pfctl *);
int pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *, bool);
int pfctl_eth_ruleset_trans(struct pfctl *, char *,
struct pfctl_eth_anchor *);
int pfctl_load_eth_ruleset(struct pfctl *, char *,
struct pfctl_eth_ruleset *, int);
int pfctl_load_eth_rule(struct pfctl *, char *, struct pfctl_eth_rule *,
int);
int pfctl_load_ruleset(struct pfctl *, char *,
struct pfctl_ruleset *, int, int);
int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int);
const char *pfctl_lookup_option(char *, const char * const *);
static struct pfctl_anchor_global pf_anchors;
static struct pfctl_anchor pf_main_anchor;
struct pfctl_anchor pf_main_anchor;
struct pfctl_eth_anchor pf_eth_main_anchor;
static struct pfr_buffer skip_b;
static const char *clearopt;
@ -1052,31 +1058,66 @@ pfctl_print_title(char *title)
}
int
pfctl_show_eth_rules(int dev, int opts, enum pfctl_show format)
pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format,
char *anchorname, int depth)
{
char anchor_call[MAXPATHLEN];
struct pfctl_eth_rules_info info;
struct pfctl_eth_rule rule;
int dotitle = opts & PF_OPT_SHOWALL;
int len = strlen(path);
int brace;
char *p;
if (pfctl_get_eth_rules_info(dev, &info)) {
if (path[0])
snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
else
snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
if (pfctl_get_eth_rules_info(dev, &info, path)) {
warn("DIOCGETETHRULES");
return (-1);
}
for (int nr = 0; nr < info.nr; nr++) {
if (pfctl_get_eth_rule(dev, nr, info.ticket, &rule,
opts & PF_OPT_CLRRULECTRS) != 0) {
brace = 0;
INDENT(depth, !(opts & PF_OPT_VERBOSE));
if (pfctl_get_eth_rule(dev, nr, info.ticket, path, &rule,
opts & PF_OPT_CLRRULECTRS, anchor_call) != 0) {
warn("DIOCGETETHRULE");
return (-1);
}
if (anchor_call[0] &&
((((p = strrchr(anchor_call, '_')) != NULL) &&
(p == anchor_call ||
*(--p) == '/')) || (opts & PF_OPT_RECURSE))) {
brace++;
if ((p = strrchr(anchor_call, '/')) !=
NULL)
p++;
else
p = &anchor_call[0];
} else
p = &anchor_call[0];
if (dotitle) {
pfctl_print_title("ETH RULES:");
dotitle = 0;
}
print_eth_rule(&rule, opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG));
printf("\n");
print_eth_rule(&rule, anchor_call,
opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG));
if (brace)
printf(" {\n");
else
printf("\n");
pfctl_print_eth_rule_counters(&rule, opts);
if (brace) {
pfctl_show_eth_rules(dev, path, opts, format,
p, depth + 1);
INDENT(depth, !(opts & PF_OPT_VERBOSE));
printf("}\n");
}
}
path[len] = '\0';
return (0);
}
@ -1508,15 +1549,70 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
}
int
pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a)
pfctl_append_eth_rule(struct pfctl *pf, struct pfctl_eth_rule *r,
const char *anchor_call)
{
struct pfctl_eth_rule *rule;
struct pfctl_eth_ruleset *rs;
char *p;
rs = &pf->eanchor->ruleset;
if (anchor_call[0] && r->anchor == NULL) {
/*
* Don't make non-brace anchors part of the main anchor pool.
*/
if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL)
err(1, "pfctl_append_rule: calloc");
pf_init_eth_ruleset(&r->anchor->ruleset);
r->anchor->ruleset.anchor = r->anchor;
if (strlcpy(r->anchor->path, anchor_call,
sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path))
errx(1, "pfctl_append_rule: strlcpy");
if ((p = strrchr(anchor_call, '/')) != NULL) {
if (!strlen(p))
err(1, "pfctl_append_eth_rule: bad anchor name %s",
anchor_call);
} else
p = (char *)anchor_call;
if (strlcpy(r->anchor->name, p,
sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name))
errx(1, "pfctl_append_eth_rule: strlcpy");
}
if ((rule = calloc(1, sizeof(*rule))) == NULL)
err(1, "calloc");
bcopy(r, rule, sizeof(*rule));
TAILQ_INSERT_TAIL(&rs->rules, rule, entries);
return (0);
}
int
pfctl_eth_ruleset_trans(struct pfctl *pf, char *path,
struct pfctl_eth_anchor *a)
{
int osize = pf->trans->pfrb_size;
if ((pf->loadopt & PFCTL_FLAG_ETH) != 0) {
if (! path[0]) {
if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
return (1);
}
if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
return (1);
}
if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize))
return (5);
return (0);
}
int
pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a, bool do_eth)
{
int osize = pf->trans->pfrb_size;
if ((pf->loadopt & PFCTL_FLAG_ETH) != 0 && do_eth) {
if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path))
return (1);
}
if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) {
if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) ||
@ -1544,22 +1640,92 @@ pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a)
}
int
pfctl_load_eth_ruleset(struct pfctl *pf)
pfctl_load_eth_ruleset(struct pfctl *pf, char *path,
struct pfctl_eth_ruleset *rs, int depth)
{
struct pfctl_eth_rule *r;
int error;
int error, len = strlen(path);
int brace = 0;
while ((r = TAILQ_FIRST(&pf->eth_rules)) != NULL) {
TAILQ_REMOVE(&pf->eth_rules, r, entries);
pf->eanchor = rs->anchor;
if (path[0])
snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->eanchor->name);
else
snprintf(&path[len], MAXPATHLEN - len, "%s", pf->eanchor->name);
if ((pf->opts & PF_OPT_NOACTION) == 0) {
error = pfctl_add_eth_rule(pf->dev, r, pf->eth_ticket);
if (error)
if (depth) {
if (TAILQ_FIRST(&rs->rules) != NULL) {
brace++;
if (pf->opts & PF_OPT_VERBOSE)
printf(" {\n");
if ((pf->opts & PF_OPT_NOACTION) == 0 &&
(error = pfctl_eth_ruleset_trans(pf,
path, rs->anchor))) {
printf("pfctl_load_eth_rulesets: "
"pfctl_eth_ruleset_trans %d\n", error);
goto error;
}
} else if (pf->opts & PF_OPT_VERBOSE)
printf("\n");
}
while ((r = TAILQ_FIRST(&rs->rules)) != NULL) {
TAILQ_REMOVE(&rs->rules, r, entries);
error = pfctl_load_eth_rule(pf, path, r, depth);
if (error)
return (error);
if (r->anchor) {
if ((error = pfctl_load_eth_ruleset(pf, path,
&r->anchor->ruleset, depth + 1)))
return (error);
}
free(r);
}
if (brace && pf->opts & PF_OPT_VERBOSE) {
INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE));
printf("}\n");
}
path[len] = '\0';
return (0);
error:
path[len] = '\0';
return (error);
}
int
pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r,
int depth)
{
char *name;
char anchor[PF_ANCHOR_NAME_SIZE];
int len = strlen(path);
if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
errx(1, "pfctl_load_eth_rule: strlcpy");
if (r->anchor) {
if (r->anchor->match) {
if (path[0])
snprintf(&path[len], MAXPATHLEN - len,
"/%s", r->anchor->name);
else
snprintf(&path[len], MAXPATHLEN - len,
"%s", r->anchor->name);
name = r->anchor->name;
} else
name = r->anchor->path;
} else
name = "";
if ((pf->opts & PF_OPT_NOACTION) == 0)
if (pfctl_add_eth_rule(pf->dev, r, anchor, name,
pf->eth_ticket))
err(1, "DIOCADDETHRULENV");
path[len] = '\0';
return (0);
}
@ -1586,7 +1752,7 @@ pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
printf(" {\n");
if ((pf->opts & PF_OPT_NOACTION) == 0 &&
(error = pfctl_ruleset_trans(pf,
path, rs->anchor))) {
path, rs->anchor, false))) {
printf("pfctl_load_rulesets: "
"pfctl_ruleset_trans %d\n", error);
goto error;
@ -1711,6 +1877,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
struct pfioc_altq pa;
struct pfctl pf;
struct pfctl_ruleset *rs;
struct pfctl_eth_ruleset *ethrs;
struct pfr_table trs;
char *path;
int osize;
@ -1719,6 +1886,11 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
pf_init_ruleset(&pf_main_anchor.ruleset);
pf_main_anchor.ruleset.anchor = &pf_main_anchor;
memset(&pf_eth_main_anchor, 0, sizeof(pf_eth_main_anchor));
pf_init_eth_ruleset(&pf_eth_main_anchor.ruleset);
pf_eth_main_anchor.ruleset.anchor = &pf_eth_main_anchor;
if (trans == NULL) {
bzero(&buf, sizeof(buf));
buf.pfrb_type = PFRB_TRANS;
@ -1742,7 +1914,6 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
pf.opts = opts;
pf.optimize = optimize;
pf.loadopt = loadopt;
TAILQ_INIT(&pf.eth_rules);
/* non-brace anchor, create without resolving the path */
if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL)
@ -1752,10 +1923,10 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
rs->anchor = pf.anchor;
if (strlcpy(pf.anchor->path, anchorname,
sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path))
errx(1, "pfctl_add_rule: strlcpy");
errx(1, "pfctl_rules: strlcpy");
if (strlcpy(pf.anchor->name, anchorname,
sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name))
errx(1, "pfctl_add_rule: strlcpy");
errx(1, "pfctl_rules: strlcpy");
pf.astack[0] = pf.anchor;
@ -1766,13 +1937,29 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
pf.trans = t;
pfctl_init_options(&pf);
/* Set up ethernet anchor */
if ((pf.eanchor = calloc(1, sizeof(*pf.eanchor))) == NULL)
ERRX("pfctl_rules: calloc");
if (strlcpy(pf.eanchor->path, anchorname,
sizeof(pf.eanchor->path)) >= sizeof(pf.eanchor->path))
errx(1, "pfctl_rules: strlcpy");
if (strlcpy(pf.eanchor->name, anchorname,
sizeof(pf.eanchor->name)) >= sizeof(pf.eanchor->name))
errx(1, "pfctl_rules: strlcpy");
ethrs = &pf.eanchor->ruleset;
pf_init_eth_ruleset(ethrs);
ethrs->anchor = pf.eanchor;
pf.eastack[0] = pf.eanchor;
if ((opts & PF_OPT_NOACTION) == 0) {
/*
* XXX For the time being we need to open transactions for
* the main ruleset before parsing, because tables are still
* loaded at parse time.
*/
if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor))
if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor, true))
ERRX("pfctl_rules");
if (pf.loadopt & PFCTL_FLAG_ETH)
pf.eth_ticket = pfctl_get_ticket(t, PF_RULESET_ETH, anchorname);
@ -1797,7 +1984,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
if ((pf.loadopt & PFCTL_FLAG_FILTER &&
(pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) ||
(pf.loadopt & PFCTL_FLAG_ETH &&
(pfctl_load_eth_ruleset(&pf))) ||
(pfctl_load_eth_ruleset(&pf, path, ethrs, 0))) ||
(pf.loadopt & PFCTL_FLAG_NAT &&
(pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) ||
pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) ||
@ -2572,7 +2759,7 @@ main(int argc, char *argv[])
sizeof(anchorname)) >= sizeof(anchorname))
errx(1, "anchor name '%s' too long",
anchoropt);
loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE;
loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE|PFCTL_FLAG_ETH;
}
if ((opts & PF_OPT_NOACTION) == 0) {
@ -2640,13 +2827,13 @@ main(int argc, char *argv[])
pfctl_show_limits(dev, opts);
break;
case 'e':
pfctl_show_eth_rules(dev, opts, 0);
pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0);
break;
case 'a':
opts |= PF_OPT_SHOWALL;
pfctl_load_fingerprints(dev, opts);
pfctl_show_eth_rules(dev, opts, 0);
pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0);
pfctl_show_nat(dev, opts, anchorname);
pfctl_show_rules(dev, path, opts, 0, anchorname, 0);
@ -2674,7 +2861,8 @@ main(int argc, char *argv[])
}
if ((opts & PF_OPT_CLRRULECTRS) && showopt == NULL) {
pfctl_show_eth_rules(dev, opts, PFCTL_SHOW_NOTHING);
pfctl_show_eth_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
anchorname, 0);
pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
anchorname, 0);
}

View file

@ -38,6 +38,8 @@
#include <libpfctl.h>
struct pfctl;
enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS,
@ -137,6 +139,13 @@ int pfctl_anchor_setup(struct pfctl_rule *,
void pf_remove_if_empty_ruleset(struct pfctl_ruleset *);
struct pfctl_ruleset *pf_find_ruleset(const char *);
struct pfctl_ruleset *pf_find_or_create_ruleset(const char *);
void pf_init_eth_ruleset(struct pfctl_eth_ruleset *);
int pfctl_eth_anchor_setup(struct pfctl *,
struct pfctl_eth_rule *,
const struct pfctl_eth_ruleset *, const char *);
struct pfctl_eth_ruleset *pf_find_or_create_eth_ruleset(const char *);
void pf_remove_if_empty_eth_ruleset(
struct pfctl_eth_ruleset *);
void expand_label(char *, size_t, struct pfctl_rule *);

View file

@ -710,14 +710,23 @@ print_eth_addr(const struct pfctl_eth_addr *a)
}
void
print_eth_rule(struct pfctl_eth_rule *r, int rule_numbers)
print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
int rule_numbers)
{
static const char *actiontypes[] = { "pass", "block" };
if (rule_numbers)
printf("@%u ", r->nr);
printf("ether %s", actiontypes[r->action]);
printf("ether ");
if (anchor_call[0]) {
if (anchor_call[0] == '_') {
printf("anchor");
} else
printf("anchor \"%s\"", anchor_call);
} else {
printf("%s", actiontypes[r->action]);
}
if (r->direction == PF_IN)
printf(" in");
else if (r->direction == PF_OUT)

View file

@ -91,7 +91,8 @@ struct pfctl {
struct pfr_buffer *trans;
struct pfctl_anchor *anchor, *alast;
int eth_nr;
struct pfctl_eth_rules eth_rules;
struct pfctl_eth_anchor *eanchor, *ealast;
struct pfctl_eth_anchor *eastack[PFCTL_ANCHOR_STACK_DEPTH];
u_int32_t eth_ticket;
const char *ruleset;
@ -274,6 +275,8 @@ int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
int pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *);
int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *);
int pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *,
const char *);
int pfctl_add_altq(struct pfctl *, struct pf_altq *);
int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t);
void pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *);
@ -294,7 +297,7 @@ int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int);
void print_src_node(struct pf_src_node *, int);
void print_eth_rule(struct pfctl_eth_rule *, int);
void print_eth_rule(struct pfctl_eth_rule *, const char *, int);
void print_rule(struct pfctl_rule *, const char *, int, int);
void print_tabledef(const char *, int, int, struct node_tinithead *);
void print_status(struct pfctl_status *, struct pfctl_syncookies *, int);

View file

@ -583,6 +583,41 @@ struct pf_keth_rule_addr {
uint8_t isset;
};
struct pf_keth_anchor;
TAILQ_HEAD(pf_keth_ruleq, pf_keth_rule);
struct pf_keth_ruleset {
struct pf_keth_ruleq rules[2];
struct pf_keth_rules {
struct pf_keth_ruleq *rules;
int open;
uint32_t ticket;
} active, inactive;
struct epoch_context epoch_ctx;
struct vnet *vnet;
struct pf_keth_anchor *anchor;
};
RB_HEAD(pf_keth_anchor_global, pf_keth_anchor);
RB_HEAD(pf_keth_anchor_node, pf_keth_anchor);
struct pf_keth_anchor {
RB_ENTRY(pf_keth_anchor) entry_node;
RB_ENTRY(pf_keth_anchor) entry_global;
struct pf_keth_anchor *parent;
struct pf_keth_anchor_node children;
char name[PF_ANCHOR_NAME_SIZE];
char path[MAXPATHLEN];
struct pf_keth_ruleset ruleset;
int refcnt; /* anchor rules */
uint8_t anchor_relative;
uint8_t anchor_wildcard;
};
RB_PROTOTYPE(pf_keth_anchor_node, pf_keth_anchor, entry_node,
pf_keth_anchor_compare);
RB_PROTOTYPE(pf_keth_anchor_global, pf_keth_anchor, entry_global,
pf_keth_anchor_compare);
struct pf_keth_rule {
#define PFE_SKIP_IFP 0
#define PFE_SKIP_DIR 1
@ -594,6 +629,10 @@ struct pf_keth_rule {
TAILQ_ENTRY(pf_keth_rule) entries;
struct pf_keth_anchor *anchor;
u_int8_t anchor_relative;
u_int8_t anchor_wildcard;
uint32_t nr;
bool quick;
@ -621,16 +660,6 @@ struct pf_keth_rule {
uint32_t dnflags;
};
TAILQ_HEAD(pf_keth_rules, pf_keth_rule);
struct pf_keth_settings {
struct pf_keth_rules rules;
uint32_t ticket;
int open;
struct vnet *vnet;
struct epoch_context epoch_ctx;
};
union pf_krule_ptr {
struct pf_krule *ptr;
u_int32_t nr;
@ -1151,6 +1180,7 @@ RB_PROTOTYPE(pf_kanchor_node, pf_kanchor, entry_node, pf_kanchor_compare);
PFR_TFLAG_COUNTERS)
struct pf_kanchor_stackframe;
struct pf_keth_anchor_stackframe;
struct pfr_table {
char pfrt_anchor[MAXPATHLEN];
@ -2206,15 +2236,17 @@ VNET_DECLARE(struct pf_kanchor_global, pf_anchors);
#define V_pf_anchors VNET(pf_anchors)
VNET_DECLARE(struct pf_kanchor, pf_main_anchor);
#define V_pf_main_anchor VNET(pf_main_anchor)
VNET_DECLARE(struct pf_keth_anchor_global, pf_keth_anchors);
#define V_pf_keth_anchors VNET(pf_keth_anchors)
#define pf_main_ruleset V_pf_main_anchor.ruleset
VNET_DECLARE(struct pf_keth_settings*, pf_keth);
VNET_DECLARE(struct pf_keth_anchor, pf_main_keth_anchor);
#define V_pf_main_keth_anchor VNET(pf_main_keth_anchor)
VNET_DECLARE(struct pf_keth_ruleset*, pf_keth);
#define V_pf_keth VNET(pf_keth)
VNET_DECLARE(struct pf_keth_settings*, pf_keth_inactive);
#define V_pf_keth_inactive VNET(pf_keth_inactive)
void pf_init_kruleset(struct pf_kruleset *);
void pf_init_keth(struct pf_keth_settings *);
void pf_init_keth(struct pf_keth_ruleset *);
int pf_kanchor_setup(struct pf_krule *,
const struct pf_kruleset *, const char *);
int pf_kanchor_nvcopyout(const struct pf_kruleset *,
@ -2227,7 +2259,21 @@ struct pf_kruleset *pf_find_kruleset(const char *);
struct pf_kruleset *pf_find_or_create_kruleset(const char *);
void pf_rs_initialize(void);
struct pf_krule *pf_krule_alloc(void);
void pf_remove_if_empty_keth_ruleset(
struct pf_keth_ruleset *);
struct pf_keth_ruleset *pf_find_keth_ruleset(const char *);
struct pf_keth_anchor *pf_find_keth_anchor(const char *);
int pf_keth_anchor_setup(struct pf_keth_rule *,
const struct pf_keth_ruleset *, const char *);
int pf_keth_anchor_nvcopyout(
const struct pf_keth_ruleset *,
const struct pf_keth_rule *, nvlist_t *);
struct pf_keth_ruleset *pf_find_or_create_keth_ruleset(const char *);
void pf_keth_anchor_remove(struct pf_keth_rule *);
void pf_krule_free(struct pf_krule *);
#endif
@ -2251,6 +2297,14 @@ void pf_step_into_anchor(struct pf_kanchor_stackframe *, int *,
int pf_step_out_of_anchor(struct pf_kanchor_stackframe *, int *,
struct pf_kruleset **, int, struct pf_krule **,
struct pf_krule **, int *);
void pf_step_into_keth_anchor(struct pf_keth_anchor_stackframe *,
int *, struct pf_keth_ruleset **,
struct pf_keth_rule **, struct pf_keth_rule **,
int *);
int pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *,
int *, struct pf_keth_ruleset **,
struct pf_keth_rule **, struct pf_keth_rule **,
int *);
int pf_map_addr(u_int8_t, struct pf_krule *,
struct pf_addr *, struct pf_addr *,

View file

@ -3404,6 +3404,110 @@ pf_step_out_of_anchor(struct pf_kanchor_stackframe *stack, int *depth,
return (quick);
}
struct pf_keth_anchor_stackframe {
struct pf_keth_ruleset *rs;
struct pf_keth_rule *r; /* XXX: + match bit */
struct pf_keth_anchor *child;
};
#define PF_ETH_ANCHOR_MATCH(f) ((uintptr_t)(f)->r & PF_ANCHORSTACK_MATCH)
#define PF_ETH_ANCHOR_RULE(f) (struct pf_keth_rule *) \
((uintptr_t)(f)->r & ~PF_ANCHORSTACK_MASK)
#define PF_ETH_ANCHOR_SET_MATCH(f) do { (f)->r = (void *) \
((uintptr_t)(f)->r | PF_ANCHORSTACK_MATCH); \
} while (0)
void
pf_step_into_keth_anchor(struct pf_keth_anchor_stackframe *stack, int *depth,
struct pf_keth_ruleset **rs, struct pf_keth_rule **r,
struct pf_keth_rule **a, int *match)
{
struct pf_keth_anchor_stackframe *f;
NET_EPOCH_ASSERT();
if (match)
*match = 0;
if (*depth >= PF_ANCHOR_STACKSIZE) {
printf("%s: anchor stack overflow on %s\n",
__func__, (*r)->anchor->name);
*r = TAILQ_NEXT(*r, entries);
return;
} else if (*depth == 0 && a != NULL)
*a = *r;
f = stack + (*depth)++;
f->rs = *rs;
f->r = *r;
if ((*r)->anchor_wildcard) {
struct pf_keth_anchor_node *parent = &(*r)->anchor->children;
if ((f->child = RB_MIN(pf_keth_anchor_node, parent)) == NULL) {
*r = NULL;
return;
}
*rs = &f->child->ruleset;
} else {
f->child = NULL;
*rs = &(*r)->anchor->ruleset;
}
*r = TAILQ_FIRST((*rs)->active.rules);
}
int
pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *stack, int *depth,
struct pf_keth_ruleset **rs, struct pf_keth_rule **r,
struct pf_keth_rule **a, int *match)
{
struct pf_keth_anchor_stackframe *f;
struct pf_keth_rule *fr;
int quick = 0;
NET_EPOCH_ASSERT();
do {
if (*depth <= 0)
break;
f = stack + *depth - 1;
fr = PF_ETH_ANCHOR_RULE(f);
if (f->child != NULL) {
struct pf_keth_anchor_node *parent;
/*
* This block traverses through
* a wildcard anchor.
*/
parent = &fr->anchor->children;
if (match != NULL && *match) {
/*
* If any of "*" matched, then
* "foo/ *" matched, mark frame
* appropriately.
*/
PF_ETH_ANCHOR_SET_MATCH(f);
*match = 0;
}
f->child = RB_NEXT(pf_keth_anchor_node, parent,
f->child);
if (f->child != NULL) {
*rs = &f->child->ruleset;
*r = TAILQ_FIRST((*rs)->active.rules);
if (*r == NULL)
continue;
else
break;
}
}
(*depth)--;
if (*depth == 0 && a != NULL)
*a = NULL;
*rs = f->rs;
if (PF_ETH_ANCHOR_MATCH(f) || (match != NULL && *match))
quick = fr->quick;
*r = TAILQ_NEXT(fr, entries);
} while (*r == NULL);
return (quick);
}
#ifdef INET6
void
pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr,
@ -3719,10 +3823,13 @@ static int
pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
{
struct ether_header *e;
struct pf_keth_rule *r, *rm;
struct pf_keth_rule *r, *rm, *a = NULL;
struct pf_keth_ruleset *ruleset = NULL;
struct pf_mtag *mtag;
struct pf_keth_settings *settings;
struct pf_keth_ruleq *rules;
int asd = 0, match = 0;
uint8_t action;
struct pf_keth_anchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE];
NET_EPOCH_ASSERT();
@ -3733,8 +3840,9 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
e = mtod(m, struct ether_header *);
settings = ck_pr_load_ptr(&V_pf_keth);
r = TAILQ_FIRST(&settings->rules);
ruleset = V_pf_keth;
rules = ck_pr_load_ptr(&ruleset->active.rules);
r = TAILQ_FIRST(rules);
rm = NULL;
while (r != NULL) {
@ -3767,16 +3875,24 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf *m)
r = TAILQ_NEXT(r, entries);
}
else {
/* Rule matches */
rm = r;
if (r->anchor == NULL) {
/* Rule matches */
rm = r;
SDT_PROBE2(pf, eth, test_rule, match, r->nr, r);
SDT_PROBE2(pf, eth, test_rule, match, r->nr, r);
if (r->quick)
break;
if (r->quick)
break;
r = TAILQ_NEXT(r, entries);
r = TAILQ_NEXT(r, entries);
} else {
pf_step_into_keth_anchor(anchor_stack, &asd,
&ruleset, &r, &a, &match);
}
}
if (r == NULL && pf_step_out_of_keth_anchor(anchor_stack, &asd,
&ruleset, &r, &a, &match))
break;
}
r = rm;

View file

@ -106,10 +106,10 @@ static void pf_mv_kpool(struct pf_kpalist *, struct pf_kpalist *);
static void pf_empty_kpool(struct pf_kpalist *);
static int pfioctl(struct cdev *, u_long, caddr_t, int,
struct thread *);
static int pf_begin_eth(uint32_t *);
static int pf_begin_eth(uint32_t *, const char *);
static void pf_rollback_eth_cb(struct epoch_context *);
static int pf_rollback_eth(uint32_t);
static int pf_commit_eth(uint32_t);
static int pf_rollback_eth(uint32_t, const char *);
static int pf_commit_eth(uint32_t, const char *);
static void pf_free_eth_rule(struct pf_keth_rule *);
#ifdef ALTQ
static int pf_begin_altq(u_int32_t *);
@ -318,7 +318,6 @@ pfattach_vnet(void)
pf_init_kruleset(&pf_main_ruleset);
pf_init_keth(V_pf_keth);
pf_init_keth(V_pf_keth_inactive);
/* default rule should never be garbage collected */
V_pf_default_rule.entries.tqe_prev = &V_pf_default_rule.entries.tqe_next;
@ -510,6 +509,7 @@ pf_free_eth_rule(struct pf_keth_rule *rule)
counter_u64_free(rule->packets[i]);
counter_u64_free(rule->bytes[i]);
}
pf_keth_anchor_remove(rule);
free(rule, M_PFRULE);
}
@ -701,26 +701,32 @@ pf_tagname2tag(const char *tagname)
}
static int
pf_begin_eth(uint32_t *ticket)
pf_begin_eth(uint32_t *ticket, const char *anchor)
{
struct pf_keth_rule *rule, *tmp;
struct pf_keth_ruleset *rs;
PF_RULES_WASSERT();
if (V_pf_keth_inactive->open) {
rs = pf_find_or_create_keth_ruleset(anchor);
if (rs == NULL)
return (EINVAL);
if (rs->inactive.open)
/* We may be waiting for NET_EPOCH_CALL(pf_rollback_eth_cb) to
* finish. */
return (EBUSY);
}
/* Purge old inactive rules. */
TAILQ_FOREACH_SAFE(rule, &V_pf_keth_inactive->rules, entries, tmp) {
TAILQ_REMOVE(&V_pf_keth_inactive->rules, rule, entries);
TAILQ_FOREACH_SAFE(rule, rs->inactive.rules, entries,
tmp) {
TAILQ_REMOVE(rs->inactive.rules, rule,
entries);
pf_free_eth_rule(rule);
}
*ticket = ++V_pf_keth_inactive->ticket;
V_pf_keth_inactive->open = 1;
*ticket = ++rs->inactive.ticket;
rs->inactive.open = 1;
return (0);
}
@ -728,38 +734,46 @@ pf_begin_eth(uint32_t *ticket)
static void
pf_rollback_eth_cb(struct epoch_context *ctx)
{
struct pf_keth_settings *settings;
struct pf_keth_ruleset *rs;
settings = __containerof(ctx, struct pf_keth_settings, epoch_ctx);
rs = __containerof(ctx, struct pf_keth_ruleset, epoch_ctx);
CURVNET_SET(settings->vnet);
MPASS(settings == V_pf_keth_inactive);
CURVNET_SET(rs->vnet);
PF_RULES_WLOCK();
pf_rollback_eth(V_pf_keth_inactive->ticket);
pf_rollback_eth(rs->inactive.ticket,
rs->anchor ? rs->anchor->path : "");
PF_RULES_WUNLOCK();
CURVNET_RESTORE();
}
static int
pf_rollback_eth(uint32_t ticket)
pf_rollback_eth(uint32_t ticket, const char *anchor)
{
struct pf_keth_rule *rule, *tmp;
struct pf_keth_ruleset *rs;
PF_RULES_WASSERT();
if (!V_pf_keth_inactive->open || ticket != V_pf_keth_inactive->ticket)
rs = pf_find_keth_ruleset(anchor);
if (rs == NULL)
return (EINVAL);
if (!rs->inactive.open ||
ticket != rs->inactive.ticket)
return (0);
/* Purge old inactive rules. */
TAILQ_FOREACH_SAFE(rule, &V_pf_keth_inactive->rules, entries, tmp) {
TAILQ_REMOVE(&V_pf_keth_inactive->rules, rule, entries);
TAILQ_FOREACH_SAFE(rule, rs->inactive.rules, entries,
tmp) {
TAILQ_REMOVE(rs->inactive.rules, rule, entries);
pf_free_eth_rule(rule);
}
V_pf_keth_inactive->open = 0;
rs->inactive.open = 0;
pf_remove_if_empty_keth_ruleset(rs);
return (0);
}
@ -773,7 +787,7 @@ pf_rollback_eth(uint32_t ticket)
} while (0)
static void
pf_eth_calc_skip_steps(struct pf_keth_rules *rules)
pf_eth_calc_skip_steps(struct pf_keth_ruleq *rules)
{
struct pf_keth_rule *cur, *prev, *head[PFE_SKIP_COUNT];
int i;
@ -802,26 +816,32 @@ pf_eth_calc_skip_steps(struct pf_keth_rules *rules)
}
static int
pf_commit_eth(uint32_t ticket)
pf_commit_eth(uint32_t ticket, const char *anchor)
{
struct pf_keth_settings *settings;
struct pf_keth_ruleq *rules;
struct pf_keth_ruleset *rs;
if (!V_pf_keth_inactive->open ||
ticket != V_pf_keth_inactive->ticket)
rs = pf_find_keth_ruleset(anchor);
if (rs == NULL) {
return (EINVAL);
}
if (!rs->inactive.open ||
ticket != rs->inactive.ticket)
return (EBUSY);
PF_RULES_WASSERT();
pf_eth_calc_skip_steps(&V_pf_keth_inactive->rules);
pf_eth_calc_skip_steps(rs->inactive.rules);
settings = V_pf_keth;
ck_pr_store_ptr(&V_pf_keth, V_pf_keth_inactive);
V_pf_keth_inactive = settings;
V_pf_keth_inactive->ticket = V_pf_keth->ticket;
rules = rs->active.rules;
ck_pr_store_ptr(&rs->active.rules, rs->inactive.rules);
rs->inactive.rules = rules;
rs->inactive.ticket = rs->active.ticket;
/* Clean up inactive rules (i.e. previously active rules), only when
* we're sure they're no longer used. */
NET_EPOCH_CALL(pf_rollback_eth_cb, &V_pf_keth_inactive->epoch_ctx);
NET_EPOCH_CALL(pf_rollback_eth_cb, &rs->epoch_ctx);
return (0);
}
@ -2475,7 +2495,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
int cpu;
hook_pf();
if (! TAILQ_EMPTY(&V_pf_keth->rules))
if (! TAILQ_EMPTY(V_pf_keth->active.rules))
hook_pf_eth();
V_pf_status.running = 1;
V_pf_status.since = time_second;
@ -2505,21 +2525,55 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
nvlist_t *nvl;
void *packed;
struct pf_keth_rule *tail;
struct pf_keth_ruleset *rs;
u_int32_t ticket, nr;
const char *anchor = "";
nvl = NULL;
packed = NULL;
#define ERROUT(x) do { error = (x); goto DIOCGETETHRULES_error; } while (0)
if (nv->len > pf_ioctl_maxcount)
ERROUT(ENOMEM);
/* Copy the request in */
packed = malloc(nv->len, M_NVLIST, M_WAITOK);
if (packed == NULL)
ERROUT(ENOMEM);
error = copyin(nv->data, packed, nv->len);
if (error)
ERROUT(error);
nvl = nvlist_unpack(packed, nv->len, 0);
if (nvl == NULL)
ERROUT(EBADMSG);
if (! nvlist_exists_string(nvl, "anchor"))
ERROUT(EBADMSG);
anchor = nvlist_get_string(nvl, "anchor");
rs = pf_find_keth_ruleset(anchor);
nvlist_destroy(nvl);
nvl = NULL;
free(packed, M_NVLIST);
packed = NULL;
if (rs == NULL)
ERROUT(ENOENT);
/* Reply */
nvl = nvlist_create(0);
if (nvl == NULL)
ERROUT(ENOMEM);
PF_RULES_RLOCK();
ticket = V_pf_keth->ticket;
tail = TAILQ_LAST(&V_pf_keth->rules, pf_keth_rules);
ticket = rs->active.ticket;
tail = TAILQ_LAST(rs->active.rules, pf_keth_ruleq);
if (tail)
nr = tail->nr + 1;
else
@ -2543,7 +2597,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
#undef ERROUT
DIOCGETETHRULES_error:
free(packed, M_TEMP);
free(packed, M_NVLIST);
nvlist_destroy(nvl);
break;
}
@ -2554,8 +2608,10 @@ DIOCGETETHRULES_error:
nvlist_t *nvl = NULL;
void *nvlpacked = NULL;
struct pf_keth_rule *rule = NULL;
struct pf_keth_ruleset *rs;
u_int32_t ticket, nr;
bool clear = false;
const char *anchor;
#define ERROUT(x) do { error = (x); goto DIOCGETETHRULE_error; } while (0)
@ -2571,6 +2627,9 @@ DIOCGETETHRULES_error:
if (! nvlist_exists_number(nvl, "ticket"))
ERROUT(EBADMSG);
ticket = nvlist_get_number(nvl, "ticket");
if (! nvlist_exists_string(nvl, "anchor"))
ERROUT(EBADMSG);
anchor = nvlist_get_string(nvl, "anchor");
if (nvlist_exists_bool(nvl, "clear"))
clear = nvlist_get_bool(nvl, "clear");
@ -2582,6 +2641,17 @@ DIOCGETETHRULES_error:
ERROUT(EBADMSG);
nr = nvlist_get_number(nvl, "nr");
PF_RULES_RLOCK();
rs = pf_find_keth_ruleset(anchor);
if (rs == NULL) {
PF_RULES_RUNLOCK();
ERROUT(ENOENT);
}
if (ticket != rs->active.ticket) {
PF_RULES_RUNLOCK();
ERROUT(EBUSY);
}
nvlist_destroy(nvl);
nvl = NULL;
free(nvlpacked, M_TEMP);
@ -2589,12 +2659,7 @@ DIOCGETETHRULES_error:
nvl = nvlist_create(0);
PF_RULES_RLOCK();
if (ticket != V_pf_keth->ticket) {
PF_RULES_RUNLOCK();
ERROUT(EBUSY);
}
rule = TAILQ_FIRST(&V_pf_keth->rules);
rule = TAILQ_FIRST(rs->active.rules);
while ((rule != NULL) && (rule->nr != nr))
rule = TAILQ_NEXT(rule, entries);
if (rule == NULL) {
@ -2605,6 +2670,8 @@ DIOCGETETHRULES_error:
NET_EPOCH_ENTER(et);
PF_RULES_RUNLOCK();
nvl = pf_keth_rule_to_nveth_rule(rule);
if (pf_keth_anchor_nvcopyout(rs, rule, nvl))
ERROUT(EBUSY);
NET_EPOCH_EXIT(et);
if (nvl == NULL)
ERROUT(ENOMEM);
@ -2638,8 +2705,10 @@ DIOCGETETHRULE_error:
struct pfioc_nv *nv = (struct pfioc_nv *)addr;
nvlist_t *nvl = NULL;
void *nvlpacked = NULL;
struct pf_keth_rule *rule = NULL;
struct pf_keth_rule *rule = NULL, *tail = NULL;
struct pf_keth_ruleset *ruleset = NULL;
struct pfi_kkif *kif = NULL;
const char *anchor = "", *anchor_call = "";
#define ERROUT(x) do { error = (x); goto DIOCADDETHRULE_error; } while (0)
@ -2658,12 +2727,21 @@ DIOCGETETHRULE_error:
if (! nvlist_exists_number(nvl, "ticket"))
ERROUT(EBADMSG);
if (nvlist_exists_string(nvl, "anchor"))
anchor = nvlist_get_string(nvl, "anchor");
if (nvlist_exists_string(nvl, "anchor_call"))
anchor_call = nvlist_get_string(nvl, "anchor_call");
ruleset = pf_find_keth_ruleset(anchor);
if (ruleset == NULL)
ERROUT(EINVAL);
if (nvlist_get_number(nvl, "ticket") !=
V_pf_keth_inactive->ticket) {
ruleset->inactive.ticket) {
DPFPRINTF(PF_DEBUG_MISC,
("ticket: %d != %d\n",
(u_int32_t)nvlist_get_number(nvl, "ticket"),
V_pf_keth_inactive->ticket));
ruleset->inactive.ticket));
ERROUT(EBUSY);
}
@ -2710,7 +2788,19 @@ DIOCGETETHRULE_error:
ERROUT(error);
}
TAILQ_INSERT_TAIL(&V_pf_keth_inactive->rules, rule, entries);
if (pf_keth_anchor_setup(rule, ruleset, anchor_call)) {
pf_free_eth_rule(rule);
PF_RULES_WUNLOCK();
ERROUT(EINVAL);
}
tail = TAILQ_LAST(ruleset->inactive.rules, pf_keth_ruleq);
if (tail)
rule->nr = tail->nr + 1;
else
rule->nr = 0;
TAILQ_INSERT_TAIL(ruleset->inactive.rules, rule, entries);
PF_RULES_WUNLOCK();
@ -4765,13 +4855,7 @@ DIOCCHANGEADDR_error:
ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
switch (ioe->rs_num) {
case PF_RULESET_ETH:
if (ioe->anchor[0]) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
error = EINVAL;
goto fail;
}
if ((error = pf_begin_eth(&ioe->ticket))) {
if ((error = pf_begin_eth(&ioe->ticket, ioe->anchor))) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
goto fail;
@ -4852,13 +4936,8 @@ DIOCCHANGEADDR_error:
ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
switch (ioe->rs_num) {
case PF_RULESET_ETH:
if (ioe->anchor[0]) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
error = EINVAL;
goto fail;
}
if ((error = pf_rollback_eth(ioe->ticket))) {
if ((error = pf_rollback_eth(ioe->ticket,
ioe->anchor))) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
goto fail; /* really bad */
@ -4913,6 +4992,7 @@ DIOCCHANGEADDR_error:
struct pfioc_trans *io = (struct pfioc_trans *)addr;
struct pfioc_trans_e *ioe, *ioes;
struct pf_kruleset *rs;
struct pf_keth_ruleset *ers;
size_t totlen;
int i;
@ -4942,19 +5022,14 @@ DIOCCHANGEADDR_error:
ioe->anchor[sizeof(ioe->anchor) - 1] = 0;
switch (ioe->rs_num) {
case PF_RULESET_ETH:
if (ioe->anchor[0]) {
ers = pf_find_keth_ruleset(ioe->anchor);
if (ers == NULL || ioe->ticket == 0 ||
ioe->ticket != ers->inactive.ticket) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
error = EINVAL;
goto fail;
}
if (!V_pf_keth_inactive->ticket ||
ioe->ticket != V_pf_keth_inactive->ticket) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
error = EBUSY;
goto fail;
}
break;
#ifdef ALTQ
case PF_RULESET_ALTQ:
@ -5008,7 +5083,7 @@ DIOCCHANGEADDR_error:
for (i = 0, ioe = ioes; i < io->size; i++, ioe++) {
switch (ioe->rs_num) {
case PF_RULESET_ETH:
if ((error = pf_commit_eth(ioe->ticket))) {
if ((error = pf_commit_eth(ioe->ticket, ioe->anchor))) {
PF_RULES_WUNLOCK();
free(ioes, M_TEMP);
goto fail; /* really bad */
@ -5051,7 +5126,7 @@ DIOCCHANGEADDR_error:
PF_RULES_WUNLOCK();
/* Only hook into EtherNet taffic if we've got rules for it. */
if (! TAILQ_EMPTY(&V_pf_keth->rules))
if (! TAILQ_EMPTY(V_pf_keth->active.rules))
hook_pf_eth();
else
dehook_pf_eth();
@ -5962,11 +6037,11 @@ shutdown_pf(void)
if ((error = pf_clear_tables()) != 0)
break;
if ((error = pf_begin_eth(&t[0])) != 0) {
if ((error = pf_begin_eth(&t[0], &nn)) != 0) {
DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: eth\n"));
break;
}
pf_commit_eth(t[0]);
pf_commit_eth(t[0], &nn);
#ifdef ALTQ
if ((error = pf_begin_altq(&t[0])) != 0) {
@ -6250,9 +6325,7 @@ pf_load_vnet(void)
PF_QUEUE_TAG_HASH_SIZE_DEFAULT);
#endif
V_pf_keth = malloc(sizeof(*V_pf_keth), M_PFRULE, M_WAITOK);
V_pf_keth_inactive = malloc(sizeof(*V_pf_keth_inactive),
M_PFRULE, M_WAITOK);
V_pf_keth = &V_pf_main_keth_anchor.ruleset;
pfattach_vnet();
V_pf_vnet_active = 1;
@ -6364,9 +6437,6 @@ pf_unload_vnet(void)
pf_counter_u64_deinit(&V_pf_status.fcounters[i]);
for (int i = 0; i < SCNT_MAX; i++)
counter_u64_free(V_pf_status.scounters[i]);
free(V_pf_keth, M_PFRULE);
free(V_pf_keth_inactive, M_PFRULE);
}
static void

View file

@ -1084,6 +1084,9 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
nvlist_add_number(nvl, "dnpipe", krule->dnpipe);
nvlist_add_number(nvl, "dnflags", krule->dnflags);
nvlist_add_number(nvl, "anchor_relative", krule->anchor_relative);
nvlist_add_number(nvl, "anchor_wildcard", krule->anchor_wildcard);
nvlist_add_number(nvl, "action", krule->action);
return (nvl);

View file

@ -70,15 +70,22 @@ __FBSDID("$FreeBSD$");
VNET_DEFINE(struct pf_kanchor_global, pf_anchors);
VNET_DEFINE(struct pf_kanchor, pf_main_anchor);
VNET_DEFINE(struct pf_keth_settings*, pf_keth);
VNET_DEFINE(struct pf_keth_settings*, pf_keth_inactive);
VNET_DEFINE(struct pf_keth_ruleset*, pf_keth);
VNET_DEFINE(struct pf_keth_anchor, pf_main_keth_anchor);
VNET_DEFINE(struct pf_keth_anchor_global, pf_keth_anchors);
static __inline int pf_kanchor_compare(struct pf_kanchor *,
struct pf_kanchor *);
static __inline int pf_keth_anchor_compare(struct pf_keth_anchor *,
struct pf_keth_anchor *);
static struct pf_kanchor *pf_find_kanchor(const char *);
RB_GENERATE(pf_kanchor_global, pf_kanchor, entry_global, pf_kanchor_compare);
RB_GENERATE(pf_kanchor_node, pf_kanchor, entry_node, pf_kanchor_compare);
RB_GENERATE(pf_keth_anchor_global, pf_keth_anchor, entry_global,
pf_keth_anchor_compare);
RB_GENERATE(pf_keth_anchor_node, pf_keth_anchor, entry_node,
pf_keth_anchor_compare);
static __inline int
pf_kanchor_compare(struct pf_kanchor *a, struct pf_kanchor *b)
@ -88,6 +95,14 @@ pf_kanchor_compare(struct pf_kanchor *a, struct pf_kanchor *b)
return (c ? (c < 0 ? -1 : 1) : 0);
}
static __inline int
pf_keth_anchor_compare(struct pf_keth_anchor *a, struct pf_keth_anchor *b)
{
int c = strcmp(a->path, b->path);
return (c ? (c < 0 ? -1 : 1) : 0);
}
int
pf_get_ruleset_number(u_int8_t action)
{
@ -148,13 +163,18 @@ pf_init_kruleset(struct pf_kruleset *ruleset)
}
void
pf_init_keth(struct pf_keth_settings *settings)
pf_init_keth(struct pf_keth_ruleset *rs)
{
TAILQ_INIT(&settings->rules);
settings->ticket = 0;
settings->open = 0;
settings->vnet = curvnet;
bzero(rs, sizeof(*rs));
TAILQ_INIT(&rs->rules[0]);
TAILQ_INIT(&rs->rules[1]);
rs->active.rules = &rs->rules[0];
rs->active.open = 0;
rs->inactive.rules = &rs->rules[1];
rs->inactive.open = 0;
rs->vnet = curvnet;
}
struct pf_kruleset *
@ -396,6 +416,53 @@ done:
return (0);
}
int
pf_keth_anchor_nvcopyout(const struct pf_keth_ruleset *rs,
const struct pf_keth_rule *r, nvlist_t *nvl)
{
char anchor_call[MAXPATHLEN] = { 0 };
if (r->anchor == NULL)
goto done;
if (!r->anchor_relative) {
strlcpy(anchor_call, "/", sizeof(anchor_call));
strlcat(anchor_call, r->anchor->path,
sizeof(anchor_call));
} else {
char a[MAXPATHLEN];
char *p;
int i;
if (rs->anchor == NULL)
a[0] = 0;
else
strlcpy(a, rs->anchor->path, MAXPATHLEN);
for (i = 1; i < r->anchor_relative; ++i) {
if ((p = strrchr(a, '/')) == NULL)
p = a;
*p = 0;
strlcat(anchor_call, "../",
sizeof(anchor_call));
}
if (strncmp(a, r->anchor->path, strlen(a))) {
printf("%s(): '%s' '%s'\n", __func__, a,
r->anchor->path);
return (1);
}
if (strlen(r->anchor->path) > strlen(a))
strlcat(anchor_call, r->anchor->path + (a[0] ?
strlen(a) + 1 : 0), sizeof(anchor_call));
}
if (r->anchor_wildcard)
strlcat(anchor_call, anchor_call[0] ? "/*" : "*",
sizeof(anchor_call));
done:
nvlist_add_string(nvl, "anchor_call", anchor_call);
return (0);
}
int
pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r,
struct pfioc_rule *pr)
@ -456,3 +523,229 @@ pf_kanchor_remove(struct pf_krule *r)
pf_remove_if_empty_kruleset(&r->anchor->ruleset);
r->anchor = NULL;
}
struct pf_keth_ruleset *
pf_find_keth_ruleset(const char *path)
{
struct pf_keth_anchor *anchor;
while (*path == '/')
path++;
if (!*path)
return (V_pf_keth);
anchor = pf_find_keth_anchor(path);
if (anchor == NULL)
return (NULL);
else
return (&anchor->ruleset);
}
static struct pf_keth_anchor *
_pf_find_keth_anchor(struct pf_keth_ruleset *rs, const char *path)
{
struct pf_keth_anchor *key, *found;
key = (struct pf_keth_anchor *)rs_malloc(sizeof(*key));
if (key == NULL)
return (NULL);
strlcpy(key->path, path, sizeof(key->path));
found = RB_FIND(pf_keth_anchor_global, &V_pf_keth_anchors, key);
rs_free(key);
return (found);
}
struct pf_keth_anchor *
pf_find_keth_anchor(const char *path)
{
return (_pf_find_keth_anchor(V_pf_keth, path));
}
struct pf_keth_ruleset *
pf_find_or_create_keth_ruleset(const char *path)
{
char *p, *q, *r;
struct pf_keth_anchor *anchor = NULL, *dup = NULL, *parent = NULL;
struct pf_keth_ruleset *ruleset;
if (path[0] == 0)
return (V_pf_keth);
while (*path == '/')
path++;
ruleset = pf_find_keth_ruleset(path);
if (ruleset != NULL)
return (ruleset);
p = (char *)rs_malloc(MAXPATHLEN);
if (p == NULL)
return (NULL);
strlcpy(p, path, MAXPATHLEN);
while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
*q = 0;
if ((ruleset = pf_find_keth_ruleset(p)) != NULL) {
parent = ruleset->anchor;
break;
}
}
if (q == NULL)
q = p;
else
q++;
strlcpy(p, path, MAXPATHLEN);
if (!*q) {
rs_free(p);
return (NULL);
}
while ((r = strchr(q, '/')) != NULL || *q) {
if (r != NULL)
*r = 0;
if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
(parent != NULL && strlen(parent->path) >=
MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
rs_free(p);
return (NULL);
}
anchor = (struct pf_keth_anchor *)rs_malloc(sizeof(*anchor));
if (anchor == NULL) {
rs_free(p);
return (NULL);
}
RB_INIT(&anchor->children);
strlcpy(anchor->name, q, sizeof(anchor->name));
if (parent != NULL) {
strlcpy(anchor->path, parent->path,
sizeof(anchor->path));
strlcat(anchor->path, "/", sizeof(anchor->path));
}
strlcat(anchor->path, anchor->name, sizeof(anchor->path));
if ((dup = RB_INSERT(pf_keth_anchor_global, &V_pf_keth_anchors, anchor)) !=
NULL) {
printf("%s: RB_INSERT1 "
"'%s' '%s' collides with '%s' '%s'\n", __func__,
anchor->path, anchor->name, dup->path, dup->name);
rs_free(anchor);
rs_free(p);
return (NULL);
}
if (parent != NULL) {
anchor->parent = parent;
if ((dup = RB_INSERT(pf_keth_anchor_node, &parent->children,
anchor)) != NULL) {
printf("%s: "
"RB_INSERT2 '%s' '%s' collides with "
"'%s' '%s'\n", __func__, anchor->path,
anchor->name, dup->path, dup->name);
RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors,
anchor);
rs_free(anchor);
rs_free(p);
return (NULL);
}
}
pf_init_keth(&anchor->ruleset);
anchor->ruleset.anchor = anchor;
parent = anchor;
if (r != NULL)
q = r + 1;
else
*q = 0;
}
rs_free(p);
return (&anchor->ruleset);
}
int
pf_keth_anchor_setup(struct pf_keth_rule *r, const struct pf_keth_ruleset *s,
const char *name)
{
char *p, *path;
struct pf_keth_ruleset *ruleset;
r->anchor = NULL;
r->anchor_relative = 0;
r->anchor_wildcard = 0;
if (!name[0])
return (0);
path = (char *)rs_malloc(MAXPATHLEN);
if (path == NULL)
return (1);
if (name[0] == '/')
strlcpy(path, name + 1, MAXPATHLEN);
else {
/* relative path */
r->anchor_relative = 1;
if (s->anchor == NULL || !s->anchor->path[0])
path[0] = 0;
else
strlcpy(path, s->anchor->path, MAXPATHLEN);
while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
if (!path[0]) {
DPFPRINTF("pf_anchor_setup: .. beyond root\n");
rs_free(path);
return (1);
}
if ((p = strrchr(path, '/')) != NULL)
*p = 0;
else
path[0] = 0;
r->anchor_relative++;
name += 3;
}
if (path[0])
strlcat(path, "/", MAXPATHLEN);
strlcat(path, name, MAXPATHLEN);
}
if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
r->anchor_wildcard = 1;
*p = 0;
}
ruleset = pf_find_or_create_keth_ruleset(path);
rs_free(path);
if (ruleset == NULL || ruleset->anchor == NULL) {
DPFPRINTF("pf_anchor_setup: ruleset\n");
return (1);
}
r->anchor = ruleset->anchor;
r->anchor->refcnt++;
return (0);
}
void
pf_keth_anchor_remove(struct pf_keth_rule *r)
{
if (r->anchor == NULL)
return;
if (r->anchor->refcnt <= 0) {
printf("%s: broken refcount\n", __func__);
r->anchor = NULL;
return;
}
if (!--r->anchor->refcnt)
pf_remove_if_empty_keth_ruleset(&r->anchor->ruleset);
r->anchor = NULL;
}
void
pf_remove_if_empty_keth_ruleset(struct pf_keth_ruleset *ruleset)
{
struct pf_keth_anchor *parent;
int i;
while (ruleset != NULL) {
if (ruleset == V_pf_keth || ruleset->anchor == NULL ||
!RB_EMPTY(&ruleset->anchor->children) ||
ruleset->anchor->refcnt > 0)
return;
for (i = 0; i < PF_RULESET_MAX; ++i)
if (!TAILQ_EMPTY(ruleset->active.rules) ||
!TAILQ_EMPTY(ruleset->inactive.rules) ||
ruleset->inactive.open)
return;
RB_REMOVE(pf_keth_anchor_global, &V_pf_keth_anchors, ruleset->anchor);
if ((parent = ruleset->anchor->parent) != NULL)
RB_REMOVE(pf_keth_anchor_node, &parent->children,
ruleset->anchor);
rs_free(ruleset->anchor);
if (parent == NULL)
return;
ruleset = &parent->ruleset;
}
}