diff options
Diffstat (limited to 'sys/netpfil')
| -rw-r--r-- | sys/netpfil/ipfilter/netinet/ip_htable.c | 45 | ||||
| -rw-r--r-- | sys/netpfil/ipfilter/netinet/ip_htable.h | 2 | ||||
| -rw-r--r-- | sys/netpfil/ipfilter/netinet/ip_nat.c | 1 | ||||
| -rw-r--r-- | sys/netpfil/ipfw/ip_dn_glue.c | 858 | ||||
| -rw-r--r-- | sys/netpfil/ipfw/ip_dn_io.c | 6 | ||||
| -rw-r--r-- | sys/netpfil/ipfw/ip_dn_private.h | 10 | ||||
| -rw-r--r-- | sys/netpfil/ipfw/ip_dummynet.c | 100 | ||||
| -rw-r--r-- | sys/netpfil/ipfw/ip_fw2.c | 9 | ||||
| -rw-r--r-- | sys/netpfil/ipfw/ip_fw_nat.c | 16 | ||||
| -rw-r--r-- | sys/netpfil/ipfw/pmod/tcpmod.c | 25 | ||||
| -rw-r--r-- | sys/netpfil/pf/if_pfsync.c | 3 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf.c | 585 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_if.c | 2 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_ioctl.c | 17 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_lb.c | 122 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_nl.c | 132 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_nl.h | 4 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_table.c | 12 |
18 files changed, 740 insertions, 1209 deletions
diff --git a/sys/netpfil/ipfilter/netinet/ip_htable.c b/sys/netpfil/ipfilter/netinet/ip_htable.c index 91b375f80db1..5f5c04732d69 100644 --- a/sys/netpfil/ipfilter/netinet/ip_htable.c +++ b/sys/netpfil/ipfilter/netinet/ip_htable.c @@ -96,6 +96,8 @@ typedef struct ipf_htable_softc_s { u_long ipf_nhtnodes[LOOKUP_POOL_SZ]; iphtable_t *ipf_htables[LOOKUP_POOL_SZ]; iphtent_t *ipf_node_explist; + ipftuneable_t *ipf_htable_tune; + u_int ipf_htable_size_max; } ipf_htable_softc_t; ipf_lookup_t ipf_htable_backend = { @@ -122,6 +124,18 @@ ipf_lookup_t ipf_htable_backend = { }; +static ipftuneable_t ipf_htable_tuneables[] = { + { { (void *)offsetof(ipf_htable_softc_t, ipf_htable_size_max) }, + "htable_size_max", 1, 0x7fffffff, + stsizeof(ipf_htable_softc_t, ipf_htable_size_max), + 0, NULL, NULL }, + { { NULL }, + NULL, 0, 0, + 0, + 0, NULL, NULL } +}; + + /* ------------------------------------------------------------------------ */ /* Function: ipf_htable_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ @@ -142,6 +156,18 @@ ipf_htable_soft_create(ipf_main_softc_t *softc) bzero((char *)softh, sizeof(*softh)); + softh->ipf_htable_tune = ipf_tune_array_copy(softh, + sizeof(ipf_htable_tuneables), + ipf_htable_tuneables); + if (softh->ipf_htable_tune == NULL) { + ipf_htable_soft_destroy(softc, softh); + return (NULL); + } + if (ipf_tune_array_link(softc, softh->ipf_htable_tune) == -1) { + ipf_htable_soft_destroy(softc, softh); + return (NULL); + } + return (softh); } @@ -160,6 +186,12 @@ ipf_htable_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_htable_softc_t *softh = arg; + if (softh->ipf_htable_tune != NULL) { + ipf_tune_array_unlink(softc, softh->ipf_htable_tune); + KFREES(softh->ipf_htable_tune, sizeof(ipf_htable_tuneables)); + softh->ipf_htable_tune = NULL; + } + KFREE(softh); } @@ -179,6 +211,8 @@ ipf_htable_soft_init(ipf_main_softc_t *softc, void *arg) bzero((char *)softh, sizeof(*softh)); + softh->ipf_htable_size_max = IPHTABLE_MAX_SIZE; + return (0); } @@ -230,6 +264,8 @@ ipf_htable_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) return (EINVAL); } + bzero(&stats, sizeof(stats)); + stats.iphs_tables = softh->ipf_htables[op->iplo_unit + 1]; stats.iphs_numtables = softh->ipf_nhtables[op->iplo_unit + 1]; stats.iphs_numnodes = softh->ipf_nhtnodes[op->iplo_unit + 1]; @@ -325,6 +361,15 @@ ipf_htable_create(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) iph->iph_name[sizeof(iph->iph_name) - 1] = '\0'; } + if ((iph->iph_size == 0) || + (iph->iph_size > softh->ipf_htable_size_max)) { + IPFERROR(30027); + return (EINVAL); + } + if (iph->iph_size > ( SIZE_MAX / sizeof(*iph->iph_table))) { + IPFERROR(30028); + return (EINVAL); + } KMALLOCS(iph->iph_table, iphtent_t **, iph->iph_size * sizeof(*iph->iph_table)); if (iph->iph_table == NULL) { diff --git a/sys/netpfil/ipfilter/netinet/ip_htable.h b/sys/netpfil/ipfilter/netinet/ip_htable.h index 55c289e57ff6..3a8782ccd4b2 100644 --- a/sys/netpfil/ipfilter/netinet/ip_htable.h +++ b/sys/netpfil/ipfilter/netinet/ip_htable.h @@ -55,6 +55,8 @@ typedef struct iphtable_s { char iph_name[FR_GROUPLEN]; /* hash table number */ } iphtable_t; +#define IPHTABLE_MAX_SIZE 1024 + /* iph_type */ #define IPHASH_LOOKUP 0 #define IPHASH_GROUPMAP 1 diff --git a/sys/netpfil/ipfilter/netinet/ip_nat.c b/sys/netpfil/ipfilter/netinet/ip_nat.c index 972511f43bd5..53c180cdfbca 100644 --- a/sys/netpfil/ipfilter/netinet/ip_nat.c +++ b/sys/netpfil/ipfilter/netinet/ip_nat.c @@ -1771,6 +1771,7 @@ ipf_nat_getent(ipf_main_softc_t *softc, caddr_t data, int getlock) IPFERROR(60029); return (ENOMEM); } + bzero(ipn, ipns.ipn_dsize); if (getlock) { READ_ENTER(&softc->ipf_nat); diff --git a/sys/netpfil/ipfw/ip_dn_glue.c b/sys/netpfil/ipfw/ip_dn_glue.c deleted file mode 100644 index 0412b730e4df..000000000000 --- a/sys/netpfil/ipfw/ip_dn_glue.c +++ /dev/null @@ -1,858 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa - * All rights reserved - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * - * Binary compatibility support for /sbin/ipfw RELENG_7 and RELENG_8 - */ - -#include "opt_inet6.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/malloc.h> -#include <sys/mbuf.h> -#include <sys/kernel.h> -#include <sys/lock.h> -#include <sys/module.h> -#include <sys/priv.h> -#include <sys/proc.h> -#include <sys/rwlock.h> -#include <sys/socket.h> -#include <sys/socketvar.h> -#include <sys/time.h> -#include <sys/taskqueue.h> -#include <net/if.h> /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ -#include <netinet/in.h> -#include <netinet/ip_var.h> /* ip_output(), IP_FORWARDING */ -#include <netinet/ip_fw.h> -#include <netinet/ip_dummynet.h> - -#include <netpfil/ipfw/ip_fw_private.h> -#include <netpfil/ipfw/dn_heap.h> -#include <netpfil/ipfw/ip_dn_private.h> -#ifdef NEW_AQM -#include <netpfil/ipfw/dn_aqm.h> -#endif -#include <netpfil/ipfw/dn_sched.h> - -/* FREEBSD7.2 ip_dummynet.h r191715*/ - -struct dn_heap_entry7 { - int64_t key; /* sorting key. Topmost element is smallest one */ - void *object; /* object pointer */ -}; - -struct dn_heap7 { - int size; - int elements; - int offset; /* XXX if > 0 this is the offset of direct ptr to obj */ - struct dn_heap_entry7 *p; /* really an array of "size" entries */ -}; - -/* Common to 7.2 and 8 */ -struct dn_flow_set { - SLIST_ENTRY(dn_flow_set) next; /* linked list in a hash slot */ - - u_short fs_nr ; /* flow_set number */ - u_short flags_fs; -#define DNOLD_HAVE_FLOW_MASK 0x0001 -#define DNOLD_IS_RED 0x0002 -#define DNOLD_IS_GENTLE_RED 0x0004 -#define DNOLD_QSIZE_IS_BYTES 0x0008 /* queue size is measured in bytes */ -#define DNOLD_NOERROR 0x0010 /* do not report ENOBUFS on drops */ -#define DNOLD_HAS_PROFILE 0x0020 /* the pipe has a delay profile. */ -#define DNOLD_IS_PIPE 0x4000 -#define DNOLD_IS_QUEUE 0x8000 - - struct dn_pipe7 *pipe ; /* pointer to parent pipe */ - u_short parent_nr ; /* parent pipe#, 0 if local to a pipe */ - - int weight ; /* WFQ queue weight */ - int qsize ; /* queue size in slots or bytes */ - int plr[4] ; /* pkt loss rate (2^31-1 means 100%) */ - - struct ipfw_flow_id flow_mask ; - - /* hash table of queues onto this flow_set */ - int rq_size ; /* number of slots */ - int rq_elements ; /* active elements */ - struct dn_flow_queue7 **rq ; /* array of rq_size entries */ - - u_int32_t last_expired ; /* do not expire too frequently */ - int backlogged ; /* #active queues for this flowset */ - - /* RED parameters */ -#define SCALE_RED 16 -#define SCALE(x) ( (x) << SCALE_RED ) -#define SCALE_VAL(x) ( (x) >> SCALE_RED ) -#define SCALE_MUL(x,y) ( ( (x) * (y) ) >> SCALE_RED ) - int w_q ; /* queue weight (scaled) */ - int max_th ; /* maximum threshold for queue (scaled) */ - int min_th ; /* minimum threshold for queue (scaled) */ - int max_p ; /* maximum value for p_b (scaled) */ - u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ - u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ - u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ - u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ - u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ - u_int lookup_depth ; /* depth of lookup table */ - int lookup_step ; /* granularity inside the lookup table */ - int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ - int avg_pkt_size ; /* medium packet size */ - int max_pkt_size ; /* max packet size */ -}; -SLIST_HEAD(dn_flow_set_head, dn_flow_set); - -#define DN_IS_PIPE 0x4000 -#define DN_IS_QUEUE 0x8000 -struct dn_flow_queue7 { - struct dn_flow_queue7 *next ; - struct ipfw_flow_id id ; - - struct mbuf *head, *tail ; /* queue of packets */ - u_int len ; - u_int len_bytes ; - - u_long numbytes; - - u_int64_t tot_pkts ; /* statistics counters */ - u_int64_t tot_bytes ; - u_int32_t drops ; - - int hash_slot ; /* debugging/diagnostic */ - - /* RED parameters */ - int avg ; /* average queue length est. (scaled) */ - int count ; /* arrivals since last RED drop */ - int random ; /* random value (scaled) */ - u_int32_t q_time; /* start of queue idle time */ - - /* WF2Q+ support */ - struct dn_flow_set *fs ; /* parent flow set */ - int heap_pos ; /* position (index) of struct in heap */ - int64_t sched_time ; /* current time when queue enters ready_heap */ - - int64_t S,F ; /* start time, finish time */ -}; - -struct dn_pipe7 { /* a pipe */ - SLIST_ENTRY(dn_pipe7) next; /* linked list in a hash slot */ - - int pipe_nr ; /* number */ - uint32_t bandwidth; /* really, bytes/tick. */ - int delay ; /* really, ticks */ - - struct mbuf *head, *tail ; /* packets in delay line */ - - /* WF2Q+ */ - struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ - struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ - struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ - - int64_t V ; /* virtual time */ - int sum; /* sum of weights of all active sessions */ - - int numbytes; - - int64_t sched_time ; /* time pipe was scheduled in ready_heap */ - - /* - * When the tx clock come from an interface (if_name[0] != '\0'), its name - * is stored below, whereas the ifp is filled when the rule is configured. - */ - char if_name[IFNAMSIZ]; - struct ifnet *ifp ; - int ready ; /* set if ifp != NULL and we got a signal from it */ - - struct dn_flow_set fs ; /* used with fixed-rate flows */ -}; -SLIST_HEAD(dn_pipe_head7, dn_pipe7); - -/* FREEBSD8 ip_dummynet.h r196045 */ -struct dn_flow_queue8 { - struct dn_flow_queue8 *next ; - struct ipfw_flow_id id ; - - struct mbuf *head, *tail ; /* queue of packets */ - u_int len ; - u_int len_bytes ; - - uint64_t numbytes ; /* credit for transmission (dynamic queues) */ - int64_t extra_bits; /* extra bits simulating unavailable channel */ - - u_int64_t tot_pkts ; /* statistics counters */ - u_int64_t tot_bytes ; - u_int32_t drops ; - - int hash_slot ; /* debugging/diagnostic */ - - /* RED parameters */ - int avg ; /* average queue length est. (scaled) */ - int count ; /* arrivals since last RED drop */ - int random ; /* random value (scaled) */ - int64_t idle_time; /* start of queue idle time */ - - /* WF2Q+ support */ - struct dn_flow_set *fs ; /* parent flow set */ - int heap_pos ; /* position (index) of struct in heap */ - int64_t sched_time ; /* current time when queue enters ready_heap */ - - int64_t S,F ; /* start time, finish time */ -}; - -struct dn_pipe8 { /* a pipe */ - SLIST_ENTRY(dn_pipe8) next; /* linked list in a hash slot */ - - int pipe_nr ; /* number */ - uint32_t bandwidth; /* really, bytes/tick. */ - int delay ; /* really, ticks */ - - struct mbuf *head, *tail ; /* packets in delay line */ - - /* WF2Q+ */ - struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ - struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ - struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ - - int64_t V ; /* virtual time */ - int sum; /* sum of weights of all active sessions */ - - /* Same as in dn_flow_queue, numbytes can become large */ - int64_t numbytes; /* bits I can transmit (more or less). */ - uint64_t burst; /* burst size, scaled: bits * hz */ - - int64_t sched_time ; /* time pipe was scheduled in ready_heap */ - int64_t idle_time; /* start of pipe idle time */ - - char if_name[IFNAMSIZ]; - struct ifnet *ifp ; - int ready ; /* set if ifp != NULL and we got a signal from it */ - - struct dn_flow_set fs ; /* used with fixed-rate flows */ - - /* fields to simulate a delay profile */ -#define ED_MAX_NAME_LEN 32 - char name[ED_MAX_NAME_LEN]; - int loss_level; - int samples_no; - int *samples; -}; - -#define ED_MAX_SAMPLES_NO 1024 -struct dn_pipe_max8 { - struct dn_pipe8 pipe; - int samples[ED_MAX_SAMPLES_NO]; -}; -SLIST_HEAD(dn_pipe_head8, dn_pipe8); - -/* - * Changes from 7.2 to 8: - * dn_pipe: - * numbytes from int to int64_t - * add burst (int64_t) - * add idle_time (int64_t) - * add profile - * add struct dn_pipe_max - * add flag DN_HAS_PROFILE - * - * dn_flow_queue - * numbytes from u_long to int64_t - * add extra_bits (int64_t) - * q_time from u_int32_t to int64_t and name idle_time - * - * dn_flow_set unchanged - * - */ - -/* NOTE:XXX copied from dummynet.c */ -#define O_NEXT(p, len) ((void *)((char *)p + len)) -static void -oid_fill(struct dn_id *oid, int len, int type, uintptr_t id) -{ - oid->len = len; - oid->type = type; - oid->subtype = 0; - oid->id = id; -} -/* make room in the buffer and move the pointer forward */ -static void * -o_next(struct dn_id **o, int len, int type) -{ - struct dn_id *ret = *o; - oid_fill(ret, len, type, 0); - *o = O_NEXT(*o, len); - return ret; -} - -static size_t pipesize7 = sizeof(struct dn_pipe7); -static size_t pipesize8 = sizeof(struct dn_pipe8); -static size_t pipesizemax8 = sizeof(struct dn_pipe_max8); - -/* Indicate 'ipfw' version - * 1: from FreeBSD 7.2 - * 0: from FreeBSD 8 - * -1: unknown (for now is unused) - * - * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives - * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknown, - * it is suppose to be the FreeBSD 8 version. - */ -static int is7 = 0; - -static int -convertflags2new(int src) -{ - int dst = 0; - - if (src & DNOLD_HAVE_FLOW_MASK) - dst |= DN_HAVE_MASK; - if (src & DNOLD_QSIZE_IS_BYTES) - dst |= DN_QSIZE_BYTES; - if (src & DNOLD_NOERROR) - dst |= DN_NOERROR; - if (src & DNOLD_IS_RED) - dst |= DN_IS_RED; - if (src & DNOLD_IS_GENTLE_RED) - dst |= DN_IS_GENTLE_RED; - if (src & DNOLD_HAS_PROFILE) - dst |= DN_HAS_PROFILE; - - return dst; -} - -static int -convertflags2old(int src) -{ - int dst = 0; - - if (src & DN_HAVE_MASK) - dst |= DNOLD_HAVE_FLOW_MASK; - if (src & DN_IS_RED) - dst |= DNOLD_IS_RED; - if (src & DN_IS_GENTLE_RED) - dst |= DNOLD_IS_GENTLE_RED; - if (src & DN_NOERROR) - dst |= DNOLD_NOERROR; - if (src & DN_HAS_PROFILE) - dst |= DNOLD_HAS_PROFILE; - if (src & DN_QSIZE_BYTES) - dst |= DNOLD_QSIZE_IS_BYTES; - - return dst; -} - -static int -dn_compat_del(void *v) -{ - struct dn_pipe7 *p = (struct dn_pipe7 *) v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *) v; - struct { - struct dn_id oid; - uintptr_t a[1]; /* add more if we want a list */ - } cmd; - - /* XXX DN_API_VERSION ??? */ - oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); - - if (is7) { - if (p->pipe_nr == 0 && p->fs.fs_nr == 0) - return EINVAL; - if (p->pipe_nr != 0 && p->fs.fs_nr != 0) - return EINVAL; - } else { - if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0) - return EINVAL; - if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0) - return EINVAL; - } - - if (p->pipe_nr != 0) { /* pipe x delete */ - cmd.a[0] = p->pipe_nr; - cmd.oid.subtype = DN_LINK; - } else { /* queue x delete */ - cmd.oid.subtype = DN_FS; - cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr; - } - - return do_config(&cmd, cmd.oid.len); -} - -static int -dn_compat_config_queue(struct dn_fs *fs, void* v) -{ - struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - struct dn_flow_set *f; - - if (is7) - f = &p7->fs; - else - f = &p8->fs; - - fs->fs_nr = f->fs_nr; - fs->sched_nr = f->parent_nr; - fs->flow_mask = f->flow_mask; - fs->buckets = f->rq_size; - fs->qsize = f->qsize; - fs->plr[0] = f->plr[0]; - fs->plr[1] = f->plr[1]; - fs->plr[2] = f->plr[2]; - fs->plr[3] = f->plr[3]; - fs->par[0] = f->weight; - fs->flags = convertflags2new(f->flags_fs); - if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) { - fs->w_q = f->w_q; - fs->max_th = f->max_th; - fs->min_th = f->min_th; - fs->max_p = f->max_p; - } - - return 0; -} - -static int -dn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p, - struct dn_fs *fs, void* v) -{ - struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - int i = p7->pipe_nr; - - sch->sched_nr = i; - sch->oid.subtype = 0; - p->link_nr = i; - fs->fs_nr = i + 2*DN_MAX_ID; - fs->sched_nr = i + DN_MAX_ID; - - /* Common to 7 and 8 */ - p->bandwidth = p7->bandwidth; - p->delay = p7->delay; - if (!is7) { - /* FreeBSD 8 has burst */ - p->burst = p8->burst; - } - - /* fill the fifo flowset */ - dn_compat_config_queue(fs, v); - fs->fs_nr = i + 2*DN_MAX_ID; - fs->sched_nr = i + DN_MAX_ID; - - /* Move scheduler related parameter from fs to sch */ - sch->buckets = fs->buckets; /*XXX*/ - fs->buckets = 0; - if (fs->flags & DN_HAVE_MASK) { - sch->flags |= DN_HAVE_MASK; - fs->flags &= ~DN_HAVE_MASK; - sch->sched_mask = fs->flow_mask; - bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id)); - } - - return 0; -} - -static int -dn_compat_config_profile(struct dn_profile *pf, struct dn_link *p, - void *v) -{ - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - - p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]); - - pf->link_nr = p->link_nr; - pf->loss_level = p8->loss_level; -// pf->bandwidth = p->bandwidth; //XXX bandwidth redundant? - pf->samples_no = p8->samples_no; - strncpy(pf->name, p8->name,sizeof(pf->name)); - bcopy(p8->samples, pf->samples, sizeof(pf->samples)); - - return 0; -} - -/* - * If p->pipe_nr != 0 the command is 'pipe x config', so need to create - * the three main struct, else only a flowset is created - */ -static int -dn_compat_configure(void *v) -{ - struct dn_id *buf = NULL, *base; - struct dn_sch *sch = NULL; - struct dn_link *p = NULL; - struct dn_fs *fs = NULL; - struct dn_profile *pf = NULL; - int lmax; - int error; - - struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; - struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; - - int i; /* number of object to configure */ - - lmax = sizeof(struct dn_id); /* command header */ - lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + - sizeof(struct dn_fs) + sizeof(struct dn_profile); - - base = buf = malloc(lmax, M_DUMMYNET, M_WAITOK|M_ZERO); - o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); - base->id = DN_API_VERSION; - - /* pipe_nr is the same in p7 and p8 */ - i = p7->pipe_nr; - if (i != 0) { /* pipe config */ - sch = o_next(&buf, sizeof(*sch), DN_SCH); - p = o_next(&buf, sizeof(*p), DN_LINK); - fs = o_next(&buf, sizeof(*fs), DN_FS); - - error = dn_compat_config_pipe(sch, p, fs, v); - if (error) { - free(buf, M_DUMMYNET); - return error; - } - if (!is7 && p8->samples_no > 0) { - /* Add profiles*/ - pf = o_next(&buf, sizeof(*pf), DN_PROFILE); - error = dn_compat_config_profile(pf, p, v); - if (error) { - free(buf, M_DUMMYNET); - return error; - } - } - } else { /* queue config */ - fs = o_next(&buf, sizeof(*fs), DN_FS); - error = dn_compat_config_queue(fs, v); - if (error) { - free(buf, M_DUMMYNET); - return error; - } - } - error = do_config(base, (char *)buf - (char *)base); - - if (buf) - free(buf, M_DUMMYNET); - return error; -} - -int -dn_compat_calc_size(void) -{ - int need = 0; - /* XXX use FreeBSD 8 struct size */ - /* NOTE: - * - half scheduler: schk_count/2 - * - all flowset: fsk_count - * - all flowset queues: queue_count - * - all pipe queue: si_count - */ - need += V_dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2; - need += V_dn_cfg.fsk_count * sizeof(struct dn_flow_set); - need += V_dn_cfg.si_count * sizeof(struct dn_flow_queue8); - need += V_dn_cfg.queue_count * sizeof(struct dn_flow_queue8); - - return need; -} - -int -dn_c_copy_q (void *_ni, void *arg) -{ - struct copy_args *a = arg; - struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start; - struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start; - struct dn_flow *ni = (struct dn_flow *)_ni; - int size = 0; - - /* XXX hash slot not set */ - /* No difference between 7.2/8 */ - fq7->len = ni->length; - fq7->len_bytes = ni->len_bytes; - fq7->id = ni->fid; - - if (is7) { - size = sizeof(struct dn_flow_queue7); - fq7->tot_pkts = ni->tot_pkts; - fq7->tot_bytes = ni->tot_bytes; - fq7->drops = ni->drops; - } else { - size = sizeof(struct dn_flow_queue8); - fq8->tot_pkts = ni->tot_pkts; - fq8->tot_bytes = ni->tot_bytes; - fq8->drops = ni->drops; - } - - *a->start += size; - return 0; -} - -int -dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq) -{ - struct dn_link *l = &s->link; - struct dn_fsk *f = s->fs; - - struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start; - struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start; - struct dn_flow_set *fs; - int size = 0; - - if (is7) { - fs = &pipe7->fs; - size = sizeof(struct dn_pipe7); - } else { - fs = &pipe8->fs; - size = sizeof(struct dn_pipe8); - } - - /* These 4 field are the same in pipe7 and pipe8 */ - pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE; - pipe7->bandwidth = l->bandwidth; - pipe7->delay = l->delay * 1000 / hz; - pipe7->pipe_nr = l->link_nr - DN_MAX_ID; - - if (!is7) { - if (s->profile) { - struct dn_profile *pf = s->profile; - strncpy(pipe8->name, pf->name, sizeof(pf->name)); - pipe8->loss_level = pf->loss_level; - pipe8->samples_no = pf->samples_no; - } - pipe8->burst = div64(l->burst , 8 * hz); - } - - fs->flow_mask = s->sch.sched_mask; - fs->rq_size = s->sch.buckets ? s->sch.buckets : 1; - - fs->parent_nr = l->link_nr - DN_MAX_ID; - fs->qsize = f->fs.qsize; - fs->plr[0] = f->fs.plr[0]; - fs->plr[1] = f->fs.plr[1]; - fs->plr[2] = f->fs.plr[2]; - fs->plr[3] = f->fs.plr[3]; - fs->w_q = f->fs.w_q; - fs->max_th = f->max_th; - fs->min_th = f->min_th; - fs->max_p = f->fs.max_p; - fs->rq_elements = nq; - - fs->flags_fs = convertflags2old(f->fs.flags); - - *a->start += size; - return 0; -} - -int -dn_compat_copy_pipe(struct copy_args *a, void *_o) -{ - int have = a->end - *a->start; - int need = 0; - int pipe_size = sizeof(struct dn_pipe8); - int queue_size = sizeof(struct dn_flow_queue8); - int n_queue = 0; /* number of queues */ - - struct dn_schk *s = (struct dn_schk *)_o; - /* calculate needed space: - * - struct dn_pipe - * - if there are instances, dn_queue * n_instances - */ - n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) : - (s->siht ? 1 : 0)); - need = pipe_size + queue_size * n_queue; - if (have < need) { - D("have %d < need %d", have, need); - return 1; - } - /* copy pipe */ - dn_c_copy_pipe(s, a, n_queue); - - /* copy queues */ - if (s->sch.flags & DN_HAVE_MASK) - dn_ht_scan(s->siht, dn_c_copy_q, a); - else if (s->siht) - dn_c_copy_q(s->siht, a); - return 0; -} - -int -dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq) -{ - struct dn_flow_set *fs = (struct dn_flow_set *)*a->start; - - fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE; - fs->fs_nr = f->fs.fs_nr; - fs->qsize = f->fs.qsize; - fs->plr[0] = f->fs.plr[0]; - fs->plr[1] = f->fs.plr[1]; - fs->plr[2] = f->fs.plr[2]; - fs->plr[3] = f->fs.plr[3]; - fs->w_q = f->fs.w_q; - fs->max_th = f->max_th; - fs->min_th = f->min_th; - fs->max_p = f->fs.max_p; - fs->flow_mask = f->fs.flow_mask; - fs->rq_elements = nq; - fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1); - fs->parent_nr = f->fs.sched_nr; - fs->weight = f->fs.par[0]; - - fs->flags_fs = convertflags2old(f->fs.flags); - *a->start += sizeof(struct dn_flow_set); - return 0; -} - -int -dn_compat_copy_queue(struct copy_args *a, void *_o) -{ - int have = a->end - *a->start; - int need = 0; - int fs_size = sizeof(struct dn_flow_set); - int queue_size = sizeof(struct dn_flow_queue8); - - struct dn_fsk *fs = (struct dn_fsk *)_o; - int n_queue = 0; /* number of queues */ - - n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) : - (fs->qht ? 1 : 0)); - - need = fs_size + queue_size * n_queue; - if (have < need) { - D("have < need"); - return 1; - } - - /* copy flowset */ - dn_c_copy_fs(fs, a, n_queue); - - /* copy queues */ - if (fs->fs.flags & DN_HAVE_MASK) - dn_ht_scan(fs->qht, dn_c_copy_q, a); - else if (fs->qht) - dn_c_copy_q(fs->qht, a); - - return 0; -} - -int -copy_data_helper_compat(void *_o, void *_arg) -{ - struct copy_args *a = _arg; - - if (a->type == DN_COMPAT_PIPE) { - struct dn_schk *s = _o; - if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) { - return 0; /* not old type */ - } - /* copy pipe parameters, and if instance exists, copy - * other parameters and eventually queues. - */ - if(dn_compat_copy_pipe(a, _o)) - return DNHT_SCAN_END; - } else if (a->type == DN_COMPAT_QUEUE) { - struct dn_fsk *fs = _o; - if (fs->fs.fs_nr >= DN_MAX_ID) - return 0; - if (dn_compat_copy_queue(a, _o)) - return DNHT_SCAN_END; - } - return 0; -} - -/* Main function to manage old requests */ -int -ip_dummynet_compat(struct sockopt *sopt) -{ - int error=0; - void *v = NULL; - struct dn_id oid; - - /* Length of data, used to found ipfw version... */ - int len = sopt->sopt_valsize; - - /* len can be 0 if command was dummynet_flush */ - if (len == pipesize7) { - D("setting compatibility with FreeBSD 7.2"); - is7 = 1; - } - else if (len == pipesize8 || len == pipesizemax8) { - D("setting compatibility with FreeBSD 8"); - is7 = 0; - } - - switch (sopt->sopt_name) { - default: - printf("dummynet: -- unknown option %d", sopt->sopt_name); - error = EINVAL; - break; - - case IP_DUMMYNET_FLUSH: - oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); - do_config(&oid, oid.len); - break; - - case IP_DUMMYNET_DEL: - v = malloc(len, M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, v, len, len); - if (error) - break; - error = dn_compat_del(v); - free(v, M_TEMP); - break; - - case IP_DUMMYNET_CONFIGURE: - v = malloc(len, M_TEMP, M_NOWAIT); - if (v == NULL) { - error = ENOMEM; - break; - } - error = sooptcopyin(sopt, v, len, len); - if (error) - break; - error = dn_compat_configure(v); - free(v, M_TEMP); - break; - - case IP_DUMMYNET_GET: { - void *buf; - int ret; - int original_size = sopt->sopt_valsize; - int size; - - ret = dummynet_get(sopt, &buf); - if (ret) - return 0;//XXX ? - size = sopt->sopt_valsize; - sopt->sopt_valsize = original_size; - D("size=%d, buf=%p", size, buf); - ret = sooptcopyout(sopt, buf, size); - if (ret) - printf(" %s ERROR sooptcopyout\n", __FUNCTION__); - if (buf) - free(buf, M_DUMMYNET); - } - } - - return error; -} diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c index 03116cb0641c..3a8de2b2bfee 100644 --- a/sys/netpfil/ipfw/ip_dn_io.c +++ b/sys/netpfil/ipfw/ip_dn_io.c @@ -43,6 +43,7 @@ #include <sys/priv.h> #include <sys/proc.h> #include <sys/rwlock.h> +#include <sys/sdt.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/sysctl.h> @@ -70,6 +71,9 @@ #endif #include <netpfil/ipfw/dn_sched.h> +SDT_PROVIDER_DEFINE(dummynet); +SDT_PROBE_DEFINE2(dummynet, , , drop, "struct mbuf *", "struct dn_queue *"); + /* * We keep a private variable for the simulation time, but we could * probably use an existing one ("softticks" in sys/kern/kern_timeout.c) @@ -545,6 +549,7 @@ dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop) drop: V_dn_cfg.io_pkt_drop++; + SDT_PROBE2(dummynet, , , drop, m, q); q->ni.drops++; ni->drops++; FREE_PKT(m); @@ -1001,6 +1006,7 @@ done: dropit: V_dn_cfg.io_pkt_drop++; + SDT_PROBE2(dummynet, , , drop, m, q); DN_BH_WUNLOCK(); if (m) FREE_PKT(m); diff --git a/sys/netpfil/ipfw/ip_dn_private.h b/sys/netpfil/ipfw/ip_dn_private.h index 756a997b6ec3..9a43b86791e0 100644 --- a/sys/netpfil/ipfw/ip_dn_private.h +++ b/sys/netpfil/ipfw/ip_dn_private.h @@ -437,15 +437,7 @@ struct copy_args { }; struct sockopt; -int ip_dummynet_compat(struct sockopt *sopt); -int dummynet_get(struct sockopt *sopt, void **compat); -int dn_c_copy_q (void *_ni, void *arg); -int dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq); -int dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq); -int dn_compat_copy_queue(struct copy_args *a, void *_o); -int dn_compat_copy_pipe(struct copy_args *a, void *_o); -int copy_data_helper_compat(void *_o, void *_arg); -int dn_compat_calc_size(void); +int dummynet_get(struct sockopt *sopt); int do_config(void *p, size_t l); /* function to drain idle object */ diff --git a/sys/netpfil/ipfw/ip_dummynet.c b/sys/netpfil/ipfw/ip_dummynet.c index b3f52322425f..61442c617753 100644 --- a/sys/netpfil/ipfw/ip_dummynet.c +++ b/sys/netpfil/ipfw/ip_dummynet.c @@ -1150,7 +1150,7 @@ copy_data_helper(void *_o, void *_arg) return 0; /* not a pipe */ /* see if the object is within one of our ranges */ - for (;r < lim; r += 2) { + for (; r < lim; r += 2) { if (n < r[0] || n > r[1]) continue; /* Found a valid entry, copy and we are done */ @@ -1183,7 +1183,7 @@ copy_data_helper(void *_o, void *_arg) if (n >= DN_MAX_ID) return 0; /* see if the object is within one of our ranges */ - for (;r < lim; r += 2) { + for (; r < lim; r += 2) { if (n < r[0] || n > r[1]) continue; if (copy_flowset(a, fs, 0)) @@ -2198,9 +2198,6 @@ compute_space(struct dn_id *cmd, struct copy_args *a) case DN_FS: /* queue show */ x = DN_C_FS | DN_C_QUEUE; break; - case DN_GET_COMPAT: /* compatibility mode */ - need = dn_compat_calc_size(); - break; } a->flags = x; if (x & DN_C_SCH) { @@ -2226,11 +2223,9 @@ compute_space(struct dn_id *cmd, struct copy_args *a) } /* - * If compat != NULL dummynet_get is called in compatibility mode. - * *compat will be the pointer to the buffer to pass to ipfw */ int -dummynet_get(struct sockopt *sopt, void **compat) +dummynet_get(struct sockopt *sopt) { int have, i, need, error; char *start = NULL, *buf; @@ -2248,37 +2243,28 @@ dummynet_get(struct sockopt *sopt, void **compat) cmd = &r.o; - if (!compat) { - /* copy at least an oid, and possibly a full object */ - error = sooptcopyin(sopt, cmd, sizeof(r), sizeof(*cmd)); - sopt->sopt_valsize = sopt_valsize; - if (error) - goto done; - l = cmd->len; + /* copy at least an oid, and possibly a full object */ + error = sooptcopyin(sopt, cmd, sizeof(r), sizeof(*cmd)); + sopt->sopt_valsize = sopt_valsize; + if (error) + goto done; + l = cmd->len; #ifdef EMULATE_SYSCTL - /* sysctl emulation. */ - if (cmd->type == DN_SYSCTL_GET) - return kesysctl_emu_get(sopt); + /* sysctl emulation. */ + if (cmd->type == DN_SYSCTL_GET) + return kesysctl_emu_get(sopt); #endif - if (l > sizeof(r)) { - /* request larger than default, allocate buffer */ - cmd = malloc(l, M_DUMMYNET, M_NOWAIT); - if (cmd == NULL) { - error = ENOMEM; - goto done; - } - error = sooptcopyin(sopt, cmd, l, l); - sopt->sopt_valsize = sopt_valsize; - if (error) - goto done; + if (l > sizeof(r)) { + /* request larger than default, allocate buffer */ + cmd = malloc(l, M_DUMMYNET, M_NOWAIT); + if (cmd == NULL) { + error = ENOMEM; + goto done; } - } else { /* compatibility */ - error = 0; - cmd->type = DN_CMD_GET; - cmd->len = sizeof(struct dn_id); - cmd->subtype = DN_GET_COMPAT; - // cmd->id = sopt_valsize; - D("compatibility mode"); + error = sooptcopyin(sopt, cmd, l, l); + sopt->sopt_valsize = sopt_valsize; + if (error) + goto done; } #ifdef NEW_AQM @@ -2337,12 +2323,7 @@ dummynet_get(struct sockopt *sopt, void **compat) } if (start == NULL) { - if (compat) { - *compat = NULL; - error = 1; // XXX - } else { - error = sooptcopyout(sopt, cmd, sizeof(*cmd)); - } + error = sooptcopyout(sopt, cmd, sizeof(*cmd)); goto done; } ND("have %d:%d sched %d, %d:%d links %d, %d:%d flowsets %d, " @@ -2355,35 +2336,20 @@ dummynet_get(struct sockopt *sopt, void **compat) sopt->sopt_valsize = sopt_valsize; a.type = cmd->subtype; - if (compat == NULL) { - memcpy(start, cmd, sizeof(*cmd)); - ((struct dn_id*)(start))->len = sizeof(struct dn_id); - buf = start + sizeof(*cmd); - } else - buf = start; + memcpy(start, cmd, sizeof(*cmd)); + ((struct dn_id*)(start))->len = sizeof(struct dn_id); + buf = start + sizeof(*cmd); a.start = &buf; a.end = start + have; /* start copying other objects */ - if (compat) { - a.type = DN_COMPAT_PIPE; - dn_ht_scan(V_dn_cfg.schedhash, copy_data_helper_compat, &a); - a.type = DN_COMPAT_QUEUE; - dn_ht_scan(V_dn_cfg.fshash, copy_data_helper_compat, &a); - } else if (a.type == DN_FS) { + if (a.type == DN_FS) { dn_ht_scan(V_dn_cfg.fshash, copy_data_helper, &a); } else { dn_ht_scan(V_dn_cfg.schedhash, copy_data_helper, &a); } DN_BH_WUNLOCK(); - if (compat) { - *compat = start; - sopt->sopt_valsize = buf - start; - /* free() is done by ip_dummynet_compat() */ - start = NULL; //XXX hack - } else { - error = sooptcopyout(sopt, start, buf - start); - } + error = sooptcopyout(sopt, start, buf - start); done: if (cmd != &r.o) free(cmd, M_DUMMYNET); @@ -2519,17 +2485,9 @@ ip_dn_ctl(struct sockopt *sopt) error = EINVAL; break; - case IP_DUMMYNET_FLUSH: - case IP_DUMMYNET_CONFIGURE: - case IP_DUMMYNET_DEL: /* remove a pipe or queue */ - case IP_DUMMYNET_GET: - D("dummynet: compat option %d", sopt->sopt_name); - error = ip_dummynet_compat(sopt); - break; - case IP_DUMMYNET3: if (sopt->sopt_dir == SOPT_GET) { - error = dummynet_get(sopt, NULL); + error = dummynet_get(sopt); break; } l = sopt->sopt_valsize; diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index b59d8d08bf80..d15d7760d7f1 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -3578,11 +3578,9 @@ sysctl_ipfw_tables_sets(SYSCTL_HANDLER_ARGS) /* * Stuff that must be initialised only on boot or module load */ -static int -ipfw_init(void) +static void +ipfw_init(void *dummy __unused) { - int error = 0; - /* * Only print out this stuff the first time around, * when called from the sysinit code. @@ -3627,14 +3625,13 @@ ipfw_init(void) ipfw_init_sopt_handler(); ipfw_init_obj_rewriter(); ipfw_iface_init(); - return (error); } /* * Called for the removal of the last instance only on module unload. */ static void -ipfw_destroy(void) +ipfw_destroy(void *dummy __unused) { ipfw_iface_destroy(); diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c index 1e2ff1bca290..8bd27f6885ab 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -999,9 +999,11 @@ ipfw_nat_del(struct sockopt *sopt) { struct cfg_nat *ptr; struct ip_fw_chain *chain = &V_layer3_chain; - int i; + int error, i; - sooptcopyin(sopt, &i, sizeof i, sizeof i); + error = sooptcopyin(sopt, &i, sizeof i, sizeof i); + if (error != 0) + return (error); /* XXX validate i */ IPFW_UH_WLOCK(chain); ptr = lookup_nat(&chain->nat, i); @@ -1104,7 +1106,7 @@ ipfw_nat_get_log(struct sockopt *sopt) { uint8_t *data; struct cfg_nat *ptr; - int i, size; + int error, i, size; struct ip_fw_chain *chain; IPFW_RLOCK_TRACKER; @@ -1134,9 +1136,9 @@ ipfw_nat_get_log(struct sockopt *sopt) i += LIBALIAS_BUF_SIZE; } IPFW_RUNLOCK(chain); - sooptcopyout(sopt, data, size); + error = sooptcopyout(sopt, data, size); free(data, M_IPFW); - return(0); + return (error); } static int @@ -1166,7 +1168,7 @@ vnet_ipfw_nat_uninit(const void *arg __unused) } static void -ipfw_nat_init(void) +ipfw_nat_init(void *dummy __unused) { /* init ipfw hooks */ @@ -1183,7 +1185,7 @@ ipfw_nat_init(void) } static void -ipfw_nat_destroy(void) +ipfw_nat_destroy(void *dummy __unused) { EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); diff --git a/sys/netpfil/ipfw/pmod/tcpmod.c b/sys/netpfil/ipfw/pmod/tcpmod.c index 0338dc792c64..50074ee98cca 100644 --- a/sys/netpfil/ipfw/pmod/tcpmod.c +++ b/sys/netpfil/ipfw/pmod/tcpmod.c @@ -57,7 +57,8 @@ VNET_DEFINE_STATIC(uint32_t, tcpmod_setmss_eid) = 0; #define V_tcpmod_setmss_eid VNET(tcpmod_setmss_eid) static int -tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss) +tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss, + int *done) { struct mbuf *m; u_char *cp; @@ -72,8 +73,10 @@ tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss) * TCP header with options. */ *mp = m = m_pullup(m, m->m_pkthdr.len); - if (m == NULL) + if (m == NULL) { + *done = 1; return (ret); + } } /* Parse TCP options. */ for (tlen -= sizeof(struct tcphdr), cp = (u_char *)(tcp + 1); @@ -114,7 +117,7 @@ tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss) #ifdef INET6 static int -tcpmod_ipv6_setmss(struct mbuf **mp, uint16_t mss) +tcpmod_ipv6_setmss(struct mbuf **mp, uint16_t mss, int *done) { struct ip6_hdr *ip6; struct ip6_hbh *hbh; @@ -142,13 +145,13 @@ tcpmod_ipv6_setmss(struct mbuf **mp, uint16_t mss) /* We must have TCP options and enough data in a packet. */ if (hlen <= sizeof(struct tcphdr) || hlen > plen) return (IP_FW_DENY); - return (tcpmod_setmss(mp, tcp, hlen, mss)); + return (tcpmod_setmss(mp, tcp, hlen, mss, done)); } #endif /* INET6 */ #ifdef INET static int -tcpmod_ipv4_setmss(struct mbuf **mp, uint16_t mss) +tcpmod_ipv4_setmss(struct mbuf **mp, uint16_t mss, int *done) { struct tcphdr *tcp; struct ip *ip; @@ -162,7 +165,7 @@ tcpmod_ipv4_setmss(struct mbuf **mp, uint16_t mss) /* We must have TCP options and enough data in a packet. */ if (hlen <= sizeof(struct tcphdr) || hlen > plen) return (IP_FW_DENY); - return (tcpmod_setmss(mp, tcp, hlen, mss)); + return (tcpmod_setmss(mp, tcp, hlen, mss, done)); } #endif /* INET */ @@ -206,19 +209,23 @@ ipfw_tcpmod(struct ip_fw_chain *chain, struct ip_fw_args *args, switch (args->f_id.addr_type) { #ifdef INET case 4: - ret = tcpmod_ipv4_setmss(&args->m, htons(icmd->arg1)); + ret = tcpmod_ipv4_setmss(&args->m, htons(icmd->arg1), + done); break; #endif #ifdef INET6 case 6: - ret = tcpmod_ipv6_setmss(&args->m, htons(icmd->arg1)); + ret = tcpmod_ipv6_setmss(&args->m, htons(icmd->arg1), + done); break; #endif } /* * We return zero in both @ret and @done on success, and ipfw_chk() * will update rule counters. Otherwise a packet will not be matched - * by rule. + * by rule. We passed @done around above in case we hit a fatal error + * somewhere, we'll return non-zero but signal that rule processing + * cannot succeed. */ return (ret); } diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c index 66bc99df2afa..de69ecbb0985 100644 --- a/sys/netpfil/pf/if_pfsync.c +++ b/sys/netpfil/pf/if_pfsync.c @@ -546,6 +546,9 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version) PF_RULES_RASSERT(); + if (strnlen(sp->pfs_1301.ifname, IFNAMSIZ) == IFNAMSIZ) + return (EINVAL); + if (sp->pfs_1301.creatorid == 0) { if (V_pf_status.debug >= PF_DEBUG_MISC) printf("%s: invalid creator id: %08x\n", __func__, diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index ec6960180413..a39f5fe58cd6 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -344,10 +344,12 @@ static int pf_test_eth_rule(int, struct pfi_kkif *, struct mbuf **); static int pf_test_rule(struct pf_krule **, struct pf_kstate **, struct pf_pdesc *, struct pf_krule **, - struct pf_kruleset **, u_short *, struct inpcb *); + struct pf_kruleset **, u_short *, struct inpcb *, + struct pf_krule_slist *); static int pf_create_state(struct pf_krule *, struct pf_test_ctx *, - struct pf_kstate **, u_int16_t, u_int16_t); + struct pf_kstate **, u_int16_t, u_int16_t, + struct pf_krule_slist *match_rules); static int pf_state_key_addr_setup(struct pf_pdesc *, struct pf_state_key_cmp *, int); static int pf_tcp_track_full(struct pf_kstate *, @@ -393,7 +395,7 @@ static bool pf_src_connlimit(struct pf_kstate *); static int pf_match_rcvif(struct mbuf *, struct pf_krule *); static void pf_counters_inc(int, struct pf_pdesc *, struct pf_kstate *, struct pf_krule *, - struct pf_krule *); + struct pf_krule *, struct pf_krule_slist *); static void pf_log_matches(struct pf_pdesc *, struct pf_krule *, struct pf_krule *, struct pf_kruleset *, struct pf_krule_slist *); @@ -489,26 +491,30 @@ BOUND_IFACE(struct pf_kstate *st, struct pf_pdesc *pd) counter_u64_add(s->anchor->states_cur, 1); \ counter_u64_add(s->anchor->states_tot, 1); \ } \ - if (s->nat_rule != NULL) { \ - counter_u64_add(s->nat_rule->states_cur, 1);\ - counter_u64_add(s->nat_rule->states_tot, 1);\ + if (s->nat_rule != NULL && s->nat_rule != s->rule) { \ + counter_u64_add(s->nat_rule->states_cur, 1); \ + counter_u64_add(s->nat_rule->states_tot, 1); \ } \ SLIST_FOREACH(mrm, &s->match_rules, entry) { \ - counter_u64_add(mrm->r->states_cur, 1); \ - counter_u64_add(mrm->r->states_tot, 1); \ + if (s->nat_rule != mrm->r) { \ + counter_u64_add(mrm->r->states_cur, 1); \ + counter_u64_add(mrm->r->states_tot, 1); \ + } \ } \ } while (0) #define STATE_DEC_COUNTERS(s) \ do { \ struct pf_krule_item *mrm; \ - if (s->nat_rule != NULL) \ - counter_u64_add(s->nat_rule->states_cur, -1);\ - if (s->anchor != NULL) \ - counter_u64_add(s->anchor->states_cur, -1); \ counter_u64_add(s->rule->states_cur, -1); \ + if (s->anchor != NULL) \ + counter_u64_add(s->anchor->states_cur, -1); \ + if (s->nat_rule != NULL && s->nat_rule != s->rule) \ + counter_u64_add(s->nat_rule->states_cur, -1); \ SLIST_FOREACH(mrm, &s->match_rules, entry) \ - counter_u64_add(mrm->r->states_cur, -1); \ + if (s->nat_rule != mrm->r) { \ + counter_u64_add(mrm->r->states_cur, -1);\ + } \ } while (0) MALLOC_DEFINE(M_PFHASH, "pf_hash", "pf(4) hash header structures"); @@ -1001,7 +1007,7 @@ pf_src_node_exists(struct pf_ksrc_node **sn, struct pf_srchash *sh) return (false); } -static void +void pf_free_src_node(struct pf_ksrc_node *sn) { @@ -2869,20 +2875,24 @@ pf_alloc_state(int flags) return (uma_zalloc(V_pf_state_z, flags | M_ZERO)); } +static __inline void +pf_free_match_rules(struct pf_krule_slist *match_rules) { + struct pf_krule_item *ri; + + while ((ri = SLIST_FIRST(match_rules))) { + SLIST_REMOVE_HEAD(match_rules, entry); + free(ri, M_PF_RULE_ITEM); + } +} + void pf_free_state(struct pf_kstate *cur) { - struct pf_krule_item *ri; - KASSERT(cur->refs == 0, ("%s: %p has refs", __func__, cur)); KASSERT(cur->timeout == PFTM_UNLINKED, ("%s: timeout %u", __func__, cur->timeout)); - while ((ri = SLIST_FIRST(&cur->match_rules))) { - SLIST_REMOVE_HEAD(&cur->match_rules, entry); - free(ri, M_PF_RULE_ITEM); - } - + pf_free_match_rules(&(cur->match_rules)); pf_normalize_tcp_cleanup(cur); uma_zfree(V_pf_state_z, cur); pf_counter_u64_add(&V_pf_status.fcounters[FCNT_STATE_REMOVALS], 1); @@ -3354,7 +3364,7 @@ pf_change_ap(struct pf_pdesc *pd, struct pf_addr *a, u_int16_t *p, u_int16_t po; uint8_t u = pd->virtual_proto == IPPROTO_UDP; - MPASS(pd->pcksum); + MPASS(pd->pcksum != NULL); if (pd->af == AF_INET) { MPASS(pd->ip_sum); } @@ -4740,7 +4750,8 @@ pf_tag_packet(struct pf_pdesc *pd, int tag) } while (0) enum pf_test_status -pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r) +pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r, + struct pf_krule_slist *match_rules) { enum pf_test_status rv; @@ -4758,7 +4769,7 @@ pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r) struct pf_kanchor *child; rv = PF_TEST_OK; RB_FOREACH(child, pf_kanchor_node, &r->anchor->children) { - rv = pf_match_rule(ctx, &child->ruleset); + rv = pf_match_rule(ctx, &child->ruleset, match_rules); if ((rv == PF_TEST_QUICK) || (rv == PF_TEST_FAIL)) { /* * we either hit a rule with quick action @@ -4769,7 +4780,7 @@ pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r) } } } else { - rv = pf_match_rule(ctx, &r->anchor->ruleset); + rv = pf_match_rule(ctx, &r->anchor->ruleset, match_rules); /* * Unless errors occured, stop iff any rule matched * within quick anchors. @@ -5618,9 +5629,10 @@ pf_rule_apply_nat(struct pf_test_ctx *ctx, struct pf_krule *r) } enum pf_test_status -pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset) +pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset, + struct pf_krule_slist *match_rules) { - struct pf_krule_item *ri; + struct pf_krule_item *ri, *rt; struct pf_krule *r; struct pf_krule *save_a; struct pf_kruleset *save_aruleset; @@ -5777,11 +5789,14 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset) return (PF_TEST_FAIL); } ri->r = r; - SLIST_INSERT_HEAD(&ctx->rules, ri, entry); - pf_counter_u64_critical_enter(); - pf_counter_u64_add_protected(&r->packets[pd->dir == PF_OUT], 1); - pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len); - pf_counter_u64_critical_exit(); + + if (SLIST_EMPTY(match_rules)) { + SLIST_INSERT_HEAD(match_rules, ri, entry); + } else { + SLIST_INSERT_AFTER(rt, ri, entry); + } + rt = ri; + pf_rule_to_actions(r, &pd->act); if (r->log) PFLOG_PACKET(r->action, PFRES_MATCH, r, @@ -5805,7 +5820,7 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset) ctx->arsm = ctx->aruleset; } if (pd->act.log & PF_LOG_MATCHES) - pf_log_matches(pd, r, ctx->a, ruleset, &ctx->rules); + pf_log_matches(pd, r, ctx->a, ruleset, match_rules); if (r->quick) { ctx->test_status = PF_TEST_QUICK; break; @@ -5822,7 +5837,7 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset) * Note: we don't need to restore if we are not going * to continue with ruleset evaluation. */ - if (pf_step_into_anchor(ctx, r) != PF_TEST_OK) { + if (pf_step_into_anchor(ctx, r, match_rules) != PF_TEST_OK) { break; } ctx->a = save_a; @@ -5838,11 +5853,11 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset) static int pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pf_pdesc *pd, struct pf_krule **am, - struct pf_kruleset **rsm, u_short *reason, struct inpcb *inp) + struct pf_kruleset **rsm, u_short *reason, struct inpcb *inp, + struct pf_krule_slist *match_rules) { struct pf_krule *r = NULL; struct pf_kruleset *ruleset = NULL; - struct pf_krule_item *ri; struct pf_test_ctx ctx; u_short transerror; int action = PF_PASS; @@ -5859,7 +5874,6 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, ctx.rsm = rsm; ctx.th = &pd->hdr.tcp; ctx.reason = *reason; - SLIST_INIT(&ctx.rules); pf_addrcpy(&pd->nsaddr, pd->src, pd->af); pf_addrcpy(&pd->ndaddr, pd->dst, pd->af); @@ -5951,44 +5965,50 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, ctx.nat_pool = &(ctx.nr->rdr); } - ruleset = &pf_main_ruleset; - rv = pf_match_rule(&ctx, ruleset); - if (rv == PF_TEST_FAIL) { - /* - * Reason has been set in pf_match_rule() already. - */ - goto cleanup; - } - - r = *ctx.rm; /* matching rule */ - ctx.a = *ctx.am; /* rule that defines an anchor containing 'r' */ - ruleset = *ctx.rsm; /* ruleset of the anchor defined by the rule 'a' */ - ctx.aruleset = ctx.arsm; /* ruleset of the 'a' rule itself */ + *ctx.rm = &V_pf_default_rule; + if (ctx.nr && ctx.nr->natpass) { + r = ctx.nr; + ruleset = *ctx.rsm; + } else { + ruleset = &pf_main_ruleset; + rv = pf_match_rule(&ctx, ruleset, match_rules); + if (rv == PF_TEST_FAIL) { + /* + * Reason has been set in pf_match_rule() already. + */ + goto cleanup; + } - REASON_SET(&ctx.reason, PFRES_MATCH); + r = *ctx.rm; /* matching rule */ + ctx.a = *ctx.am; /* rule that defines an anchor containing 'r' */ + ruleset = *ctx.rsm; /* ruleset of the anchor defined by the rule 'a' */ + ctx.aruleset = ctx.arsm; /* ruleset of the 'a' rule itself */ - /* apply actions for last matching pass/block rule */ - pf_rule_to_actions(r, &pd->act); - transerror = pf_rule_apply_nat(&ctx, r); - switch (transerror) { - case PFRES_MATCH: - /* Translation action found in rule and applied successfully */ - case PFRES_MAX: - /* No translation action found in rule */ - break; - default: - /* Translation action found in rule but failed to apply */ - REASON_SET(&ctx.reason, transerror); - goto cleanup; + /* apply actions for last matching pass/block rule */ + pf_rule_to_actions(r, &pd->act); + transerror = pf_rule_apply_nat(&ctx, r); + switch (transerror) { + case PFRES_MATCH: + /* Translation action found in rule and applied successfully */ + case PFRES_MAX: + /* No translation action found in rule */ + break; + default: + /* Translation action found in rule but failed to apply */ + REASON_SET(&ctx.reason, transerror); + goto cleanup; + } } + REASON_SET(&ctx.reason, PFRES_MATCH); + if (r->log) { if (ctx.rewrite) m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any); PFLOG_PACKET(r->action, ctx.reason, r, ctx.a, ruleset, pd, 1, NULL); } if (pd->act.log & PF_LOG_MATCHES) - pf_log_matches(pd, r, ctx.a, ruleset, &ctx.rules); + pf_log_matches(pd, r, ctx.a, ruleset, match_rules); if (pd->virtual_proto != PF_VPROTO_FRAGMENT && (r->action == PF_DROP) && ((r->rule_flag & PFRULE_RETURNRST) || @@ -6033,7 +6053,8 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, (pd->flags & PFDESC_TCP_NORM)))) { bool nat64; - action = pf_create_state(r, &ctx, sm, bproto_sum, bip_sum); + action = pf_create_state(r, &ctx, sm, bproto_sum, bip_sum, + match_rules); ctx.sk = ctx.nk = NULL; if (action != PF_PASS) { pf_udp_mapping_release(ctx.udp_mapping); @@ -6079,11 +6100,6 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, action = PF_AFRT; } } else { - while ((ri = SLIST_FIRST(&ctx.rules))) { - SLIST_REMOVE_HEAD(&ctx.rules, entry); - free(ri, M_PF_RULE_ITEM); - } - uma_zfree(V_pf_state_key_z, ctx.sk); uma_zfree(V_pf_state_key_z, ctx.nk); ctx.sk = ctx.nk = NULL; @@ -6111,11 +6127,6 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, return (action); cleanup: - while ((ri = SLIST_FIRST(&ctx.rules))) { - SLIST_REMOVE_HEAD(&ctx.rules, entry); - free(ri, M_PF_RULE_ITEM); - } - uma_zfree(V_pf_state_key_z, ctx.sk); uma_zfree(V_pf_state_key_z, ctx.nk); pf_udp_mapping_release(ctx.udp_mapping); @@ -6126,7 +6137,8 @@ cleanup: static int pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, - struct pf_kstate **sm, u_int16_t bproto_sum, u_int16_t bip_sum) + struct pf_kstate **sm, u_int16_t bproto_sum, u_int16_t bip_sum, + struct pf_krule_slist *match_rules) { struct pf_pdesc *pd = ctx->pd; struct pf_kstate *s = NULL; @@ -6140,7 +6152,6 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, struct tcphdr *th = &pd->hdr.tcp; u_int16_t mss = V_tcp_mssdflt; u_short sn_reason; - struct pf_krule_item *ri; /* check maximums */ if (r->max_states && @@ -6192,7 +6203,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, s->rule = r; s->nat_rule = ctx->nr; s->anchor = ctx->a; - memcpy(&s->match_rules, &ctx->rules, sizeof(s->match_rules)); + s->match_rules = *match_rules; memcpy(&s->act, &pd->act, sizeof(struct pf_rule_actions)); if (pd->act.allow_opts) @@ -6356,11 +6367,6 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, return (PF_PASS); csfailed: - while ((ri = SLIST_FIRST(&ctx->rules))) { - SLIST_REMOVE_HEAD(&ctx->rules, entry); - free(ri, M_PF_RULE_ITEM); - } - uma_zfree(V_pf_state_key_z, ctx->sk); uma_zfree(V_pf_state_key_z, ctx->nk); @@ -7518,6 +7524,7 @@ static void pf_sctp_multihome_delayed(struct pf_pdesc *pd, struct pfi_kkif *kif, struct pf_kstate *s, int action) { + struct pf_krule_slist match_rules; struct pf_sctp_multihome_job *j, *tmp; struct pf_sctp_source *i; int ret; @@ -7565,8 +7572,14 @@ again: if (s->rule->rule_flag & PFRULE_ALLOW_RELATED) { j->pd.related_rule = s->rule; } + SLIST_INIT(&match_rules); ret = pf_test_rule(&r, &sm, - &j->pd, &ra, &rs, &reason, NULL); + &j->pd, &ra, &rs, &reason, NULL, &match_rules); + /* + * Nothing to do about match rules, the processed + * packet has already increased the counters. + */ + pf_free_match_rules(&match_rules); PF_RULES_RUNLOCK(); SDT_PROBE4(pf, sctp, multihome, test, kif, r, j->pd.m, ret); if (ret != PF_DROP && sm != NULL) { @@ -7627,6 +7640,7 @@ again: nj->pd.m = j->pd.m; nj->op = j->op; + MPASS(nj->pd.pcksum); TAILQ_INSERT_TAIL(&pd->sctp_multihome_jobs, nj, next); } PF_SCTP_ENDPOINTS_UNLOCK(); @@ -7746,6 +7760,7 @@ pf_multihome_scan(int start, int len, struct pf_pdesc *pd, int op) job->pd.m = pd->m; job->op = op; + MPASS(job->pd.pcksum); TAILQ_INSERT_TAIL(&pd->sctp_multihome_jobs, job, next); break; } @@ -7779,6 +7794,7 @@ pf_multihome_scan(int start, int len, struct pf_pdesc *pd, int op) job->pd.m = pd->m; job->op = op; + MPASS(job->pd.pcksum); TAILQ_INSERT_TAIL(&pd->sctp_multihome_jobs, job, next); break; } @@ -9221,28 +9237,41 @@ pf_route(struct pf_krule *r, struct ifnet *oifp, } } - if (r->rt == PF_DUPTO) + if (r->rt == PF_DUPTO || (pd->af != pd->naf && s->direction == PF_IN)) skip_test = true; - if (pd->dir == PF_IN && !skip_test) { - if (pf_test(AF_INET, PF_OUT, PFIL_FWD, ifp, &m0, inp, - &pd->act) != PF_PASS) { - action = PF_DROP; - SDT_PROBE1(pf, ip, route_to, drop, __LINE__); - goto bad; - } else if (m0 == NULL) { - action = PF_DROP; - SDT_PROBE1(pf, ip, route_to, drop, __LINE__); - goto done; - } - if (m0->m_len < sizeof(struct ip)) { - DPFPRINTF(PF_DEBUG_URGENT, - "%s: m0->m_len < sizeof(struct ip)", __func__); - SDT_PROBE1(pf, ip, route_to, drop, __LINE__); - action = PF_DROP; - goto bad; + if (pd->dir == PF_IN) { + if (skip_test) { + struct pfi_kkif *out_kif = (struct pfi_kkif *)ifp->if_pf_kif; + MPASS(s != NULL); + pf_counter_u64_critical_enter(); + pf_counter_u64_add_protected( + &out_kif->pfik_bytes[pd->naf == AF_INET6][1] + [action != PF_PASS && action != PF_AFRT], pd->tot_len); + pf_counter_u64_add_protected( + &out_kif->pfik_packets[pd->naf == AF_INET6][1] + [action != PF_PASS && action != PF_AFRT], 1); + pf_counter_u64_critical_exit(); + } else { + if (pf_test(AF_INET, PF_OUT, PFIL_FWD, ifp, &m0, inp, + &pd->act) != PF_PASS) { + action = PF_DROP; + SDT_PROBE1(pf, ip, route_to, drop, __LINE__); + goto bad; + } else if (m0 == NULL) { + action = PF_DROP; + SDT_PROBE1(pf, ip, route_to, drop, __LINE__); + goto done; + } + if (m0->m_len < sizeof(struct ip)) { + DPFPRINTF(PF_DEBUG_URGENT, + "%s: m0->m_len < sizeof(struct ip)", __func__); + SDT_PROBE1(pf, ip, route_to, drop, __LINE__); + action = PF_DROP; + goto bad; + } + ip = mtod(m0, struct ip *); } - ip = mtod(m0, struct ip *); } if (ifp->if_flags & IFF_LOOPBACK) @@ -9538,29 +9567,42 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp, } } - if (r->rt == PF_DUPTO) + if (r->rt == PF_DUPTO || (pd->af != pd->naf && s->direction == PF_IN)) skip_test = true; - if (pd->dir == PF_IN && !skip_test) { - if (pf_test(AF_INET6, PF_OUT, PFIL_FWD | PF_PFIL_NOREFRAGMENT, - ifp, &m0, inp, &pd->act) != PF_PASS) { - action = PF_DROP; - SDT_PROBE1(pf, ip6, route_to, drop, __LINE__); - goto bad; - } else if (m0 == NULL) { - action = PF_DROP; - SDT_PROBE1(pf, ip6, route_to, drop, __LINE__); - goto done; - } - if (m0->m_len < sizeof(struct ip6_hdr)) { - DPFPRINTF(PF_DEBUG_URGENT, - "%s: m0->m_len < sizeof(struct ip6_hdr)", - __func__); - action = PF_DROP; - SDT_PROBE1(pf, ip6, route_to, drop, __LINE__); - goto bad; + if (pd->dir == PF_IN) { + if (skip_test) { + struct pfi_kkif *out_kif = (struct pfi_kkif *)ifp->if_pf_kif; + MPASS(s != NULL); + pf_counter_u64_critical_enter(); + pf_counter_u64_add_protected( + &out_kif->pfik_bytes[pd->naf == AF_INET6][1] + [action != PF_PASS && action != PF_AFRT], pd->tot_len); + pf_counter_u64_add_protected( + &out_kif->pfik_packets[pd->naf == AF_INET6][1] + [action != PF_PASS && action != PF_AFRT], 1); + pf_counter_u64_critical_exit(); + } else { + if (pf_test(AF_INET6, PF_OUT, PFIL_FWD | PF_PFIL_NOREFRAGMENT, + ifp, &m0, inp, &pd->act) != PF_PASS) { + action = PF_DROP; + SDT_PROBE1(pf, ip6, route_to, drop, __LINE__); + goto bad; + } else if (m0 == NULL) { + action = PF_DROP; + SDT_PROBE1(pf, ip6, route_to, drop, __LINE__); + goto done; + } + if (m0->m_len < sizeof(struct ip6_hdr)) { + DPFPRINTF(PF_DEBUG_URGENT, + "%s: m0->m_len < sizeof(struct ip6_hdr)", + __func__); + action = PF_DROP; + SDT_PROBE1(pf, ip6, route_to, drop, __LINE__); + goto bad; + } + ip6 = mtod(m0, struct ip6_hdr *); } - ip6 = mtod(m0, struct ip6_hdr *); } if (ifp->if_flags & IFF_LOOPBACK) @@ -10410,28 +10452,28 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, __func__); *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } h = mtod(pd->m, struct ip *); if (pd->m->m_pkthdr.len < ntohs(h->ip_len)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } if (pf_normalize_ip(reason, pd) != PF_PASS) { /* We do IP header normalization and packet reassembly here */ *m0 = pd->m; *action = PF_DROP; - return (-1); + return (PF_DROP); } *m0 = pd->m; h = mtod(pd->m, struct ip *); if (pf_walk_header(pd, h, reason) != PF_PASS) { *action = PF_DROP; - return (-1); + return (PF_DROP); } pd->src = (struct pf_addr *)&h->ip_src; @@ -10461,7 +10503,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, ", pullup failed", __func__); *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } h = mtod(pd->m, struct ip6_hdr *); @@ -10469,7 +10511,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, sizeof(struct ip6_hdr) + ntohs(h->ip6_plen)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } /* @@ -10478,12 +10520,12 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, */ if (htons(h->ip6_plen) == 0) { *action = PF_DROP; - return (-1); + return (PF_DROP); } if (pf_walk_header6(pd, h, reason) != PF_PASS) { *action = PF_DROP; - return (-1); + return (PF_DROP); } h = mtod(pd->m, struct ip6_hdr *); @@ -10505,13 +10547,13 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, PF_PASS) { *m0 = pd->m; *action = PF_DROP; - return (-1); + return (PF_DROP); } *m0 = pd->m; if (pd->m == NULL) { /* packet sits in reassembly queue, no error */ *action = PF_PASS; - return (-1); + return (PF_DROP); } /* Update pointers into the packet. */ @@ -10523,7 +10565,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, if (pf_walk_header6(pd, h, reason) != PF_PASS) { *action = PF_DROP; - return (-1); + return (PF_DROP); } if (m_tag_find(pd->m, PACKET_TAG_PF_REASSEMBLED, NULL) != NULL) { @@ -10553,7 +10595,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, reason, af)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } pd->hdrlen = sizeof(*th); pd->p_len = pd->tot_len - pd->off - (th->th_off << 2); @@ -10569,7 +10611,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, reason, af)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } pd->hdrlen = sizeof(*uh); if (uh->uh_dport == 0 || @@ -10577,7 +10619,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, ntohs(uh->uh_ulen) < sizeof(struct udphdr)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } pd->sport = &uh->uh_sport; pd->dport = &uh->uh_dport; @@ -10589,7 +10631,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, reason, af)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } pd->hdrlen = sizeof(pd->hdr.sctp); pd->p_len = pd->tot_len - pd->off; @@ -10599,19 +10641,23 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, if (pd->hdr.sctp.src_port == 0 || pd->hdr.sctp.dest_port == 0) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); - } - if (pf_scan_sctp(pd) != PF_PASS) { - *action = PF_DROP; - REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } + /* * Placeholder. The SCTP checksum is 32-bits, but * pf_test_state() expects to update a 16-bit checksum. * Provide a dummy value which we'll subsequently ignore. + * Do this before pf_scan_sctp() so any jobs we enqueue + * have a pcksum set. */ pd->pcksum = &pd->sctp_dummy_sum; + + if (pf_scan_sctp(pd) != PF_PASS) { + *action = PF_DROP; + REASON_SET(reason, PFRES_SHORT); + return (PF_DROP); + } break; } case IPPROTO_ICMP: { @@ -10619,7 +10665,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, reason, af)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } pd->pcksum = &pd->hdr.icmp.icmp_cksum; pd->hdrlen = ICMP_MINLEN; @@ -10633,7 +10679,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, reason, af)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } /* ICMP headers we look further into to match state */ switch (pd->hdr.icmp6.icmp6_type) { @@ -10659,7 +10705,7 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, reason, af)) { *action = PF_DROP; REASON_SET(reason, PFRES_SHORT); - return (-1); + return (PF_DROP); } pd->hdrlen = icmp_hlen; pd->pcksum = &pd->hdr.icmp6.icmp6_cksum; @@ -10682,111 +10728,173 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0, MPASS(pd->pcksum != NULL); - return (0); + return (PF_PASS); +} + +static __inline void +pf_rule_counters_inc(struct pf_pdesc *pd, struct pf_krule *r, int dir_out, + int op_pass, sa_family_t af, struct pf_addr *src_host, + struct pf_addr *dst_host) +{ + pf_counter_u64_add_protected(&(r->packets[dir_out]), 1); + pf_counter_u64_add_protected(&(r->bytes[dir_out]), pd->tot_len); + pf_update_timestamp(r); + + if (r->src.addr.type == PF_ADDR_TABLE) + pfr_update_stats(r->src.addr.p.tbl, src_host, af, + pd->tot_len, dir_out, op_pass, r->src.neg); + if (r->dst.addr.type == PF_ADDR_TABLE) + pfr_update_stats(r->dst.addr.p.tbl, dst_host, af, + pd->tot_len, dir_out, op_pass, r->dst.neg); } static void -pf_counters_inc(int action, struct pf_pdesc *pd, - struct pf_kstate *s, struct pf_krule *r, struct pf_krule *a) +pf_counters_inc(int action, struct pf_pdesc *pd, struct pf_kstate *s, + struct pf_krule *r, struct pf_krule *a, struct pf_krule_slist *match_rules) { - struct pf_krule *tr; - int dir = pd->dir; - int dirndx; + struct pf_krule_slist *mr = match_rules; + struct pf_krule_item *ri; + struct pf_krule *nr = NULL; + struct pf_addr *src_host = pd->src; + struct pf_addr *dst_host = pd->dst; + struct pf_state_key *key; + int dir_out = (pd->dir == PF_OUT); + int op_r_pass = (r->action == PF_PASS); + int op_pass = (action == PF_PASS || action == PF_AFRT); + int s_dir_in, s_dir_out, s_dir_rev; + sa_family_t af = pd->af; pf_counter_u64_critical_enter(); + + /* + * Set AF for interface counters, it will be later overwritten for + * rule and state counters with value from proper state key. + */ + if (action == PF_AFRT) { + MPASS(s != NULL); + if (s->direction == PF_OUT && dir_out) + af = pd->naf; + } + pf_counter_u64_add_protected( - &pd->kif->pfik_bytes[pd->af == AF_INET6][dir == PF_OUT][action != PF_PASS], + &pd->kif->pfik_bytes[af == AF_INET6][dir_out][!op_pass], pd->tot_len); pf_counter_u64_add_protected( - &pd->kif->pfik_packets[pd->af == AF_INET6][dir == PF_OUT][action != PF_PASS], + &pd->kif->pfik_packets[af == AF_INET6][dir_out][!op_pass], 1); - if (action == PF_PASS || action == PF_AFRT || r->action == PF_DROP) { - dirndx = (dir == PF_OUT); - pf_counter_u64_add_protected(&r->packets[dirndx], 1); - pf_counter_u64_add_protected(&r->bytes[dirndx], pd->tot_len); - pf_update_timestamp(r); + /* If the rule has failed to apply, don't increase its counters */ + if (!(op_pass || r->action == PF_DROP)) { + pf_counter_u64_critical_exit(); + return; + } + + if (s != NULL) { + PF_STATE_LOCK_ASSERT(s); + mr = &(s->match_rules); - if (a != NULL) { - pf_counter_u64_add_protected(&a->packets[dirndx], 1); - pf_counter_u64_add_protected(&a->bytes[dirndx], pd->tot_len); + /* + * For af-to on the inbound direction we can determine + * the direction of passing packet only by checking direction + * of AF translation. The af-to in "in" direction covers both + * the inbound and the outbound side of state tracking, + * so pd->dir is always PF_IN. We set dir_out and s_dir_rev + * in a way to count packets as if the state was outbound, + * because pfctl -ss shows the state with "->", as if it was + * oubound. + */ + if (action == PF_AFRT && s->direction == PF_IN) { + dir_out = (pd->naf == s->rule->naf); + s_dir_in = 1; + s_dir_out = 0; + s_dir_rev = (pd->naf == s->rule->af); + } else { + dir_out = (pd->dir == PF_OUT); + s_dir_in = (s->direction == PF_IN); + s_dir_out = (s->direction == PF_OUT); + s_dir_rev = (pd->dir != s->direction); } - if (s != NULL) { - struct pf_krule_item *ri; - if (s->nat_rule != NULL) { - pf_counter_u64_add_protected(&s->nat_rule->packets[dirndx], + /* pd->tot_len is a problematic with af-to rules. Sure, we can + * agree that it's the post-af-to packet length that was + * forwarded through a state, but what about tables which match + * on pre-af-to addresses? We don't have access the the original + * packet length anymore. + */ + s->packets[s_dir_rev]++; + s->bytes[s_dir_rev] += pd->tot_len; + + /* + * Source nodes are accessed unlocked here. But since we are + * operating with stateful tracking and the state is locked, + * those SNs could not have been freed. + */ + for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) { + if (s->sns[sn_type] != NULL) { + counter_u64_add( + s->sns[sn_type]->packets[dir_out], 1); - pf_counter_u64_add_protected(&s->nat_rule->bytes[dirndx], + counter_u64_add( + s->sns[sn_type]->bytes[dir_out], pd->tot_len); } - /* - * Source nodes are accessed unlocked here. - * But since we are operating with stateful tracking - * and the state is locked, those SNs could not have - * been freed. - */ - for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) { - if (s->sns[sn_type] != NULL) { - counter_u64_add( - s->sns[sn_type]->packets[dirndx], - 1); - counter_u64_add( - s->sns[sn_type]->bytes[dirndx], - pd->tot_len); - } - } - dirndx = (dir == s->direction) ? 0 : 1; - s->packets[dirndx]++; - s->bytes[dirndx] += pd->tot_len; - - SLIST_FOREACH(ri, &s->match_rules, entry) { - pf_counter_u64_add_protected(&ri->r->packets[dirndx], 1); - pf_counter_u64_add_protected(&ri->r->bytes[dirndx], pd->tot_len); + } - if (ri->r->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(ri->r->src.addr.p.tbl, - (s == NULL) ? pd->src : - &s->key[(s->direction == PF_IN)]-> - addr[(s->direction == PF_OUT)], - pd->af, pd->tot_len, dir == PF_OUT, - r->action == PF_PASS, ri->r->src.neg); - if (ri->r->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(ri->r->dst.addr.p.tbl, - (s == NULL) ? pd->dst : - &s->key[(s->direction == PF_IN)]-> - addr[(s->direction == PF_IN)], - pd->af, pd->tot_len, dir == PF_OUT, - r->action == PF_PASS, ri->r->dst.neg); + /* Start with pre-NAT addresses */ + key = s->key[(s->direction == PF_OUT)]; + src_host = &(key->addr[s_dir_out]); + dst_host = &(key->addr[s_dir_in]); + af = key->af; + if (s->nat_rule) { + /* Old-style NAT rules */ + if (s->nat_rule->action == PF_NAT || + s->nat_rule->action == PF_RDR || + s->nat_rule->action == PF_BINAT) { + nr = s->nat_rule; + pf_rule_counters_inc(pd, s->nat_rule, dir_out, + op_r_pass, af, src_host, dst_host); + /* Use post-NAT addresses from now on */ + key = s->key[s_dir_in]; + src_host = &(key->addr[s_dir_out]); + dst_host = &(key->addr[s_dir_in]); + af = key->af; } } + } - tr = r; - if (s != NULL && s->nat_rule != NULL && - r == &V_pf_default_rule) - tr = s->nat_rule; - - if (tr->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->src.addr.p.tbl, - (s == NULL) ? pd->src : - &s->key[(s->direction == PF_IN)]-> - addr[(s->direction == PF_OUT)], - pd->af, pd->tot_len, dir == PF_OUT, - r->action == PF_PASS, tr->src.neg); - if (tr->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->dst.addr.p.tbl, - (s == NULL) ? pd->dst : - &s->key[(s->direction == PF_IN)]-> - addr[(s->direction == PF_IN)], - pd->af, pd->tot_len, dir == PF_OUT, - r->action == PF_PASS, tr->dst.neg); + SLIST_FOREACH(ri, mr, entry) { + pf_rule_counters_inc(pd, ri->r, dir_out, op_r_pass, af, + src_host, dst_host); + if (s && s->nat_rule == ri->r) { + /* Use post-NAT addresses after a match NAT rule */ + key = s->key[s_dir_in]; + src_host = &(key->addr[s_dir_out]); + dst_host = &(key->addr[s_dir_in]); + af = key->af; + } } + + if (s == NULL) { + pf_free_match_rules(mr); + } + + if (a != NULL) { + pf_rule_counters_inc(pd, a, dir_out, op_r_pass, af, + src_host, dst_host); + } + + if (r != nr) { + pf_rule_counters_inc(pd, r, dir_out, op_r_pass, af, + src_host, dst_host); + } + pf_counter_u64_critical_exit(); } + static void pf_log_matches(struct pf_pdesc *pd, struct pf_krule *rm, struct pf_krule *am, struct pf_kruleset *ruleset, - struct pf_krule_slist *matchrules) + struct pf_krule_slist *match_rules) { struct pf_krule_item *ri; @@ -10794,7 +10902,7 @@ pf_log_matches(struct pf_pdesc *pd, struct pf_krule *rm, if (rm->log & PF_LOG_MATCHES) return; - SLIST_FOREACH(ri, matchrules, entry) + SLIST_FOREACH(ri, match_rules, entry) if (ri->r->log & PF_LOG_MATCHES) PFLOG_PACKET(rm->action, PFRES_MATCH, rm, am, ruleset, pd, 1, ri->r); @@ -10811,6 +10919,8 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 struct pf_krule *a = NULL, *r = &V_pf_default_rule; struct pf_kstate *s = NULL; struct pf_kruleset *ruleset = NULL; + struct pf_krule_item *ri; + struct pf_krule_slist match_rules; struct pf_pdesc pd; int use_2nd_queue = 0; uint16_t tag; @@ -10847,6 +10957,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 } pf_init_pdesc(&pd, *m0); + SLIST_INIT(&match_rules); if (pd.pf_mtag != NULL && (pd.pf_mtag->flags & PF_MTAG_FLAG_ROUTE_TO)) { pd.pf_mtag->flags &= ~PF_MTAG_FLAG_ROUTE_TO; @@ -10879,7 +10990,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 PF_RULES_RLOCK(); if (pf_setup_pdesc(af, dir, &pd, m0, &action, &reason, - kif, default_actions) == -1) { + kif, default_actions) != PF_PASS) { if (action != PF_PASS) pd.act.log |= PF_LOG_FORCE; goto done; @@ -10943,7 +11054,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 action = PF_DROP; else action = pf_test_rule(&r, &s, &pd, &a, - &ruleset, &reason, inp); + &ruleset, &reason, inp, &match_rules); if (action != PF_PASS) REASON_SET(&reason, PFRES_FRAG); break; @@ -11001,7 +11112,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 break; } else { action = pf_test_rule(&r, &s, &pd, - &a, &ruleset, &reason, inp); + &a, &ruleset, &reason, inp, &match_rules); } } break; @@ -11022,7 +11133,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 a = s->anchor; } else if (s == NULL) { action = pf_test_rule(&r, &s, - &pd, &a, &ruleset, &reason, inp); + &pd, &a, &ruleset, &reason, inp, &match_rules); } break; @@ -11050,7 +11161,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 a = s->anchor; } else if (s == NULL) action = pf_test_rule(&r, &s, &pd, - &a, &ruleset, &reason, inp); + &a, &ruleset, &reason, inp, &match_rules); break; } @@ -11059,8 +11170,11 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0 done: PF_RULES_RUNLOCK(); - if (pd.m == NULL) + /* if packet sits in reassembly queue, return without error */ + if (pd.m == NULL) { + pf_free_match_rules(&match_rules); goto eat_pkt; + } if (s) memcpy(&pd.act, &s->act, sizeof(s->act)); @@ -11157,6 +11271,8 @@ done: (dir == PF_IN) ? PF_DIVERT_MTAG_DIR_IN : PF_DIVERT_MTAG_DIR_OUT; + pf_counters_inc(action, &pd, s, r, a, &match_rules); + if (s) PF_STATE_UNLOCK(s); @@ -11198,7 +11314,6 @@ done: if (pd.act.log) { struct pf_krule *lr; - struct pf_krule_item *ri; if (s != NULL && s->nat_rule != NULL && s->nat_rule->log & PF_LOG_ALL) @@ -11217,7 +11332,7 @@ done: } } - pf_counters_inc(action, &pd, s, r, a); + pf_counters_inc(action, &pd, s, r, a, &match_rules); switch (action) { case PF_SYNPROXY_DROP: diff --git a/sys/netpfil/pf/pf_if.c b/sys/netpfil/pf/pf_if.c index f3be036ef745..6f41d453a7d1 100644 --- a/sys/netpfil/pf/pf_if.c +++ b/sys/netpfil/pf/pf_if.c @@ -702,7 +702,7 @@ pfi_table_update(struct pfr_ktable *kt, struct pfi_kkif *kif, uint8_t net, } if ((e = pfr_set_addrs(&kt->pfrkt_t, V_pfi_buffer, V_pfi_buffer_cnt, &size2, - NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK))) + NULL, NULL, NULL, PFR_FLAG_START | PFR_FLAG_DONE, PFR_TFLAG_ALLMASK))) printf("%s: cannot set %d new addresses into table %s: %d\n", __func__, V_pfi_buffer_cnt, kt->pfrkt_name, e); } diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index d58af6e5ec4d..5ec67021068b 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -259,7 +259,7 @@ static void dehook_pf_eth(void); static void dehook_pf(void); static int shutdown_pf(void); static int pf_load(void); -static void pf_unload(void); +static void pf_unload(void *); static struct cdevsw pf_cdevsw = { .d_ioctl = pfioctl, @@ -4792,6 +4792,17 @@ DIOCCHANGEADDR_error: error = ENODEV; goto fail; } + if (strnlen(io->pfrio_table.pfrt_anchor, MAXPATHLEN) + == MAXPATHLEN) { + error = EINVAL; + goto fail; + } + if (strnlen(io->pfrio_table.pfrt_name, PF_TABLE_NAME_SIZE) + == PF_TABLE_NAME_SIZE) { + error = EINVAL; + goto fail; + } + PF_RULES_WLOCK(); error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); @@ -5131,7 +5142,7 @@ DIOCCHANGEADDR_error: error = pfr_set_addrs(&io->pfrio_table, pfras, io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd, &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags | - PFR_FLAG_USERIOCTL, 0); + PFR_FLAG_START | PFR_FLAG_DONE | PFR_FLAG_USERIOCTL, 0); PF_RULES_WUNLOCK(); if (error == 0 && io->pfrio_flags & PFR_FLAG_FEEDBACK) error = copyout(pfras, io->pfrio_buffer, totlen); @@ -7082,7 +7093,7 @@ pf_unload_vnet(void) } static void -pf_unload(void) +pf_unload(void *dummy __unused) { sx_xlock(&pf_end_lock); diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c index b8b5157c9b15..7aeb8266ca8c 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -73,7 +73,7 @@ VNET_DEFINE_STATIC(int, pf_rdr_srcport_rewrite_tries) = 16; static uint64_t pf_hash(struct pf_addr *, struct pf_addr *, struct pf_poolhashkey *, sa_family_t); -struct pf_krule *pf_match_translation(int, struct pf_test_ctx *); +static struct pf_krule *pf_match_translation(int, struct pf_test_ctx *); static enum pf_test_status pf_step_into_translation_anchor(int, struct pf_test_ctx *, struct pf_krule *); static int pf_get_sport(struct pf_pdesc *, struct pf_krule *, @@ -216,6 +216,7 @@ pf_match_translation_rule(int rs_num, struct pf_test_ctx *ctx, struct pf_krulese */ ctx->arsm = ctx->aruleset; } + break; } else { ctx->a = r; /* remember anchor */ ctx->aruleset = ruleset; /* and its ruleset */ @@ -273,7 +274,7 @@ pf_step_into_translation_anchor(int rs_num, struct pf_test_ctx *ctx, struct pf_k return (rv); } -struct pf_krule * +static struct pf_krule * pf_match_translation(int rs_num, struct pf_test_ctx *ctx) { enum pf_test_status rv; @@ -534,6 +535,63 @@ pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r, return (1); } +static __inline u_short +pf_check_src_node_valid(struct pf_ksrc_node *sn, struct pf_kpool *rpool) +{ + struct pf_addr *raddr, *rmask; + struct pf_addr *caddr; /* cached redirection address */ + struct pf_kpooladdr *pa; + sa_family_t raf; + sa_family_t caf; /* cached redirection AF */ + u_short valid = 0; + + KASSERT(sn != NULL, ("sn is NULL")); + KASSERT(rpool != NULL, ("rpool is NULL")); + + /* check if the cached entry is still valid */ + + if (sn->type == PF_SN_LIMIT) { + /* Always valid as it does not store redirection address */ + return (1); + } + + mtx_lock(&rpool->mtx); + caddr = &(sn->raddr); + caf = sn->raf; + + TAILQ_FOREACH(pa, &rpool->list, entries) { + if (PF_AZERO(caddr, caf)) { + valid = 1; + goto done; + } else if (pa->addr.type == PF_ADDR_DYNIFTL) { + if (pfr_kentry_byaddr(pa->addr.p.dyn->pfid_kt, caddr, caf, 0)) { + valid = 1; + goto done; + } + } else if (pa->addr.type == PF_ADDR_TABLE) { + if (pfr_kentry_byaddr(pa->addr.p.tbl, caddr, caf, 0)) { + valid = 1; + goto done; + } + } else if (pa->addr.type != PF_ADDR_NOROUTE) { + /* PF_ADDR_URPFFAILED, PF_ADDR_RANGE, PF_ADDR_ADDRMASK */ + raddr = &(pa->addr.v.a.addr); + rmask = &(pa->addr.v.a.mask); + raf = pa->af; + if (raf == caf && pf_match_addr(0, raddr, rmask, caddr, caf)) { + valid = 1; + goto done; + } + } + /* else PF_ADDR_NOROUTE */ + } + +done: + mtx_unlock(&rpool->mtx); + + return (valid); +} + u_short pf_map_addr(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr, struct pf_addr *naddr, struct pfi_kkif **nkif, sa_family_t *naf, @@ -873,6 +931,45 @@ pf_map_addr_sn(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr, if (sn != NULL) { PF_SRC_NODE_LOCK_ASSERT(sn); + /* + * Check if source node's redirection address still exists + * in pool from which the SN was created. If not, delete it. + * Similar to pf_kill_srcnodes(). Unlink the source node + * from tree, unlink it from states, then free it. Do not + * overlap source node and state locks to avoid LOR. + */ + if (!pf_check_src_node_valid(sn, rpool)) { + pf_unlink_src_node(sn); + PF_SRC_NODE_UNLOCK(sn); + if (V_pf_status.debug >= PF_DEBUG_NOISY) { + printf("%s: stale src tracking (%d) ", + __func__, sn_type); + pf_print_host(saddr, 0, saf); + printf(" to "); + pf_print_host(&(sn->raddr), 0, sn->raf); + if (nkif) + printf("@%s", sn->rkif->pfik_name); + printf("\n"); + } + + for (int i = 0; i <= V_pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + struct pf_kstate *st; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(st, &ih->states, entry) { + if (st->sns[sn->type] == sn) { + st->sns[sn->type] = NULL; + } + } + PF_HASHROW_UNLOCK(ih); + } + pf_free_src_node(sn); + counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1); + sn = NULL; + goto map_addr; + } + (*naf) = sn->raf; /* If the supplied address is the same as the current one we've @@ -901,9 +998,10 @@ pf_map_addr_sn(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr, goto done; } +map_addr: /* - * Source node has not been found. Find a new address and store it - * in variables given by the caller. + * Source node has not been found or is invalid. Find a new address + * and store it in variables given by the caller. */ if ((reason = pf_map_addr(saf, r, saddr, naddr, nkif, naf, init_addr, rpool)) != 0) { @@ -973,6 +1071,7 @@ pf_get_transaddr(struct pf_test_ctx *ctx, struct pf_krule *r, { struct pf_pdesc *pd = ctx->pd; struct pf_addr *naddr; + int idx; uint16_t *nportp; uint16_t low, high; u_short reason; @@ -987,8 +1086,19 @@ pf_get_transaddr(struct pf_test_ctx *ctx, struct pf_krule *r, return (PFRES_MEMORY); } - naddr = &ctx->nk->addr[1]; - nportp = &ctx->nk->port[1]; + switch (nat_action) { + case PF_NAT: + idx = pd->sidx; + break; + case PF_BINAT: + idx = 1; + break; + case PF_RDR: + idx = pd->didx; + break; + } + naddr = &ctx->nk->addr[idx]; + nportp = &ctx->nk->port[idx]; switch (nat_action) { case PF_NAT: diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index 082b9b565153..993981a9c0de 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -2100,6 +2100,7 @@ struct nl_parsed_table_addrs { size_t addr_count; int nadd; int ndel; + int nchange; }; #define _OUT(_field) offsetof(struct pfr_addr, _field) static const struct nlattr_parser nla_p_pfr_addr[] = { @@ -2209,6 +2210,123 @@ pf_handle_table_del_addrs(struct nlmsghdr *hdr, struct nl_pstate *npt) return (error); } +static int +pf_handle_table_set_addrs(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + struct nl_parsed_table_addrs attrs = { 0 }; + struct nl_writer *nw = npt->nw; + struct genlmsghdr *ghdr_new; + int error; + + error = nl_parse_nlmsg(hdr, &table_addr_parser, npt, &attrs); + if (error != 0) + return (error); + + PF_RULES_WLOCK(); + error = pfr_set_addrs(&attrs.table, &attrs.addrs[0], + attrs.addr_count, NULL, &attrs.nadd, &attrs.ndel, &attrs.nchange, + attrs.flags | PFR_FLAG_USERIOCTL, 0); + PF_RULES_WUNLOCK(); + + if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) + return (ENOMEM); + + ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); + ghdr_new->cmd = PFNL_CMD_TABLE_DEL_ADDR; + ghdr_new->version = 0; + ghdr_new->reserved = 0; + + nlattr_add_u32(nw, PF_TA_NBR_ADDED, attrs.nadd); + nlattr_add_u32(nw, PF_TA_NBR_DELETED, attrs.ndel); + nlattr_add_u32(nw, PF_TA_NBR_CHANGED, attrs.nchange); + + if (!nlmsg_end(nw)) + return (ENOMEM); + + return (error); +} + +static int +nlattr_add_pfr_addr(struct nl_writer *nw, int attr, const struct pfr_addr *a) +{ + int off = nlattr_add_nested(nw, attr); + if (off == 0) + return (false); + + nlattr_add_u32(nw, PFR_A_AF, a->pfra_af); + nlattr_add_u8(nw, PFR_A_NET, a->pfra_net); + nlattr_add_bool(nw, PFR_A_NOT, a->pfra_not); + nlattr_add_in6_addr(nw, PFR_A_ADDR, &a->pfra_u._pfra_ip6addr); + + nlattr_set_len(nw, off); + + return (true); +} + +static int +pf_handle_table_get_addrs(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + struct pfioc_table attrs = { 0 }; + struct pfr_addr *pfras; + struct nl_writer *nw = npt->nw; + struct genlmsghdr *ghdr_new; + int size = 0; + int error; + + PF_RULES_RLOCK_TRACKER; + + error = nl_parse_nlmsg(hdr, &table_addr_parser, npt, &attrs); + if (error != 0) + return (error); + + PF_RULES_RLOCK(); + /* Get required size. */ + error = pfr_get_addrs(&attrs.pfrio_table, NULL, + &size, attrs.pfrio_flags | PFR_FLAG_USERIOCTL); + if (error != 0) { + PF_RULES_RUNLOCK(); + return (error); + } + pfras = mallocarray(size, sizeof(struct pfr_addr), M_PF, + M_NOWAIT | M_ZERO); + if (pfras == NULL) { + PF_RULES_RUNLOCK(); + return (ENOMEM); + } + /* Now get the addresses. */ + error = pfr_get_addrs(&attrs.pfrio_table, pfras, + &size, attrs.pfrio_flags | PFR_FLAG_USERIOCTL); + PF_RULES_RUNLOCK(); + if (error != 0) + goto out; + + for (int i = 0; i < size; i++) { + if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) { + nlmsg_abort(nw); + error = ENOMEM; + goto out; + } + ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); + ghdr_new->cmd = PFNL_CMD_TABLE_GET_ADDR; + ghdr_new->version = 0; + ghdr_new->reserved = 0; + + if (i == 0) + nlattr_add_u32(nw, PF_TA_ADDR_COUNT, size); + + nlattr_add_pfr_addr(nw, PF_TA_ADDR, &pfras[i]); + if (!nlmsg_end(nw)) { + nlmsg_abort(nw); + error = ENOMEM; + goto out; + } + } + +out: + free(pfras, M_PF); + return (error); +} + static const struct nlhdr_parser *all_parsers[] = { &state_parser, &addrule_parser, @@ -2460,6 +2578,20 @@ static const struct genl_cmd pf_cmds[] = { .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, + { + .cmd_num = PFNL_CMD_TABLE_SET_ADDR, + .cmd_name = "TABLE_SET_ADDRS", + .cmd_cb = pf_handle_table_set_addrs, + .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, + .cmd_priv = PRIV_NETINET_PF, + }, + { + .cmd_num = PFNL_CMD_TABLE_GET_ADDR, + .cmd_name = "TABLE_GET_ADDRS", + .cmd_cb = pf_handle_table_get_addrs, + .cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, + .cmd_priv = PRIV_NETINET_PF, + }, }; void diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h index c46c8f2b2592..e1eb3e628df5 100644 --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -69,6 +69,8 @@ enum { PFNL_CMD_CLR_ADDRS = 31, PFNL_CMD_TABLE_ADD_ADDR = 32, PFNL_CMD_TABLE_DEL_ADDR = 33, + PFNL_CMD_TABLE_SET_ADDR = 34, + PFNL_CMD_TABLE_GET_ADDR = 35, __PFNL_CMD_MAX, }; #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1) @@ -483,6 +485,8 @@ enum pf_table_addrs_t { PF_TA_FLAGS = 3, /* u32 */ PF_TA_NBR_ADDED = 4, /* u32 */ PF_TA_NBR_DELETED = 5, /* u32 */ + PF_TA_NBR_CHANGED = 6, /* u32 */ + PF_TA_ADDR_COUNT = 7, /* u32 */ }; #ifdef _KERNEL diff --git a/sys/netpfil/pf/pf_table.c b/sys/netpfil/pf/pf_table.c index 73ec18fa7646..0e2b9fe1cac8 100644 --- a/sys/netpfil/pf/pf_table.c +++ b/sys/netpfil/pf/pf_table.c @@ -399,7 +399,8 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, PF_RULES_WASSERT(); - ACCEPT_FLAGS(flags, PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); + ACCEPT_FLAGS(flags, PFR_FLAG_START | PFR_FLAG_DONE | + PFR_FLAG_DUMMY | PFR_FLAG_FEEDBACK); if (pfr_validate_table(tbl, ignore_pfrt_flags, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); @@ -411,7 +412,8 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, tmpkt = pfr_create_ktable(&V_pfr_nulltable, 0, 0); if (tmpkt == NULL) return (ENOMEM); - pfr_mark_addrs(kt); + if (flags & PFR_FLAG_START) + pfr_mark_addrs(kt); SLIST_INIT(&addq); SLIST_INIT(&delq); SLIST_INIT(&changeq); @@ -444,6 +446,7 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, } p = pfr_create_kentry(&ad, (kt->pfrkt_flags & PFR_TFLAG_COUNTERS) != 0); + p->pfrke_mark = PFR_FB_ADDED; if (p == NULL) senderr(ENOMEM); if (pfr_route_kentry(tmpkt, p)) { @@ -459,7 +462,8 @@ _skip: if (flags & PFR_FLAG_FEEDBACK) bcopy(&ad, addr + i, sizeof(ad)); } - pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); + if (flags & PFR_FLAG_DONE) + pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); if ((flags & PFR_FLAG_FEEDBACK) && *size2) { if (*size2 < size+xdel) { *size2 = size+xdel; @@ -2071,7 +2075,7 @@ pfr_lookup_table(struct pfr_table *tbl) (struct pfr_ktable *)tbl)); } -static struct pfr_kentry * +struct pfr_kentry * pfr_kentry_byaddr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, int exact) { |
