diff options
author | Renato Botelho <renato@netgate.com> | 2015-08-17 13:53:13 -0300 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2015-08-17 13:53:13 -0300 |
commit | 14cc93f3403d906f596ddc18d531bb13f053fa76 (patch) | |
tree | b10a13ccfcf21df206a5471e9b3a8b11ec927232 | |
parent | fa9181508d9f4170f8a35bdfbe349210c30dbceb (diff) | |
download | FreeBSD-src-14cc93f3403d906f596ddc18d531bb13f053fa76.zip FreeBSD-src-14cc93f3403d906f596ddc18d531bb13f053fa76.tar.gz |
Importing pfSense patch divert.RELENG_10.diff
-rw-r--r-- | sbin/pfctl/parse.y | 22 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 8 | ||||
-rw-r--r-- | sys/net/pfvar.h | 11 | ||||
-rw-r--r-- | sys/netinet/ip_divert.c | 15 | ||||
-rw-r--r-- | sys/netpfil/pf/pf.c | 153 | ||||
-rw-r--r-- | sys/netpfil/pf/pf.h | 4 |
6 files changed, 148 insertions, 65 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 75e7e99..a32bbb1 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -162,6 +162,7 @@ struct node_icmp { enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, + PF_STATE_OPT_MAX_PACKETS, PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, }; @@ -173,6 +174,7 @@ struct node_state_opt { u_int32_t max_states; u_int32_t max_src_states; u_int32_t max_src_conn; + u_int32_t max_packets; struct { u_int32_t limit; u_int32_t seconds; @@ -472,7 +474,7 @@ int parseport(char *, struct range *r, int); %token LOAD RULESET_OPTIMIZATION %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY -%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS +%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS MAXPCKT %token IEEE8021QPCP IEEE8021QSETPCP %token DIVERTTO DIVERTREPLY %token <v.string> STRING @@ -2132,6 +2134,14 @@ pfrule : action dir logquick interface route af proto fromto } r.rule_flag |= PFRULE_NOSYNC; break; + case PF_STATE_OPT_MAX_PACKETS: + if (o->data.max_packets == 0) { + yyerror("max_packets must be" + "greater than 0"); + YYERROR; + } + r.spare2 = o->data.max_packets; + break; case PF_STATE_OPT_SRCTRACK: if (srctrack) { yyerror("state option " @@ -3839,6 +3849,15 @@ state_opt_item : MAXIMUM NUMBER { $$->next = NULL; $$->tail = $$; } + | MAXPCKT NUMBER { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_PACKETS; + $$->data.max_packets = $2; + $$->next = NULL; + $$->tail = $$; + } | MAXSRCCONN NUMBER { if ($2 < 0 || $2 > UINT_MAX) { yyerror("only positive values permitted"); @@ -5667,6 +5686,7 @@ lookup(char *s) { "match", MATCH}, { "max", MAXIMUM}, { "max-mss", MAXMSS}, + { "max-packets", MAXPCKT}, { "max-src-conn", MAXSRCCONN}, { "max-src-conn-rate", MAXSRCCONNRATE}, { "max-src-nodes", MAXSRCNODES}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index ab20398..b4fe20a 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -922,7 +922,7 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) printf(" probability %s%%", buf); } opts = 0; - if (r->max_states || r->max_src_nodes || r->max_src_states) + if (r->max_states || r->max_src_nodes || r->max_src_states || r->spare2) opts = 1; if (r->rule_flag & PFRULE_NOSYNC) opts = 1; @@ -969,6 +969,12 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric) printf("max-src-conn %u", r->max_src_conn); opts = 0; } + if (r->spare2) { + if (!opts) + printf(", "); + printf("max-packets %u", r->spare2); + opts = 0; + } if (r->max_src_conn_rate.limit) { if (!opts) printf(", "); diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 01efaa0..3be7615 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -761,7 +761,13 @@ struct pf_state { u_int64_t id; u_int32_t creatorid; u_int8_t direction; - u_int8_t pad[3]; + u_int8_t pad[2]; + u_int8_t local_flags; +#define PFSTATE_DIVERT_ALTQ 0x10 +#define PFSTATE_DIVERT_DNCOOKIE 0x20 +#define PFSTATE_DIVERT_ACTION 0x40 +#define PFSTATE_DIVERT_TAG 0x80 +#define PFSTATE_DIVERT_MASK 0xFF00 u_int refs; TAILQ_ENTRY(pf_state) sync_list; @@ -788,6 +794,7 @@ struct pf_state { u_int32_t pdnpipe; u_int32_t dnpipe; u_int16_t tag; + u_int16_t divert_cookie; u_int8_t log; u_int8_t state_flags; #define PFSTATE_ALLOWOPTS 0x01 @@ -800,7 +807,7 @@ struct pf_state { /* XXX */ u_int8_t sync_updates; - u_int8_t _tail[3]; + u_int8_t _tail; }; /* diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c index e698035..b74d60d 100644 --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -267,8 +267,7 @@ divert_packet(struct mbuf *m, int incoming) * this iface name will come along for the ride. * (see div_output for the other half of this.) */ - strlcpy(divsrc.sin_zero, m->m_pkthdr.rcvif->if_xname, - sizeof(divsrc.sin_zero)); + *((u_short *)divsrc.sin_zero) = m->m_pkthdr.rcvif->if_index; } /* Put packet on socket queue, if any */ @@ -342,7 +341,7 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, /* Loopback avoidance and state recovery */ if (sin) { - int i; + u_short idx; /* set the starting point. We provide a non-zero slot, * but a non_matching chain_id to skip that info and use @@ -350,7 +349,7 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, */ dt->slot = 1; /* dummy, chain_id is invalid */ dt->chain_id = 0; - dt->rulenum = sin->sin_port+1; /* host format ? */ + dt->rulenum = sin->sin_port; /* host format ? */ dt->rule_id = 0; /* * Find receive interface with the given name, stuffed @@ -358,10 +357,9 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, * The name is user supplied data so don't trust its size * or that it is zero terminated. */ - for (i = 0; i < sizeof(sin->sin_zero) && sin->sin_zero[i]; i++) - ; - if ( i > 0 && i < sizeof(sin->sin_zero)) - m->m_pkthdr.rcvif = ifunit(sin->sin_zero); + idx = *((u_short *)sin->sin_zero); + if ( idx > 0 ) + m->m_pkthdr.rcvif = ifnet_byindex(idx); } /* Reinject packet into the system as incoming or outgoing */ @@ -832,5 +830,4 @@ static moduledata_t ipdivertmod = { }; DECLARE_MODULE(ipdivert, ipdivertmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); -MODULE_DEPEND(ipdivert, ipfw, 2, 2, 2); MODULE_VERSION(ipdivert, 1); diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 76cfebc..0fcb0d7 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -90,6 +90,7 @@ __FBSDID("$FreeBSD$"); #include <netpfil/ipfw/ip_fw_private.h> /* XXX: only for DIR_IN/DIR_OUT */ #include <netinet/ip_fw.h> #include <netinet/ip_dummynet.h> +#include <netinet/ip_divert.h> #ifdef INET6 #include <netinet/ip6.h> @@ -313,6 +314,14 @@ VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); #define PACKET_LOOPED(mtag) ((mtag)->flags & PF_PACKET_LOOPED) +#define PF_DIVERT_MAXPACKETS_REACHED() \ +do { \ + if (r->spare2 && \ + s->packets[dir == PF_OUT] > r->spare2) \ + /* fake that divert already happened */ \ + pd.pf_mtag->flags |= PF_PACKET_LOOPED; \ +} while(0) + #define STATE_LOOKUP(i, k, d, s, pd) \ do { \ (s) = pf_find_state((i), (k), (d)); \ @@ -6128,6 +6137,8 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) struct pf_pdesc pd; int off = 0, dirndx, pqid = 0; int loopedfrom = 0; + u_int16_t divertcookie = 0; + u_int8_t divflags = 0; struct ip_fw_args dnflow; M_ASSERTPKTHDR(m); @@ -6160,12 +6171,10 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) pd.pf_mtag->flags |= PF_PACKET_LOOPED; if (rr->info & IPFW_IS_DUMMYNET) loopedfrom = 1; - if (rr->info & IPFW_IS_DIVERT && rr->rulenum == 0) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - goto done; - } + if (rr->info & IPFW_IS_DIVERT) { + divertcookie = rr->rulenum; + divflags = (u_int8_t)(divertcookie >> 8); + divertcookie &= ~PFSTATE_DIVERT_MASK; } if (pd.pf_mtag && pd.pf_mtag->flags & PF_FASTFWD_OURS_PRESENT) { m->m_flags |= M_FASTFWD_OURS; @@ -6332,6 +6341,17 @@ done: ("pf: dropping packet with ip options\n")); } + if (s) { + PF_DIVERT_MAXPACKETS_REACHED(); + + if (divflags) { + s->divert_cookie = divertcookie; + s->local_flags |= divflags; + } else { + divertcookie = s->divert_cookie; + divflags = s->local_flags; + } + } if (s && s->tag > 0 && pf_tag_packet(m, &pd, s->tag)) { action = PF_DROP; REASON_SET(&reason, PFRES_MEMORY); @@ -6359,7 +6379,33 @@ done: } #endif /* ALTQ */ - if (s && (s->dnpipe || s->pdnpipe)) { + if (divflags & PFSTATE_DIVERT_TAG) + pd.pf_mtag->tag = divertcookie; + else if (divflags & PFSTATE_DIVERT_ALTQ) + pd.pf_mtag->qid = divertcookie; + else if (divflags & PFSTATE_DIVERT_ACTION) { + struct pf_rule *dlr; + action = PF_DROP; + if (s) + pf_unlink_state(s, PF_ENTER_LOCKED); + REASON_SET(&reason, PFRES_DIVERT); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: changing action to with overload from divert.\n")); + dlr = r; + PFLOG_PACKET(kif, m, AF_INET, dir, reason, dlr, a, + ruleset, &pd, (s == NULL)); + m_freem(*m0); + *m0 = NULL; + /* NOTE: Fake this to avoid divert giving errors to the application. */ + return (PF_PASS); + } + + if (divflags & PFSTATE_DIVERT_DNCOOKIE) { + pd.act.dnpipe = divertcookie; + pd.act.pdnpipe = divertcookie; + pd.act.flags |= PFRULE_DN_IS_PIPE; + } else if (s && (s->dnpipe || s->pdnpipe)) { pd.act.dnpipe = s->dnpipe; pd.act.pdnpipe = s->pdnpipe; pd.act.flags = s->state_flags; @@ -6414,10 +6460,51 @@ done: PF_STATE_UNLOCK(s); return (action); } - } else - pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; + } continueprocessing: + if (action == PF_PASS && r->divert.port && ip_divert_ptr != NULL && + !PACKET_LOOPED(&pd)) { + if (!r->spare2 || + (s && s->packets[dir == PF_OUT] <= r->spare2)) { + ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, + sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); + if (ipfwtag != NULL) { + ((struct ipfw_rule_ref *)(ipfwtag+1))->info = + ntohs(r->divert.port); + ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; + + if (s) + PF_STATE_UNLOCK(s); + + m_tag_prepend(m, ipfwtag); + if (m->m_flags & M_FASTFWD_OURS) { + if (pd.pf_mtag == NULL && + ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate tag\n")); + } + pd.pf_mtag->flags |= PF_FASTFWD_OURS_PRESENT; + m->m_flags &= ~M_FASTFWD_OURS; + } + ip_divert_ptr(*m0, dir == PF_IN ? DIR_IN : DIR_OUT); + *m0 = NULL; + + return (action); + } else { + /* XXX: ipfw has the same behaviour! */ + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: failed to allocate divert tag\n")); + } + } + } + /* * connections redirected to loopback should not match sockets * bound specifically to loopback due to security implications, @@ -6427,51 +6514,15 @@ continueprocessing: pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && - (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) + (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { m->m_flags |= M_SKIP_FIREWALL; - if (action == PF_PASS && r->divert.port && ip_divert_ptr != NULL && - !PACKET_LOOPED(&pd)) { - - ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0, - sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO); - if (ipfwtag != NULL) { - ((struct ipfw_rule_ref *)(ipfwtag+1))->info = - ntohs(r->divert.port); - ((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir; - - if (s) - PF_STATE_UNLOCK(s); - - m_tag_prepend(m, ipfwtag); - if (m->m_flags & M_FASTFWD_OURS) { - if (pd.pf_mtag == NULL && - ((pd.pf_mtag = pf_get_mtag(m)) == NULL)) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: failed to allocate tag\n")); - } else { - pd.pf_mtag->flags |= - PF_FASTFWD_OURS_PRESENT; - m->m_flags &= ~M_FASTFWD_OURS; - } - } - ip_divert_ptr(*m0, dir == PF_IN ? DIR_IN : DIR_OUT); - *m0 = NULL; - - return (action); - } else { - /* XXX: ipfw has the same behaviour! */ - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); - log = 1; - DPFPRINTF(PF_DEBUG_MISC, - ("pf: failed to allocate divert tag\n")); - } + if (PACKET_LOOPED(pd.pf_mtag) && !loopedfrom) + m->m_flags |= M_FASTFWD_OURS; } + pd.pf_mtag->flags &= ~PF_PACKET_LOOPED; + if (log) { struct pf_rule *lr; @@ -6605,7 +6656,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) PF_RULES_RLOCK(); - if (ip_dn_io_ptr != NULL && + if (((ip_dn_io_ptr != NULL) || (ip_divert_ptr != NULL)) && ((dn_tag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL)) != NULL)) { struct ipfw_rule_ref *rr = (struct ipfw_rule_ref *)(dn_tag+1); pd.pf_mtag->flags |= PF_PACKET_LOOPED; diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h index e588eb1..5351786 100644 --- a/sys/netpfil/pf/pf.h +++ b/sys/netpfil/pf/pf.h @@ -126,7 +126,8 @@ enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, #define PFRES_MAXSTATES 12 /* State limit */ #define PFRES_SRCLIMIT 13 /* Source node/conn limit */ #define PFRES_SYNPROXY 14 /* SYN proxy */ -#define PFRES_MAX 15 /* total+1 */ +#define PFRES_DIVERT 15 /* Divert override */ +#define PFRES_MAX 16 /* total+1 */ #define PFRES_NAMES { \ "match", \ @@ -144,6 +145,7 @@ enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, "state-limit", \ "src-limit", \ "synproxy", \ + "divert", \ NULL \ } |