On 21/02/18(Wed) 02:37, Henning Brauer wrote: > Here comes generic delay functionality for pf. > The manpage bits are missing for the moment, but it's really simple to > use: > > match in set delay 10000 > > delay is in ms. should I change the parser to explicitely require > "ms", as in "match in set delay 10000ms"? > I have a pool_sethardlimit as a "last resort" style upper limit of > delayed packets held, hardcoded to 10000 right now - does that need to > be tunable? if not - what value? I'll obviously make it a define at > least. > delay range is 1-65535ms - is 65s too excessive, aka do we need to > impose a lower upper limit? > > I would welcome a few tests (and then oks from ppl with the right @) so > that we can go further once the basic functionality is in. And yes, > things like a delay range with random value in between chosen can be > added later. Getting the basic delay machinery, foremost in if.c, right > is the main goal now, the pf side is relatively easy to twiddle with.
I'd suggest moving the pool allocation and the function in net/pf*.c and only have a function call under #if NPF > 0. More comments inline. > Index: sys/net/if.c > =================================================================== > RCS file: /cvs/src/sys/net/if.c,v > retrieving revision 1.547 > diff -u -p -r1.547 if.c > --- sys/net/if.c 20 Feb 2018 03:46:45 -0000 1.547 > +++ sys/net/if.c 21 Feb 2018 01:26:37 -0000 > @@ -127,6 +127,10 @@ > > #if NPF > 0 > #include <net/pfvar.h> > +#include <sys/pool.h> > + > +#define IF_TIMEOUTPL_LIMIT 10000 > +struct pool iftimeopl; I'd suggest defining your own structure containing a timeout and a mbuf pointer instead of abusing ph_cookie. Since you're already allocating something it doesn't matter much and you're code won't be broken by a future refactoring. > #endif > > void if_attachsetup(struct ifnet *); > @@ -164,6 +168,10 @@ void ifa_print_all(void); > > void if_qstart_compat(struct ifqueue *); > > +#if NPF > 0 > +void if_enqueue_delayed(void *); > +#endif > + > /* > * interface index map > * > @@ -261,6 +269,14 @@ ifinit(void) > } > > net_tick(&net_tick_to); > + > +#if NPF > 0 > + pool_init(&iftimeopl, sizeof(struct timeout), 0, IPL_SOFTNET, 0, > + "iftimeout", NULL); > + pool_sethardlimit(&iftimeopl, IF_TIMEOUTPL_LIMIT, "iftimeout pool " > + "limit exceeded, dropping packets", 60); > +/* XXX hardlimit as last resort safeguard, limit? */ > +#endif > } > > static struct if_idxmap if_idxmap = { > @@ -674,12 +690,44 @@ if_qstart_compat(struct ifqueue *ifq) > KERNEL_UNLOCK(); > } > > +#if NPF > 0 > +void > +if_enqueue_delayed(void *arg) > +{ > + struct mbuf *m = arg; > + struct ifnet *ifp; > + struct timeout *to = m->m_pkthdr.ph_cookie; > + > + ifp = if_get(m->m_pkthdr.ph_ifidx); > + if (ifp != NULL) { > + if_enqueue(ifp, m); > + if_put(ifp); > + } You're leaking a mbuf if the interface has been detached. > + pool_put(&iftimeopl, to); > +} > +#endif > + > int > if_enqueue(struct ifnet *ifp, struct mbuf *m) > { > unsigned int idx; > struct ifqueue *ifq; > int error; > + > +#if NPF > 0 > + if (m->m_pkthdr.pf.delay > 0) { > + struct timeout *to; > + > + if ((to = pool_get(&iftimeopl, PR_NOWAIT)) == NULL) > + return (ENOBUFS); > + timeout_set(to, if_enqueue_delayed, m); > + timeout_add_msec(to, m->m_pkthdr.pf.delay); > + m->m_pkthdr.pf.delay = 0; > + m->m_pkthdr.ph_cookie = to; > + m->m_pkthdr.ph_ifidx = ifp->if_index; > + return (0); > + } > +#endif > > #if NBRIDGE > 0 > if (ifp->if_bridgeport && (m->m_flags & M_PROTO1) == 0) { > Index: sys/net/pf.c > =================================================================== > RCS file: /cvs/src/sys/net/pf.c,v > retrieving revision 1.1061 > diff -u -p -r1.1061 pf.c > --- sys/net/pf.c 18 Feb 2018 21:45:30 -0000 1.1061 > +++ sys/net/pf.c 21 Feb 2018 01:16:00 -0000 > @@ -3484,6 +3484,8 @@ pf_rule_to_actions(struct pf_rule *r, st > a->set_prio[0] = r->set_prio[0]; > a->set_prio[1] = r->set_prio[1]; > } > + if (r->rule_flag & PFRULE_SETDELAY) > + a->delay = r->delay; > } > > #define PF_TEST_ATTRIB(t, a) \ > @@ -3979,6 +3981,7 @@ pf_create_state(struct pf_pdesc *pd, str > #endif /* NPFSYNC > 0 */ > s->set_prio[0] = act->set_prio[0]; > s->set_prio[1] = act->set_prio[1]; > + s->delay = act->delay; > SLIST_INIT(&s->src_nodes); > > switch (pd->proto) { > @@ -7025,6 +7028,7 @@ done: > if (s->state_flags & PFSTATE_SETPRIO) > pd.m->m_pkthdr.pf.prio = s->set_prio[0]; > } > + pd.m->m_pkthdr.pf.delay = s->delay; > } else { > pf_scrub(pd.m, r->scrub_flags, pd.af, r->min_ttl, > r->set_tos); > @@ -7037,6 +7041,7 @@ done: > if (r->scrub_flags & PFSTATE_SETPRIO) > pd.m->m_pkthdr.pf.prio = r->set_prio[0]; > } > + pd.m->m_pkthdr.pf.delay = r->delay; > } > } > > Index: sys/net/pfvar.h > =================================================================== > RCS file: /cvs/src/sys/net/pfvar.h,v > retrieving revision 1.476 > diff -u -p -r1.476 pfvar.h > --- sys/net/pfvar.h 9 Feb 2018 09:35:03 -0000 1.476 > +++ sys/net/pfvar.h 10 Feb 2018 01:24:43 -0000 > @@ -459,11 +459,12 @@ struct pf_rule_actions { > u_int16_t pqid; > u_int16_t max_mss; > u_int16_t flags; > + u_int16_t delay; > u_int8_t log; > u_int8_t set_tos; > u_int8_t min_ttl; > u_int8_t set_prio[2]; > - u_int8_t pad[3]; > + u_int8_t pad[1]; > }; > > union pf_rule_ptr { > @@ -546,6 +547,7 @@ struct pf_rule { > u_int16_t tag; > u_int16_t match_tag; > u_int16_t scrub_flags; > + u_int16_t delay; > > struct pf_rule_uid uid; > struct pf_rule_gid gid; > @@ -605,8 +607,9 @@ struct pf_rule { > #define PFRULE_RETURNICMP 0x0004 > #define PFRULE_RETURN 0x0008 > #define PFRULE_NOSYNC 0x0010 > -#define PFRULE_SRCTRACK 0x0020 /* track source states */ > -#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ > +#define PFRULE_SRCTRACK 0x0020 /* track source states */ > +#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ > +#define PFRULE_SETDELAY 0x0080 > > /* rule flags again */ > #define PFRULE_IFBOUND 0x00010000 /* if-bound */ > @@ -761,6 +764,7 @@ struct pf_state { > int32_t creation; > int32_t expire; > int32_t pfsync_time; > + int rtableid[2]; /* rtables stack and wire */ > u_int16_t qid; > u_int16_t pqid; > u_int16_t tag; > @@ -781,14 +785,13 @@ struct pf_state { > u_int8_t timeout; > u_int8_t sync_state; /* PFSYNC_S_x */ > u_int8_t sync_updates; > - int rtableid[2]; /* rtables stack and wire */ > u_int8_t min_ttl; > u_int8_t set_tos; > u_int8_t set_prio[2]; > u_int16_t max_mss; > u_int16_t if_index_in; > u_int16_t if_index_out; > - u_int8_t pad2[2]; > + u_int16_t delay; > }; > > /* > Index: sys/sys/mbuf.h > =================================================================== > RCS file: /cvs/src/sys/sys/mbuf.h,v > retrieving revision 1.235 > diff -u -p -r1.235 mbuf.h > --- sys/sys/mbuf.h 11 Feb 2018 00:24:13 -0000 1.235 > +++ sys/sys/mbuf.h 11 Feb 2018 04:47:44 -0000 > @@ -100,10 +100,11 @@ struct pkthdr_pf { > struct inpcb *inp; /* connected pcb for outgoing packet */ > u_int32_t qid; /* queue id */ > u_int16_t tag; /* tag id */ > + u_int16_t delay; /* delay packet by X ms */ > u_int8_t flags; > u_int8_t routed; > u_int8_t prio; > - u_int8_t pad[3]; > + u_int8_t pad; > }; > > /* pkthdr_pf.flags */ > Index: sbin/pfctl/parse.y > =================================================================== > RCS file: /cvs/src/sbin/pfctl/parse.y,v > retrieving revision 1.670 > diff -u -p -r1.670 parse.y > --- sbin/pfctl/parse.y 8 Feb 2018 09:15:46 -0000 1.670 > +++ sbin/pfctl/parse.y 9 Feb 2018 01:25:46 -0000 > @@ -236,6 +236,7 @@ struct filter_opts { > #define FOM_SETPRIO 0x0400 > #define FOM_ONCE 0x1000 > #define FOM_PRIO 0x2000 > +#define FOM_SETDELAY 0x4000 > struct node_uid *uid; > struct node_gid *gid; > struct node_if *rcv; > @@ -262,6 +263,7 @@ struct filter_opts { > u_int rtableid; > u_int8_t prio; > u_int8_t set_prio[2]; > + u_int16_t delay; > struct divertspec divert; > struct redirspec nat; > struct redirspec rdr; > @@ -477,7 +479,7 @@ int parseport(char *, struct range *r, i > %token BITMASK RANDOM SOURCEHASH ROUNDROBIN LEASTSTATES STATICPORT > PROBABILITY > %token WEIGHT BANDWIDTH FLOWS QUANTUM > %token QUEUE PRIORITY QLIMIT RTABLE RDOMAIN MINIMUM BURST PARENT > -%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT > +%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT DELAY > %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE > %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW MAXPKTRATE > %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE > @@ -978,6 +980,10 @@ anchorrule : ANCHOR anchorname dir quick > r.set_prio[1] = $9.set_prio[1]; > r.scrub_flags |= PFSTATE_SETPRIO; > } > + if ($9.marker & FOM_SETDELAY) { > + r.delay = $9.delay; > + r.rule_flag |= PFRULE_SETDELAY; > + } > > decide_address_family($8.src.host, &r.af); > decide_address_family($8.dst.host, &r.af); > @@ -1913,6 +1919,11 @@ pfrule : action dir logquick interface > r.rule_flag |= PFRULE_FRAGMENT; > r.allow_opts = $8.allowopts; > > + if ($8.marker & FOM_SETDELAY) { > + r.delay = $8.delay; > + r.rule_flag |= PFRULE_SETDELAY; > + } > + > decide_address_family($7.src.host, &r.af); > decide_address_family($7.dst.host, &r.af); > > @@ -2308,6 +2319,19 @@ filter_set : prio { > filter_opts.marker |= FOM_SETTOS; > filter_opts.settos = $2; > } > + | DELAY NUMBER { > + if (filter_opts.delay) { > + yyerror("delay cannot be respecified"); > + YYERROR; > + } > + if ($2 < 0 || $2 > 0xffff) { > + yyerror("illegal delay value %d (0-%u)", $2, > + 0xffff); > + YYERROR; > + } > + filter_opts.marker |= FOM_SETDELAY; > + filter_opts.delay = $2; > + } > ; > > prio : PRIO NUMBER { > @@ -5110,6 +5134,7 @@ lookup(char *s) > { "code", CODE}, > { "debug", DEBUG}, > { "default", DEFAULT}, > + { "delay", DELAY}, > { "divert-packet", DIVERTPACKET}, > { "divert-reply", DIVERTREPLY}, > { "divert-to", DIVERTTO}, > Index: sbin/pfctl/pfctl_parser.c > =================================================================== > RCS file: /cvs/src/sbin/pfctl/pfctl_parser.c,v > retrieving revision 1.319 > diff -u -p -r1.319 pfctl_parser.c > --- sbin/pfctl/pfctl_parser.c 8 Feb 2018 02:26:39 -0000 1.319 > +++ sbin/pfctl/pfctl_parser.c 9 Feb 2018 01:25:46 -0000 > @@ -871,30 +871,35 @@ print_rule(struct pf_rule *r, const char > printf(" max-pkt-rate %u/%u", r->pktrate.limit, > r->pktrate.seconds); > > - if (r->scrub_flags & PFSTATE_SETMASK || r->qname[0]) { > + if (r->scrub_flags & PFSTATE_SETMASK || r->qname[0] || > + r->rule_flag & PFRULE_SETDELAY) { > char *comma = ""; > printf(" set ("); > if (r->scrub_flags & PFSTATE_SETPRIO) { > if (r->set_prio[0] == r->set_prio[1]) > - printf("%s prio %u", comma, r->set_prio[0]); > + printf("%sprio %u", comma, r->set_prio[0]); > else > - printf("%s prio(%u, %u)", comma, r->set_prio[0], > + printf("%sprio(%u, %u)", comma, r->set_prio[0], > r->set_prio[1]); > - comma = ","; > + comma = ", "; > } > if (r->qname[0]) { > if (r->pqname[0]) > - printf("%s queue(%s, %s)", comma, r->qname, > + printf("%squeue(%s, %s)", comma, r->qname, > r->pqname); > else > - printf("%s queue %s", comma, r->qname); > - comma = ","; > + printf("%squeue %s", comma, r->qname); > + comma = ", "; > } > if (r->scrub_flags & PFSTATE_SETTOS) { > - printf("%s tos 0x%2.2x", comma, r->set_tos); > - comma = ","; > + printf("%stos 0x%2.2x", comma, r->set_tos); > + comma = ", "; > } > - printf(" )"); > + if (r->rule_flag & PFRULE_SETDELAY) { > + printf("%sdelay %u", comma, r->delay); > + comma = ", "; > + } > + printf(")"); > } > > ropts = 0; >