diff options
Diffstat (limited to 'sys/netpfil/pf')
| -rw-r--r-- | sys/netpfil/pf/if_pfsync.c | 3 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf.c | 3 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_if.c | 2 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_ioctl.c | 13 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_lb.c | 118 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_nl.c | 44 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_nl.h | 2 | ||||
| -rw-r--r-- | sys/netpfil/pf/pf_table.c | 12 | 
8 files changed, 186 insertions, 11 deletions
| 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 d6fc24a23fe9..a39f5fe58cd6 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -1007,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)  { @@ -5965,6 +5965,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,  		ctx.nat_pool = &(ctx.nr->rdr);  	} +	*ctx.rm = &V_pf_default_rule;  	if (ctx.nr && ctx.nr->natpass) {  		r = ctx.nr;  		ruleset = *ctx.rsm; 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 a4557f139ae5..5ec67021068b 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -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); diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c index fb1b121d0bc0..7aeb8266ca8c 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -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 */ @@ -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..21d4db1b8478 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,42 @@ 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 const struct nlhdr_parser *all_parsers[] = {  	&state_parser,  	&addrule_parser, @@ -2460,6 +2497,13 @@ 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, +	},  };  void diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h index c46c8f2b2592..d1538ab4ff5b 100644 --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -69,6 +69,7 @@ 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_MAX,  };  #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1) @@ -483,6 +484,7 @@ 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 */  };  #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)  { | 
