Index: lib/libpfctl/libpfctl.h =================================================================== --- lib/libpfctl/libpfctl.h +++ lib/libpfctl/libpfctl.h @@ -43,6 +43,7 @@ struct pf_rule_addr dst; union pf_rule_ptr skip[PF_SKIP_COUNT]; char label[PF_RULE_LABEL_SIZE]; + char schedule[PF_RULE_LABEL_SIZE]; char ifname[IFNAMSIZ]; char qname[PF_QNAME_SIZE]; char pqname[PF_QNAME_SIZE]; @@ -174,5 +175,6 @@ int libpfctl_add_rule(int dev, const struct pfctl_rule *r, const char *anchor, const char *anchor_call, u_int32_t ticket, u_int32_t pool_ticket); +int libpfctl_kill_schedule(int dev, const char *sched, int *killed); #endif Index: lib/libpfctl/libpfctl.c =================================================================== --- lib/libpfctl/libpfctl.c +++ lib/libpfctl/libpfctl.c @@ -291,6 +291,9 @@ rule->skip[i].nr = skip[i]; strlcpy(rule->label, nvlist_get_string(nvl, "label"), PF_RULE_LABEL_SIZE); + if (nvlist_exists_string(nvl, "schedule")) + strlcpy(rule->schedule, nvlist_get_string(nvl, "schedule"), + PF_RULE_LABEL_SIZE); strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE); strlcpy(rule->pqname, nvlist_get_string(nvl, "pqname"), PF_QNAME_SIZE); @@ -395,6 +398,7 @@ pfctl_nv_add_rule_addr(nvlr, "dst", &r->dst); nvlist_add_string(nvlr, "label", r->label); + nvlist_add_string(nvlr, "schedule", r->schedule); nvlist_add_string(nvlr, "ifname", r->ifname); nvlist_add_string(nvlr, "qname", r->qname); nvlist_add_string(nvlr, "pqname", r->pqname); @@ -534,3 +538,40 @@ return (0); } + +int +libpfctl_kill_schedule(int dev, const char *sched, int *killed) +{ + struct pfioc_nv nv; + nvlist_t *nvl; + int ret; + + nvl = nvlist_create(0); + + nvlist_add_string(nvl, "schedule", sched); + + nv.data = nvlist_pack(nvl, &nv.len); + nv.size = nv.len; // Reply will be smaller than the request + + nvlist_destroy(nvl); + + ret = ioctl(dev, DIOCKILLSCHEDULE, &nv); + if (ret != 0) { + free(nv.data); + return (ret); + } + + nvl = nvlist_unpack(nv.data, nv.len, 0); + if (nvl == NULL) { + free(nv.data); + return (EIO); + } + + if (killed) + *killed = nvlist_get_number(nvl, "killed"); + + free(nv.data); + nvlist_destroy(nvl); + + return (0); +} Index: sbin/pfctl/parse.y =================================================================== --- sbin/pfctl/parse.y +++ sbin/pfctl/parse.y @@ -242,6 +242,7 @@ int fragment; int allowopts; char *label; + char *schedule; struct node_qassign queues; char *tag; char *match_tag; @@ -349,6 +350,7 @@ int check_rulestate(int); int getservice(char *); int rule_label(struct pfctl_rule *, char *); +int rule_schedule(struct pfctl_rule *, char *); int rt_tableid_max(void); void mv_rules(struct pfctl_ruleset *, struct pfctl_ruleset *); @@ -460,7 +462,7 @@ %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 +%token ANTISPOOF FOR INCLUDE SCHEDULE %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY %token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME %token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL @@ -499,7 +501,7 @@ %type gids gid_list gid_item %type route %type redirection redirpool -%type label stringall tag anchorname +%type label schedule stringall tag anchorname %type string varstring numberstring %type keep %type state_opt_spec state_opt_list state_opt_item @@ -2090,6 +2092,9 @@ if (rule_label(&r, $9.label)) YYERROR; free($9.label); + if (rule_schedule(&r, $9.schedule)) + YYERROR; + free($9.schedule); r.flags = $9.flags.b1; r.flagset = $9.flags.b2; if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { @@ -2533,6 +2538,13 @@ } filter_opts.label = $1; } + | schedule { + if (filter_opts.schedule) { + yyerror("schedule cannot be redefined"); + YYERROR; + } + filter_opts.schedule = $1; + } | qname { if (filter_opts.queues.qname) { yyerror("queue cannot be redefined"); @@ -3852,6 +3864,11 @@ } ; +schedule : SCHEDULE STRING { + $$ = $2; + } + ; + qname : QUEUE STRING { $$.qname = $2; $$.pqname = NULL; @@ -5260,6 +5277,7 @@ int added = 0, error = 0; char ifname[IF_NAMESIZE]; char label[PF_RULE_LABEL_SIZE]; + char schedule[PF_RULE_LABEL_SIZE]; char tagname[PF_TAG_NAME_SIZE]; char match_tagname[PF_TAG_NAME_SIZE]; struct pf_pooladdr *pa; @@ -5268,6 +5286,8 @@ if (strlcpy(label, r->label, sizeof(label)) >= sizeof(label)) errx(1, "expand_rule: strlcpy"); + if (strlcpy(schedule, r->schedule, sizeof(schedule)) >= sizeof(schedule)) + errx(1, "expand_rule: strlcpy"); if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname)) errx(1, "expand_rule: strlcpy"); if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >= @@ -5319,6 +5339,9 @@ if (strlcpy(r->label, label, sizeof(r->label)) >= sizeof(r->label)) errx(1, "expand_rule: strlcpy"); + if (strlcpy(r->schedule, schedule, sizeof(r->schedule)) >= + sizeof(r->schedule)) + errx(1, "expand_rule: strlcpy"); if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >= sizeof(r->tagname)) errx(1, "expand_rule: strlcpy"); @@ -5327,6 +5350,8 @@ errx(1, "expand_rule: strlcpy"); expand_label(r->label, PF_RULE_LABEL_SIZE, r->ifname, r->af, src_host, src_port, dst_host, dst_port, proto->proto); + expand_label(r->schedule, PF_RULE_LABEL_SIZE, r->ifname, r->af, + src_host, src_port, dst_host, dst_port, proto->proto); expand_label(r->tagname, PF_TAG_NAME_SIZE, r->ifname, r->af, src_host, src_port, dst_host, dst_port, proto->proto); expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r->ifname, @@ -5592,6 +5617,7 @@ { "rtable", RTABLE}, { "rule", RULE}, { "ruleset-optimization", RULESET_OPTIMIZATION}, + { "schedule", SCHEDULE}, { "scrub", SCRUB}, { "set", SET}, { "set-tos", SETTOS}, @@ -6227,6 +6253,20 @@ return (0); } +int +rule_schedule(struct pfctl_rule *r, char *s) +{ + if (s) { + if (strlcpy(r->schedule, s, sizeof(r->schedule)) >= + sizeof(r->schedule)) { + yyerror("rule schedule too long (max %d chars)", + sizeof(r->schedule)-1); + return (-1); + } + } + return (0); +} + u_int16_t parseicmpspec(char *w, sa_family_t af) { Index: sbin/pfctl/pfctl.8 =================================================================== --- sbin/pfctl/pfctl.8 +++ sbin/pfctl/pfctl.8 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 3, 2016 +.Dd April 9, 2021 .Dt PFCTL 8 .Os .Sh NAME @@ -56,6 +56,7 @@ .Op Ar address ... .Oc Xc .Op Fl x Ar level +.Op Fl y Ar schedule .Ek .Sh DESCRIPTION The @@ -658,6 +659,9 @@ .It Fl x Cm loud Generate debug messages for common conditions. .El +.It Fl y Ar schedule +Kill all states marked with +.Ar schedule . .It Fl z Clear per-rule statistics. .El Index: sbin/pfctl/pfctl.c =================================================================== --- sbin/pfctl/pfctl.c +++ sbin/pfctl/pfctl.c @@ -125,6 +125,7 @@ static const char *pf_device = "/dev/pf"; static char *ifaceopt; static char *tableopt; +static char *schedule; static const char *tblcmdopt; static int src_node_killers; static char *src_node_kill[2]; @@ -2122,7 +2123,7 @@ usage(); while ((ch = getopt(argc, argv, - "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) { + "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:y:z")) != -1) { switch (ch) { case 'a': anchoropt = optarg; @@ -2244,6 +2245,14 @@ } mode = O_RDWR; break; + case 'y': + if (schedule != NULL && strlen(schedule) > + PF_RULE_LABEL_SIZE) + errx(1, "Schedule label cannot be more than %d" + " characters\n", PF_RULE_LABEL_SIZE); + schedule = optarg; + mode = O_RDWR; + break; case 'z': opts |= PF_OPT_CLRRULECTRS; mode = O_RDWR; @@ -2447,6 +2456,17 @@ if (src_node_killers) pfctl_kill_src_nodes(dev, ifaceopt, opts); + if (schedule) { + int killed; + + if (libpfctl_kill_schedule(dev, schedule, &killed)) + err(1, "DIOCKILLSCHEDULE"); + + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "killed %d states from %s schedule label\n", + killed, schedule); + } + if (tblcmdopt != NULL) { error = pfctl_command_tables(argc, argv, tableopt, tblcmdopt, rulesopt, anchorname, opts); Index: sbin/pfctl/pfctl_parser.c =================================================================== --- sbin/pfctl/pfctl_parser.c +++ sbin/pfctl/pfctl_parser.c @@ -1018,6 +1018,8 @@ } if (r->label[0]) printf(" label \"%s\"", r->label); + if (r->schedule[0]) + printf(" schedule \"%s\"", r->schedule); if (r->qname[0] && r->pqname[0]) printf(" queue(%s, %s)", r->qname, r->pqname); else if (r->qname[0]) Index: share/man/man5/pf.conf.5 =================================================================== --- share/man/man5/pf.conf.5 +++ share/man/man5/pf.conf.5 @@ -28,7 +28,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd December 7, 2019 +.Dd April 9, 2021 .Dt PF.CONF 5 .Os .Sh NAME @@ -1807,6 +1807,13 @@ The macro expansion for the .Ar label directive occurs only at configuration file parse time, not during runtime. +.It Ar schedule Aq Ar string +Adds a schedule label (name) to the rule, which can be used to identify and +terminate states created by matching rules, +using +.Bd -literal -offset indent +# pfctl -y string +.Ed .It Xo Ar queue Aq Ar queue .No \*(Ba ( Aq Ar queue , .Aq Ar queue ) @@ -2885,9 +2892,10 @@ "max-mss" number | "random-id" | "reassemble tcp" | fragmentation | "allow-opts" | "label" string | "tag" string | [ ! ] "tagged" string | - "set prio" ( number | "(" number [ [ "," ] number ] ")" ) | - "queue" ( string | "(" string [ [ "," ] string ] ")" ) | - "rtable" number | "probability" number"%" | "prio" number + "schedule" string | "set prio" ( number | "(" number [ [ "," ] + number ] ")" ) | "queue" ( string | "(" string [ [ "," ] + string ] ")" ) | "rtable" number | "probability" number"%" | + "prio" number nat-rule = [ "no" ] "nat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] [ "on" ifspec ] [ af ] Index: sys/net/pfvar.h =================================================================== --- sys/net/pfvar.h +++ sys/net/pfvar.h @@ -324,6 +324,7 @@ struct pf_rule_addr dst; union pf_krule_ptr skip[PF_SKIP_COUNT]; char label[PF_RULE_LABEL_SIZE]; + char schedule[PF_RULE_LABEL_SIZE]; char ifname[IFNAMSIZ]; char qname[PF_QNAME_SIZE]; char pqname[PF_QNAME_SIZE]; @@ -1299,6 +1300,8 @@ #define DIOCSETIFFLAG _IOWR('D', 89, struct pfioc_iface) #define DIOCCLRIFFLAG _IOWR('D', 90, struct pfioc_iface) #define DIOCKILLSRCNODES _IOWR('D', 91, struct pfioc_src_node_kill) +#define DIOCKILLSCHEDULE _IOWR('D', 92, struct pfioc_nv) + struct pf_ifspeed_v0 { char ifname[IFNAMSIZ]; u_int32_t baudrate; Index: sys/netpfil/pf/pf_ioctl.c =================================================================== --- sys/netpfil/pf/pf_ioctl.c +++ sys/netpfil/pf/pf_ioctl.c @@ -197,6 +197,7 @@ static int pf_clear_tables(void); static void pf_clear_srcnodes(struct pf_ksrc_node *); static void pf_kill_srcnodes(struct pfioc_src_node_kill *); +static int pf_kill_schedule(struct pfioc_nv *); static void pf_tbladdr_copyout(struct pf_addr_wrap *); /* @@ -1764,6 +1765,9 @@ &rule->dst)); PFNV_CHK(pf_nvstring(nvl, "label", rule->label, sizeof(rule->label))); + if (nvlist_exists_string(nvl, "schedule")) + PFNV_CHK(pf_nvstring(nvl, "schedule", rule->schedule, + sizeof(rule->schedule))); PFNV_CHK(pf_nvstring(nvl, "ifname", rule->ifname, sizeof(rule->ifname))); PFNV_CHK(pf_nvstring(nvl, "qname", rule->qname, sizeof(rule->qname))); @@ -1938,6 +1942,7 @@ } nvlist_add_string(nvl, "label", rule->label); + nvlist_add_string(nvl, "schedule", rule->schedule); nvlist_add_string(nvl, "ifname", rule->ifname); nvlist_add_string(nvl, "qname", rule->qname); nvlist_add_string(nvl, "pqname", rule->pqname); @@ -4372,6 +4377,10 @@ pf_kill_srcnodes((struct pfioc_src_node_kill *)addr); break; + case DIOCKILLSCHEDULE: + error = pf_kill_schedule((struct pfioc_nv *)addr); + break; + case DIOCSETHOSTID: { u_int32_t *hostid = (u_int32_t *)addr; @@ -4651,6 +4660,74 @@ psnk->psnk_killed = pf_free_src_nodes(&kill); } +static int +pf_kill_schedule(struct pfioc_nv *nv) +{ + nvlist_t *nvl = NULL; + void *nvlpacked = NULL; + const char *schedule; + struct pf_state *state; + int killed = 0; + int error = 0; + +#define ERROUT(x) do { error = (x); goto on_error; } while (0) + + if (nv->len > pf_ioctl_maxcount) + ERROUT(ENOMEM); + + nvlpacked = malloc(nv->len, M_TEMP, M_WAITOK); + if (nvlpacked == NULL) + ERROUT(ENOMEM); + + error = copyin(nv->data, nvlpacked, nv->len); + if (error) + ERROUT(error); + + nvl = nvlist_unpack(nvlpacked, nv->len, 0); + if (nvl == NULL) + ERROUT(EBADMSG); + + if (! nvlist_exists_string(nvl, "schedule")) + ERROUT(EBADMSG); + + schedule = nvlist_get_string(nvl, "schedule"); + + for (u_long i = 0; i <= pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + +relock_DIOCKILLSCHEDULE: + PF_HASHROW_LOCK(ih); + LIST_FOREACH(state, &ih->states, entry) { + if (!strcmp(schedule, state->rule.ptr->schedule)) { + pf_unlink_state(state, PF_ENTER_LOCKED); + killed++; + goto relock_DIOCKILLSCHEDULE; + } + } + PF_HASHROW_UNLOCK(ih); + } + + nvlist_destroy(nvl); + nvl = nvlist_create(0); + if (nvl == NULL) + ERROUT(ENOMEM); + + nvlist_add_number(nvl, "killed", killed); + free(nvlpacked, M_TEMP); + nvlpacked = nvlist_pack(nvl, &nv->len); + if (nvlpacked == NULL) + ERROUT(ENOMEM); + + error = copyout(nvlpacked, nv->data, nv->len); + +#undef ERROUT +on_error: + free(nvlpacked, M_TEMP); + nvlist_destroy(nvl); + + return (error); +} + /* * XXX - Check for version missmatch!!! */