diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -114,6 +114,9 @@ } max_src_conn_rate; u_int32_t qid; u_int32_t pqid; + u_int16_t dnpipe; + u_int16_t dnrpipe; + u_int32_t free_flags; u_int32_t nr; u_int32_t prob; uid_t cuid; diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -480,6 +480,9 @@ nvlist_get_number(nvl, "max_src_conn_rate.seconds"); rule->qid = nvlist_get_number(nvl, "qid"); rule->pqid = nvlist_get_number(nvl, "pqid"); + rule->dnpipe = nvlist_get_number(nvl, "dnpipe"); + rule->dnrpipe = nvlist_get_number(nvl, "dnrpipe"); + rule->free_flags = nvlist_get_number(nvl, "dnflags"); rule->prob = nvlist_get_number(nvl, "prob"); rule->cuid = nvlist_get_number(nvl, "cuid"); rule->cpid = nvlist_get_number(nvl, "cpid"); @@ -584,6 +587,9 @@ r->max_src_conn_rate.limit); nvlist_add_number(nvlr, "max_src_conn_rate.seconds", r->max_src_conn_rate.seconds); + nvlist_add_number(nvlr, "dnpipe", r->dnpipe); + nvlist_add_number(nvlr, "dnrpipe", r->dnrpipe); + nvlist_add_number(nvlr, "dnflags", r->free_flags); nvlist_add_number(nvlr, "prob", r->prob); nvlist_add_number(nvlr, "cuid", r->cuid); nvlist_add_number(nvlr, "cpid", r->cpid); diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -248,6 +248,9 @@ char *tag; char *match_tag; u_int8_t match_tag_not; + u_int16_t dnpipe; + u_int16_t dnrpipe; + u_int32_t free_flags; u_int rtableid; u_int8_t prio; u_int8_t set_prio[2]; @@ -468,6 +471,7 @@ %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET %token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME %token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL +%token DNPIPE DNQUEUE %token LOAD RULESET_OPTIMIZATION PRIO %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY @@ -2464,6 +2468,15 @@ } #endif + if ($9.dnpipe || $9.dnrpipe) { + r.dnpipe = $9.dnpipe; + r.dnrpipe = $9.dnrpipe; + if ($9.free_flags & PFRULE_DN_IS_PIPE) + r.free_flags |= PFRULE_DN_IS_PIPE; + else + r.free_flags |= PFRULE_DN_IS_QUEUE; + } + expand_rule(&r, $4, $5.host, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, $9.uid, $9.gid, $9.icmpspec, ""); @@ -2565,6 +2578,32 @@ } filter_opts.queues = $1; } + | DNPIPE number { + filter_opts.dnpipe = $2; + filter_opts.free_flags |= PFRULE_DN_IS_PIPE; + } + | DNPIPE '(' number ')' { + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_PIPE; + } + | DNPIPE '(' number comma number ')' { + filter_opts.dnrpipe = $5; + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_PIPE; + } + | DNQUEUE number { + filter_opts.dnpipe = $2; + filter_opts.free_flags |= PFRULE_DN_IS_QUEUE; + } + | DNQUEUE '(' number comma number ')' { + filter_opts.dnrpipe = $5; + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_QUEUE; + } + | DNQUEUE '(' number ')' { + filter_opts.dnpipe = $3; + filter_opts.free_flags |= PFRULE_DN_IS_QUEUE; + } | TAG string { filter_opts.tag = $2; } @@ -5592,6 +5631,8 @@ { "debug", DEBUG}, { "divert-reply", DIVERTREPLY}, { "divert-to", DIVERTTO}, + { "dnpipe", DNPIPE}, + { "dnqueue", DNQUEUE}, { "drop", DROP}, { "drop-ovl", FRAGDROP}, { "dup-to", DUPTO}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -1018,6 +1018,15 @@ i = 0; while (r->label[i][0]) printf(" label \"%s\"", r->label[i++]); + /* Only dnrpipe as we might do (0, 42) to only queue return traffic. */ + if (r->dnrpipe) + printf(" %s(%d, %d)", + r->free_flags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue", + r->dnpipe, r->dnrpipe); + else if (r->dnpipe) + printf(" %s %d", + r->free_flags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue", + r->dnpipe); if (r->qname[0] && r->pqname[0]) printf(" queue(%s, %s)", r->qname, r->pqname); else if (r->qname[0]) diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -563,6 +563,9 @@ struct pf_rule_actions { uint16_t qid; uint16_t pqid; + uint16_t dnpipe; + uint16_t dnrpipe; /* Reverse direction pipe */ + uint32_t flags; }; union pf_krule_ptr { @@ -608,6 +611,9 @@ } max_src_conn_rate; u_int16_t qid; u_int16_t pqid; + u_int16_t dnpipe; + u_int16_t dnrpipe; + u_int32_t free_flags; u_int32_t nr; u_int32_t prob; uid_t cuid; @@ -755,6 +761,8 @@ /* was PFSTATE_PFLOW 0x04 */ #define PFSTATE_NOSYNC 0x08 #define PFSTATE_ACK 0x10 +#define PFRULE_DN_IS_PIPE 0x40 +#define PFRULE_DN_IS_QUEUE 0x80 #define PFSTATE_SETPRIO 0x0200 #define PFSTATE_SETMASK (PFSTATE_SETPRIO) @@ -858,6 +866,8 @@ u_int32_t pfsync_time; u_int16_t qid; u_int16_t pqid; + u_int16_t dnpipe; + u_int16_t dnrpipe; u_int16_t tag; u_int8_t log; }; diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c --- a/sys/netpfil/ipfw/ip_dn_io.c +++ b/sys/netpfil/ipfw/ip_dn_io.c @@ -841,7 +841,8 @@ m_tag_prepend(m, mtag); /* Attach to mbuf chain. */ dt = (struct dn_pkt_tag *)(mtag + 1); dt->rule = fwa->rule; - dt->rule.info &= IPFW_ONEPASS; /* only keep this info */ + /* only keep this info */ + dt->rule.info &= (IPFW_ONEPASS | IPFW_IS_DUMMYNET); dt->dn_dir = dir; dt->ifp = fwa->flags & IPFW_ARGS_OUT ? fwa->ifp : NULL; /* dt->output tame is updated as we move through */ diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -94,6 +94,13 @@ #include #include +/* dummynet */ +#include +#include +#include +#include +#include + #ifdef INET6 #include #include @@ -3310,6 +3317,12 @@ a->qid = r->qid; if (r->pqid) a->pqid = r->pqid; + if (r->dnpipe) + a->dnpipe = r->dnpipe; + if (r->dnrpipe) + a->dnpipe = r->dnrpipe; + if (r->free_flags & PFRULE_DN_IS_PIPE) + a->flags |= PFRULE_DN_IS_PIPE; } int @@ -3982,6 +3995,9 @@ s->sync_state = PFSYNC_S_NONE; s->qid = pd->act.qid; s->pqid = pd->act.pqid; + s->dnpipe = pd->act.dnpipe; + s->dnrpipe = pd->act.dnrpipe; + s->state_flags |= pd->act.flags; if (nr != NULL) s->log |= nr->log & PF_LOG_ALL; switch (pd->proto) { @@ -6226,6 +6242,64 @@ return (0); } +static bool +pf_pdesc_to_dnflow(int dir, const struct pf_pdesc *pd, + const struct pf_krule *r, const struct pf_kstate *s, + struct ip_fw_args *dnflow) +{ + int dndir = r->direction; + + if (s && dndir == PF_INOUT) + dndir = s->direction; + + memset(dnflow, 0, sizeof(*dnflow)); + + if (pd->dport != NULL) + dnflow->f_id.dst_port = ntohs(*pd->dport); + if (pd->sport != NULL) + dnflow->f_id.src_port = ntohs(*pd->sport); + + if (dir == PF_IN) + dnflow->flags |= IPFW_ARGS_IN; + else + dnflow->flags |= IPFW_ARGS_OUT; + + if (dir != dndir && pd->act.dnrpipe) { + dnflow->rule.info = pd->act.dnrpipe; + } + else if (dir == dndir) { + dnflow->rule.info = pd->act.dnpipe; + } + else { + return (false); + } + + dnflow->rule.info |= IPFW_IS_DUMMYNET; + if (r->free_flags & PFRULE_DN_IS_PIPE) + dnflow->rule.info |= IPFW_IS_PIPE; + + dnflow->f_id.proto = pd->proto; + dnflow->f_id.extra = dnflow->rule.info; + switch (pd->af) { + case AF_INET: + dnflow->f_id.addr_type = 4; + dnflow->f_id.src_ip = ntohl(pd->src->v4.s_addr); + dnflow->f_id.dst_ip = ntohl(pd->dst->v4.s_addr); + break; + case AF_INET6: + dnflow->flags |= IPFW_ARGS_IP6; + dnflow->f_id.addr_type = 6; + dnflow->f_id.src_ip6 = pd->src->v6; + dnflow->f_id.dst_ip6 = pd->dst->v6; + break; + default: + panic("Invalid AF"); + break; + } + + return (true); +} + #ifdef INET int pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) @@ -6267,10 +6341,11 @@ PF_RULES_RLOCK(); - if (__predict_false(ip_divert_ptr != NULL) && + if ((__predict_false(ip_divert_ptr != NULL) || ip_dn_io_ptr != NULL) && ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1); - if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) { + if ((rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) || + (rr->info & IPFW_IS_DUMMYNET)) { if (pd.pf_mtag == NULL && ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { action = PF_DROP; @@ -6405,6 +6480,8 @@ log = action != PF_PASS; goto done; } + pd.sport = &pd.hdr.udp.uh_sport; + pd.dport = &pd.hdr.udp.uh_dport; if (pd.hdr.udp.uh_dport == 0 || ntohs(pd.hdr.udp.uh_ulen) > m->m_pkthdr.len - off || ntohs(pd.hdr.udp.uh_ulen) < sizeof(struct udphdr)) { @@ -6524,6 +6601,47 @@ } #endif /* ALTQ */ + if (s && (s->dnpipe || s->dnrpipe)) { + pd.act.dnpipe = s->dnpipe; + pd.act.dnrpipe = s->dnrpipe; + pd.act.flags = s->state_flags; + } else if (r->dnpipe || r->dnrpipe) { + pd.act.dnpipe = r->dnpipe; + pd.act.dnrpipe = r->dnrpipe; + pd.act.flags = r->free_flags; + } + if ((pd.act.dnpipe || pd.act.dnrpipe) && !PACKET_LOOPED(&pd)) { + if (ip_dn_io_ptr == NULL) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } else { + struct ip_fw_args dnflow; + + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + if (s) + PF_STATE_UNLOCK(s); + return (action); + } + + if (pf_pdesc_to_dnflow(dir, &pd, r, s, &dnflow)) { + ip_dn_io_ptr(m0, &dnflow); + + if (*m0 == NULL) { + if (s) + PF_STATE_UNLOCK(s); + return (action); + } else { + /* This is dummynet fast io processing */ + m_tag_delete(*m0, m_tag_first(*m0)); + pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; + } + } + } + } + /* * connections redirected to loopback should not match sockets * bound specifically to loopback due to security implications, @@ -6684,6 +6802,7 @@ u_short action, reason = 0, log = 0; struct mbuf *m = *m0, *n = NULL; struct m_tag *mtag; + struct m_tag *ipfwtag; struct ip6_hdr *h = NULL; struct pf_krule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; struct pf_kstate *s = NULL; @@ -6719,7 +6838,19 @@ PF_RULES_RLOCK(); /* We do IP header normalization and packet reassembly here */ - if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { + if (ip_dn_io_ptr != NULL && + ((ipfwtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { + struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(ipfwtag+1); + if (rr->info & IPFW_IS_DUMMYNET) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + goto done; + } + pd.pf_mtag->flags |= PF_PACKET_LOOPED; + m_tag_delete(m, ipfwtag); + } + } else if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { action = PF_DROP; goto done; } @@ -6828,6 +6959,8 @@ goto done; } pd.p_len = pd.tot_len - off - (pd.hdr.tcp.th_off << 2); + pd.sport = &pd.hdr.tcp.th_sport; + pd.dport = &pd.hdr.tcp.th_dport; action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); if (action == PF_DROP) goto done; @@ -6851,6 +6984,8 @@ log = action != PF_PASS; goto done; } + pd.sport = &pd.hdr.udp.uh_sport; + pd.dport = &pd.hdr.udp.uh_dport; if (pd.hdr.udp.uh_dport == 0 || ntohs(pd.hdr.udp.uh_ulen) > m->m_pkthdr.len - off || ntohs(pd.hdr.udp.uh_ulen) < sizeof(struct udphdr)) { @@ -6974,6 +7109,47 @@ } #endif /* ALTQ */ + if (s && (s->dnpipe || s->dnrpipe)) { + pd.act.dnpipe = s->dnpipe; + pd.act.dnrpipe = s->dnrpipe; + pd.act.flags = s->state_flags; + } else { + pd.act.dnpipe = r->dnpipe; + pd.act.dnrpipe = r->dnrpipe; + pd.act.flags = r->free_flags; + } + if ((pd.act.dnpipe || pd.act.dnrpipe) && !PACKET_LOOPED(&pd)) { + if (ip_dn_io_ptr == NULL) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } else { + struct ip_fw_args dnflow; + + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + if (s) + PF_STATE_UNLOCK(s); + return (action); + } + + if (pf_pdesc_to_dnflow(dir, &pd, r, s, &dnflow)) { + ip_dn_io_ptr(m0, &dnflow); + + if (*m0 == NULL) { + if (s) + PF_STATE_UNLOCK(s); + return (action); + } else { + /* This is dummynet fast io processing */ + m_tag_delete(*m0, m_tag_first(*m0)); + pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; + } + } + } + } + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && (s->nat_rule.ptr->action == PF_RDR || diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c --- a/sys/netpfil/pf/pf_nv.c +++ b/sys/netpfil/pf/pf_nv.c @@ -527,6 +527,9 @@ sizeof(rule->pqname))); PFNV_CHK(pf_nvstring(nvl, "tagname", rule->tagname, sizeof(rule->tagname))); + PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &rule->dnpipe, 0)); + PFNV_CHK(pf_nvuint16_opt(nvl, "dnrpipe", &rule->dnrpipe, 0)); + PFNV_CHK(pf_nvuint32_opt(nvl, "dnflags", &rule->free_flags, 0)); PFNV_CHK(pf_nvstring(nvl, "match_tagname", rule->match_tagname, sizeof(rule->match_tagname))); PFNV_CHK(pf_nvstring(nvl, "overload_tblname", rule->overload_tblname, @@ -687,6 +690,9 @@ nvlist_add_string(nvl, "ifname", rule->ifname); nvlist_add_string(nvl, "qname", rule->qname); nvlist_add_string(nvl, "pqname", rule->pqname); + nvlist_add_number(nvl, "dnpipe", rule->dnpipe); + nvlist_add_number(nvl, "dnrpipe", rule->dnrpipe); + nvlist_add_number(nvl, "dnflags", rule->free_flags); nvlist_add_string(nvl, "tagname", rule->tagname); nvlist_add_string(nvl, "match_tagname", rule->match_tagname); nvlist_add_string(nvl, "overload_tblname", rule->overload_tblname);