示例#1
0
/*
 * npfctl_save: export the config dictionary as it was submitted,
 * including the current snapshot of the connections.  Additionally,
 * indicate whether the ruleset is currently active.
 */
int
npfctl_save(u_long cmd, void *data)
{
	struct plistref *pref = data;
	prop_array_t rulelist, natlist, tables, rprocs, conlist;
	prop_dictionary_t npf_dict = NULL;
	int error;

	rulelist = prop_array_create();
	natlist = prop_array_create();
	tables = prop_array_create();
	rprocs = prop_array_create();
	conlist = prop_array_create();

	/*
	 * Serialise the connections and NAT policies.
	 */
	npf_config_enter();
	error = npf_conndb_export(conlist);
	if (error) {
		goto out;
	}
	error = npf_ruleset_export(npf_config_ruleset(), rulelist);
	if (error) {
		goto out;
	}
	error = npf_ruleset_export(npf_config_natset(), natlist);
	if (error) {
		goto out;
	}
	error = npf_tableset_export(npf_config_tableset(), tables);
	if (error) {
		goto out;
	}
	error = npf_rprocset_export(npf_config_rprocs(), rprocs);
	if (error) {
		goto out;
	}
	prop_array_t alglist = npf_alg_export();

	npf_dict = prop_dictionary_create();
	prop_dictionary_set_uint32(npf_dict, "version", NPF_VERSION);
	prop_dictionary_set_and_rel(npf_dict, "algs", alglist);
	prop_dictionary_set_and_rel(npf_dict, "rules", rulelist);
	prop_dictionary_set_and_rel(npf_dict, "nat", natlist);
	prop_dictionary_set_and_rel(npf_dict, "tables", tables);
	prop_dictionary_set_and_rel(npf_dict, "rprocs", rprocs);
	prop_dictionary_set_and_rel(npf_dict, "conn-list", conlist);
	prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p());
	error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict);
out:
	npf_config_exit();

	if (!npf_dict) {
		prop_object_release(rulelist);
		prop_object_release(natlist);
		prop_object_release(tables);
		prop_object_release(rprocs);
		prop_object_release(conlist);
	} else {
		prop_object_release(npf_dict);
	}
	return error;
}
示例#2
0
/*
 * npfctl_rule: add or remove dynamic rules in the specified ruleset.
 */
int
npfctl_rule(u_long cmd, void *data)
{
	struct plistref *pref = data;
	prop_dictionary_t npf_rule, retdict = NULL;
	npf_ruleset_t *rlset;
	npf_rule_t *rl = NULL;
	const char *ruleset_name;
	uint32_t rcmd = 0;
	int error;

	error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_rule);
	if (error) {
		return error;
	}
	prop_dictionary_get_uint32(npf_rule, "command", &rcmd);
	if (!prop_dictionary_get_cstring_nocopy(npf_rule,
	    "ruleset-name", &ruleset_name)) {
		error = EINVAL;
		goto out;
	}

	if (rcmd == NPF_CMD_RULE_ADD) {
		retdict = prop_dictionary_create();
		if (npf_mk_singlerule(npf_rule, NULL, &rl, retdict) != 0) {
			error = EINVAL;
			goto out;
		}
	}

	npf_config_enter();
	rlset = npf_config_ruleset();

	switch (rcmd) {
	case NPF_CMD_RULE_ADD: {
		if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) {
			/* Success. */
			uint64_t id = npf_rule_getid(rl);
			prop_dictionary_set_uint64(retdict, "id", id);
			rl = NULL;
		}
		break;
	}
	case NPF_CMD_RULE_REMOVE: {
		uint64_t id;

		if (!prop_dictionary_get_uint64(npf_rule, "id", &id)) {
			error = EINVAL;
			break;
		}
		error = npf_ruleset_remove(rlset, ruleset_name, id);
		break;
	}
	case NPF_CMD_RULE_REMKEY: {
		prop_object_t obj = prop_dictionary_get(npf_rule, "key");
		const void *key = prop_data_data_nocopy(obj);
		size_t len = prop_data_size(obj);

		if (len == 0 || len > NPF_RULE_MAXKEYLEN) {
			error = EINVAL;
			break;
		}
		error = npf_ruleset_remkey(rlset, ruleset_name, key, len);
		break;
	}
	case NPF_CMD_RULE_LIST: {
		retdict = npf_ruleset_list(rlset, ruleset_name);
		if (!retdict) {
			error = ESRCH;
		}
		break;
	}
	case NPF_CMD_RULE_FLUSH: {
		error = npf_ruleset_flush(rlset, ruleset_name);
		break;
	}
	default:
		error = EINVAL;
		break;
	}

	/* Destroy any removed rules. */
	if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) {
		npf_config_sync();
		npf_ruleset_gc(rlset);
	}
	npf_config_exit();

	if (rl) {
		KASSERT(error);
		npf_rule_free(rl);
	}
out:
	if (retdict) {
		prop_object_release(npf_rule);
		prop_dictionary_copyout_ioctl(pref, cmd, retdict);
		prop_object_release(retdict);
	}
	return error;
}
示例#3
0
/*
 * npf_packet_handler: main packet handling routine for layer 3.
 *
 * Note: packet flow and inspection logic is in strict order.
 */
int
npf_packet_handler(void *arg, struct mbuf **mp, ifnet_t *ifp, int di)
{
	nbuf_t nbuf;
	npf_cache_t npc;
	npf_session_t *se;
	npf_rule_t *rl;
	npf_rproc_t *rp;
	int error, retfl;
	int decision;

	/*
	 * Initialise packet information cache.
	 * Note: it is enough to clear the info bits.
	 */
	KASSERT(ifp != NULL);
	nbuf_init(&nbuf, *mp, ifp);
	npc.npc_info = 0;
	decision = NPF_DECISION_BLOCK;
	error = 0;
	retfl = 0;
	rp = NULL;

	/* Cache everything.  Determine whether it is an IP fragment. */
	if (npf_cache_all(&npc, &nbuf) & NPC_IPFRAG) {
		/*
		 * Pass to IPv4 or IPv6 reassembly mechanism.
		 */
		error = npf_reassembly(&npc, &nbuf, mp);
		if (error) {
			se = NULL;
			goto out;
		}
		if (*mp == NULL) {
			/* More fragments should come; return. */
			return 0;
		}
	}

	/* Inspect the list of sessions (if found, acquires a reference). */
	se = npf_session_inspect(&npc, &nbuf, di, &error);

	/* If "passing" session found - skip the ruleset inspection. */
	if (se && npf_session_pass(se, &rp)) {
		npf_stats_inc(NPF_STAT_PASS_SESSION);
		KASSERT(error == 0);
		goto pass;
	}
	if (error) {
		if (error == ENETUNREACH)
			goto block;
		goto out;
	}

	/* Acquire the lock, inspect the ruleset using this packet. */
	int slock = npf_config_read_enter();
	npf_ruleset_t *rlset = npf_config_ruleset();

	rl = npf_ruleset_inspect(&npc, &nbuf, rlset, di, NPF_LAYER_3);
	if (rl == NULL) {
		const bool pass = npf_default_pass();
		npf_config_read_exit(slock);

		if (pass) {
			npf_stats_inc(NPF_STAT_PASS_DEFAULT);
			goto pass;
		}
		npf_stats_inc(NPF_STAT_BLOCK_DEFAULT);
		goto block;
	}

	/*
	 * Get the rule procedure (acquires a reference) for association
	 * with a session (if any) and execution.
	 */
	KASSERT(rp == NULL);
	rp = npf_rule_getrproc(rl);

	/* Conclude with the rule and release the lock. */
	error = npf_rule_conclude(rl, &retfl);
	npf_config_read_exit(slock);

	if (error) {
		npf_stats_inc(NPF_STAT_BLOCK_RULESET);
		goto block;
	}
	npf_stats_inc(NPF_STAT_PASS_RULESET);

	/*
	 * Establish a "pass" session, if required.  Just proceed,
	 * if session creation fails (e.g. due to unsupported protocol).
	 */
	if ((retfl & NPF_RULE_STATEFUL) != 0 && !se) {
		se = npf_session_establish(&npc, &nbuf, di,
		    (retfl & NPF_RULE_MULTIENDS) == 0);
		if (se) {
			/*
			 * Note: the reference on the rule procedure is
			 * transfered to the session.  It will be released
			 * on session destruction.
			 */
			npf_session_setpass(se, rp);
		}
	}
pass:
	decision = NPF_DECISION_PASS;
	KASSERT(error == 0);
	/*
	 * Perform NAT.
	 */
	error = npf_do_nat(&npc, se, &nbuf, di);
block:
	/*
	 * Execute the rule procedure, if any is associated.
	 * It may reverse the decision from pass to block.
	 */
	if (rp && !npf_rproc_run(&npc, &nbuf, rp, &decision)) {
		if (se) {
			npf_session_release(se);
		}
		npf_rproc_release(rp);
		*mp = NULL;
		return 0;
	}
out:
	/*
	 * Release the reference on a session.  Release the reference on a
	 * rule procedure only if there was no association.
	 */
	if (se) {
		npf_session_release(se);
	} else if (rp) {
		npf_rproc_release(rp);
	}

	/* Reset mbuf pointer before returning to the caller. */
	if ((*mp = nbuf_head_mbuf(&nbuf)) == NULL) {
		return error ? error : ENOMEM;
	}

	/* Pass the packet if decided and there is no error. */
	if (decision == NPF_DECISION_PASS && !error) {
		/*
		 * XXX: Disable for now, it will be set accordingly later,
		 * for optimisations (to reduce inspection).
		 */
		(*mp)->m_flags &= ~M_CANFASTFWD;
		return 0;
	}

	/*
	 * Block the packet.  ENETUNREACH is used to indicate blocking.
	 * Depending on the flags and protocol, return TCP reset (RST) or
	 * ICMP destination unreachable.
	 */
	if (retfl && npf_return_block(&npc, &nbuf, retfl)) {
		*mp = NULL;
	}

	if (!error) {
		error = ENETUNREACH;
	}

	if (*mp) {
		m_freem(*mp);
		*mp = NULL;
	}
	return error;
}