FreeBSD/sys/netpfil/ipfw/ip_fw_sockopt.c
/*
 * Adds one or more rules to ipfw @chain.
 * Data layout (version 0)(current):
 * Request:
 * [
 *   ip_fw3_opheader
 *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
 *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3)
 * ]
 * Reply:
 * [
 *   ip_fw3_opheader
 *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
 *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ]
 * ]
 *
 * Rules in reply are modified to store their actual ruleset number.
 *
 * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
 * according to their idx field and there has to be no duplicates.
 * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
 * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
 *
 * Returns 0 on success.
 */
Adds one or more rules to ipfw @chain.

Rules in reply are modified to store their actual ruleset number.

(*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
according to their idx field and there has to be no duplicates.
(*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
(*3) Each ip_fw structure needs to be aligned to u64 boundary.

Returns 0 on success.
Adds one or more rules to ipfw @chain . Rules in reply are modified to store their actual ruleset number . ( * 1 ) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending according to their idx field and there has to be no duplicates . ( * 2 ) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending . ( * 3 ) Each ip_fw structure needs to be aligned to u64 boundary . Returns 0 on success .
static int
add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
    struct sockopt_data *sd)
{
	ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
	ipfw_obj_ntlv *ntlv;
	int clen, error, idx;
	uint32_t count, read;
	struct ip_fw_rule *r;
	struct rule_check_info rci, *ci, *cbuf;
	int i, rsize;
	op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
	ctlv = (ipfw_obj_ctlv *)(op3 + 1);
	read = sizeof(ip_fw3_opheader);
	rtlv = NULL;
	tstate = NULL;
	cbuf = NULL;
	memset(&rci, 0, sizeof(struct rule_check_info));
	if (read + sizeof(*ctlv) > sd->valsize)
		return (EINVAL);
	if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
		clen = ctlv->head.length;
		if (clen > sd->valsize || clen < sizeof(*ctlv))
			return (EINVAL);
		if ((clen % sizeof(uint64_t)) != 0)
			return (EINVAL);
		count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
		if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
			return (EINVAL);
		idx = -1;
		ntlv = (ipfw_obj_ntlv *)(ctlv + 1);
		while (count > 0) {
			if (ntlv->head.length != sizeof(ipfw_obj_ntlv))
				return (EINVAL);
			error = ipfw_check_object_name_generic(ntlv->name);
			if (error != 0)
				return (error);
			if (ntlv->idx <= idx)
				return (EINVAL);
			idx = ntlv->idx;
			count--;
			ntlv++;
		}
		tstate = ctlv;
		read += ctlv->head.length;
		ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
	}
	if (read + sizeof(*ctlv) > sd->valsize)
		return (EINVAL);
	if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
		clen = ctlv->head.length;
		if (clen + read > sd->valsize || clen < sizeof(*ctlv))
			return (EINVAL);
		if ((clen % sizeof(uint64_t)) != 0)
			return (EINVAL);
		if (ctlv->count != 1)
			return (ENOTSUP);
		clen -= sizeof(*ctlv);
		if (ctlv->count > clen / sizeof(struct ip_fw_rule))
			return (EINVAL);
		if (ctlv->count == 1) {
			memset(&rci, 0, sizeof(struct rule_check_info));
			cbuf = &rci;
		} else
			cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP,
			    M_WAITOK | M_ZERO);
		ci = cbuf;
		idx = 0;
		r = (struct ip_fw_rule *)(ctlv + 1);
		count = 0;
		error = 0;
		while (clen > 0) {
			rsize = roundup2(RULESIZE(r), sizeof(uint64_t));
			if (rsize > clen || ctlv->count <= count) {
				error = EINVAL;
				break;
			}
			ci->ctlv = tstate;
			error = check_ipfw_rule1(r, rsize, ci);
			if (error != 0)
				break;
			if (r->rulenum != 0 && r->rulenum < idx) {
				printf("rulenum %d idx %d\n", r->rulenum, idx);
				error = EINVAL;
				break;
			}
			idx = r->rulenum;
			ci->urule = (caddr_t)r;
			rsize = roundup2(rsize, sizeof(uint64_t));
			clen -= rsize;
			r = (struct ip_fw_rule *)((caddr_t)r + rsize);
			count++;
			ci++;
		}
		if (ctlv->count != count || error != 0) {
			if (cbuf != &rci)
				free(cbuf, M_TEMP);
			return (EINVAL);
		}
		rtlv = ctlv;
		read += ctlv->head.length;
		ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
	}
	if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) {
		if (cbuf != NULL && cbuf != &rci)
			free(cbuf, M_TEMP);
		return (EINVAL);
	}
	for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) {
		clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule);
		ci->krule = ipfw_alloc_rule(chain, clen);
		import_rule1(ci);
	}
	if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) {
		for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++)
			free_rule(ci->krule);
	}
	if (cbuf != NULL && cbuf != &rci)
		free(cbuf, M_TEMP);
	return (error);
}
static int add_rules ( struct ip_fw_chain * chain , ip_fw3_opheader * op3 , struct sockopt_data * sd ) { ipfw_obj_ctlv * ctlv , * rtlv , * tstate ; ipfw_obj_ntlv * ntlv ; int clen , error , idx ; uint32_t count , read ; struct ip_fw_rule * r ; struct rule_check_info rci , * ci , * cbuf ; int i , rsize ; op3 = ( ip_fw3_opheader * ) ipfw_get_sopt_space ( sd , sd -> valsize ) ; ctlv = ( ipfw_obj_ctlv * ) ( op3 + 1 ) ; read = sizeof ( ip_fw3_opheader ) ; rtlv = NULL ; tstate = NULL ; cbuf = NULL ; memset ( & rci , 0 , sizeof ( struct rule_check_info ) ) ; if ( read + sizeof ( * ctlv ) > sd -> valsize ) return ( EINVAL ) ; if ( ctlv -> head . type == IPFW_TLV_TBLNAME_LIST ) { clen = ctlv -> head . length ; if ( clen > sd -> valsize || clen < sizeof ( * ctlv ) ) return ( EINVAL ) ; if ( ( clen % sizeof ( uint64_t ) ) != 0 ) return ( EINVAL ) ; count = ( ctlv -> head . length - sizeof ( * ctlv ) ) / sizeof ( * ntlv ) ; if ( ctlv -> count != count || ctlv -> objsize != sizeof ( * ntlv ) ) return ( EINVAL ) ; idx = -1 ; ntlv = ( ipfw_obj_ntlv * ) ( ctlv + 1 ) ; while ( count > 0 ) { if ( ntlv -> head . length != sizeof ( ipfw_obj_ntlv ) ) return ( EINVAL ) ; error = ipfw_check_object_name_generic ( ntlv -> name ) ; if ( error != 0 ) return ( error ) ; if ( ntlv -> idx <= idx ) return ( EINVAL ) ; idx = ntlv -> idx ; count -- ; ntlv ++ ; } tstate = ctlv ; read += ctlv -> head . length ; ctlv = ( ipfw_obj_ctlv * ) ( ( caddr_t ) ctlv + ctlv -> head . length ) ; } if ( read + sizeof ( * ctlv ) > sd -> valsize ) return ( EINVAL ) ; if ( ctlv -> head . type == IPFW_TLV_RULE_LIST ) { clen = ctlv -> head . length ; if ( clen + read > sd -> valsize || clen < sizeof ( * ctlv ) ) return ( EINVAL ) ; if ( ( clen % sizeof ( uint64_t ) ) != 0 ) return ( EINVAL ) ; if ( ctlv -> count != 1 ) return ( ENOTSUP ) ; clen -= sizeof ( * ctlv ) ; if ( ctlv -> count > clen / sizeof ( struct ip_fw_rule ) ) return ( EINVAL ) ; if ( ctlv -> count == 1 ) { memset ( & rci , 0 , sizeof ( struct rule_check_info ) ) ; cbuf = & rci ; } else cbuf = malloc ( ctlv -> count * sizeof ( * ci ) , M_TEMP , M_WAITOK | M_ZERO ) ; ci = cbuf ; idx = 0 ; r = ( struct ip_fw_rule * ) ( ctlv + 1 ) ; count = 0 ; error = 0 ; while ( clen > 0 ) { rsize = roundup2 ( RULESIZE ( r ) , sizeof ( uint64_t ) ) ; if ( rsize > clen || ctlv -> count <= count ) { error = EINVAL ; break ; } ci -> ctlv = tstate ; error = check_ipfw_rule1 ( r , rsize , ci ) ; if ( error != 0 ) break ; if ( r -> rulenum != 0 && r -> rulenum < idx ) { printf ( " \n " , r -> rulenum , idx ) ; error = EINVAL ; break ; } idx = r -> rulenum ; ci -> urule = ( caddr_t ) r ; rsize = roundup2 ( rsize , sizeof ( uint64_t ) ) ; clen -= rsize ; r = ( struct ip_fw_rule * ) ( ( caddr_t ) r + rsize ) ; count ++ ; ci ++ ; } if ( ctlv -> count != count || error != 0 ) { if ( cbuf != & rci ) free ( cbuf , M_TEMP ) ; return ( EINVAL ) ; } rtlv = ctlv ; read += ctlv -> head . length ; ctlv = ( ipfw_obj_ctlv * ) ( ( caddr_t ) ctlv + ctlv -> head . length ) ; } if ( read != sd -> valsize || rtlv == NULL || rtlv -> count == 0 ) { if ( cbuf != NULL && cbuf != & rci ) free ( cbuf , M_TEMP ) ; return ( EINVAL ) ; } for ( i = 0 , ci = cbuf ; i < rtlv -> count ; i ++ , ci ++ ) { clen = RULEKSIZE1 ( ( struct ip_fw_rule * ) ci -> urule ) ; ci -> krule = ipfw_alloc_rule ( chain , clen ) ; import_rule1 ( ci ) ; } if ( ( error = commit_rules ( chain , cbuf , rtlv -> count ) ) != 0 ) { for ( i = 0 , ci = cbuf ; i < rtlv -> count ; i ++ , ci ++ ) free_rule ( ci -> krule ) ; } if ( cbuf != NULL && cbuf != & rci ) free ( cbuf , M_TEMP ) ; return ( error ) ; }
Adds one or more rules to ipfw @chain.
Adds one or more rules to ipfw @chain .
/* Check size and alignment */ /*
		 * Some table names or other named objects.
		 * Check for validness.
		 */ /*
		 * Check each TLV.
		 * Ensure TLVs are sorted ascending and
		 * there are no duplicates.
		 */ /*
		 * TODO: Permit adding multiple rules at once
		 */ /* Allocate state for each rule or use stack */ /*
		 * Check each rule for validness.
		 * Ensure numbered rules are sorted ascending
		 * and properly aligned
		 */ /* Check sorting */ /*
	 * Passed rules seems to be valid.
	 * Allocate storage and try to add them to chain.
	 */ /* Free allocate krules */