Example #1
0
static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
				u_int16_t subsys_id)
{
	struct sk_buff *nskb, *oskb = skb;
	struct net *net = sock_net(skb->sk);
	const struct nfnetlink_subsystem *ss;
	const struct nfnl_callback *nc;
	bool success = true, done = false;
	int err;

	if (subsys_id >= NFNL_SUBSYS_COUNT)
		return netlink_ack(skb, nlh, -EINVAL);
replay:
	nskb = netlink_skb_clone(oskb, GFP_KERNEL);
	if (!nskb)
		return netlink_ack(oskb, nlh, -ENOMEM);

	nskb->sk = oskb->sk;
	skb = nskb;

	nfnl_lock(subsys_id);
	ss = rcu_dereference_protected(table[subsys_id].subsys,
				       lockdep_is_held(&table[subsys_id].mutex));
	if (!ss) {
#ifdef CONFIG_MODULES
		nfnl_unlock(subsys_id);
		request_module("nfnetlink-subsys-%d", subsys_id);
		nfnl_lock(subsys_id);
		ss = rcu_dereference_protected(table[subsys_id].subsys,
					       lockdep_is_held(&table[subsys_id].mutex));
		if (!ss)
#endif
		{
			nfnl_unlock(subsys_id);
			netlink_ack(skb, nlh, -EOPNOTSUPP);
			return kfree_skb(nskb);
		}
	}

	if (!ss->commit || !ss->abort) {
		nfnl_unlock(subsys_id);
		netlink_ack(skb, nlh, -EOPNOTSUPP);
		return kfree_skb(skb);
	}

	while (skb->len >= nlmsg_total_size(0)) {
		int msglen, type;

		nlh = nlmsg_hdr(skb);
		err = 0;

		if (nlh->nlmsg_len < NLMSG_HDRLEN) {
			err = -EINVAL;
			goto ack;
		}

		/* Only requests are handled by the kernel */
		if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
			err = -EINVAL;
			goto ack;
		}

		type = nlh->nlmsg_type;
		if (type == NFNL_MSG_BATCH_BEGIN) {
			/* Malformed: Batch begin twice */
			success = false;
			goto done;
		} else if (type == NFNL_MSG_BATCH_END) {
			done = true;
			goto done;
		} else if (type < NLMSG_MIN_TYPE) {
			err = -EINVAL;
			goto ack;
		}

		/* We only accept a batch with messages for the same
		 * subsystem.
		 */
		if (NFNL_SUBSYS_ID(type) != subsys_id) {
			err = -EINVAL;
			goto ack;
		}

		nc = nfnetlink_find_client(type, ss);
		if (!nc) {
			err = -EINVAL;
			goto ack;
		}

		{
			int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
			u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
			struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
			struct nlattr *attr = (void *)nlh + min_len;
			int attrlen = nlh->nlmsg_len - min_len;

			err = nla_parse(cda, ss->cb[cb_id].attr_count,
					attr, attrlen, ss->cb[cb_id].policy);
			if (err < 0)
				goto ack;

			if (nc->call_batch) {
				err = nc->call_batch(net->nfnl, skb, nlh,
						     (const struct nlattr **)cda);
			}

			/* The lock was released to autoload some module, we
			 * have to abort and start from scratch using the
			 * original skb.
			 */
			if (err == -EAGAIN) {
				ss->abort(skb);
				nfnl_unlock(subsys_id);
				kfree_skb(nskb);
				goto replay;
			}
		}
ack:
		if (nlh->nlmsg_flags & NLM_F_ACK || err) {
			/* We don't stop processing the batch on errors, thus,
			 * userspace gets all the errors that the batch
			 * triggers.
			 */
			netlink_ack(skb, nlh, err);
			if (err)
				success = false;
		}

		msglen = NLMSG_ALIGN(nlh->nlmsg_len);
		if (msglen > skb->len)
			msglen = skb->len;
		skb_pull(skb, msglen);
	}
done:
	if (success && done)
		ss->commit(skb);
	else
		ss->abort(skb);

	nfnl_unlock(subsys_id);
	kfree_skb(nskb);
}
Example #2
0
/* Process one complete nfnetlink message. */
static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
	struct net *net = sock_net(skb->sk);
	const struct nfnl_callback *nc;
	const struct nfnetlink_subsystem *ss;
	int type, err;

	/* All the messages must at least contain nfgenmsg */
	if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
		return 0;

	type = nlh->nlmsg_type;
replay:
	rcu_read_lock();
	ss = nfnetlink_get_subsys(type);
	if (!ss) {
#ifdef CONFIG_MODULES
		rcu_read_unlock();
		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
		rcu_read_lock();
		ss = nfnetlink_get_subsys(type);
		if (!ss)
#endif
		{
			rcu_read_unlock();
			return -EINVAL;
		}
	}

	nc = nfnetlink_find_client(type, ss);
	if (!nc) {
		rcu_read_unlock();
		return -EINVAL;
	}

	{
		int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
		u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
		struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
		struct nlattr *attr = (void *)nlh + min_len;
		int attrlen = nlh->nlmsg_len - min_len;
		__u8 subsys_id = NFNL_SUBSYS_ID(type);

		err = nla_parse(cda, ss->cb[cb_id].attr_count,
				attr, attrlen, ss->cb[cb_id].policy);
		if (err < 0) {
			rcu_read_unlock();
			return err;
		}

		if (nc->call_rcu) {
			err = nc->call_rcu(net->nfnl, skb, nlh,
					   (const struct nlattr **)cda);
			rcu_read_unlock();
		} else {
			rcu_read_unlock();
			nfnl_lock(subsys_id);
			if (rcu_dereference_protected(table[subsys_id].subsys,
				lockdep_is_held(&table[subsys_id].mutex)) != ss ||
			    nfnetlink_find_client(type, ss) != nc)
				err = -EAGAIN;
			else if (nc->call)
				err = nc->call(net->nfnl, skb, nlh,
						   (const struct nlattr **)cda);
			else
				err = -EINVAL;
			nfnl_unlock(subsys_id);
		}
		if (err == -EAGAIN)
			goto replay;
		return err;
	}
}
Example #3
0
/* Process one complete nfnetlink message. */
static int nfnetlink_rcv_msg(struct sk_buff *skb,
				    struct nlmsghdr *nlh, int *errp)
{
	struct nfnl_callback *nc;
	struct nfnetlink_subsystem *ss;
	int type, err = 0;

	DEBUGP("entered; subsys=%u, msgtype=%u\n",
		 NFNL_SUBSYS_ID(nlh->nlmsg_type),
		 NFNL_MSG_TYPE(nlh->nlmsg_type));

	if (security_netlink_recv(skb, CAP_NET_ADMIN)) {
		DEBUGP("missing CAP_NET_ADMIN\n");
		*errp = -EPERM;
		return -1;
	}

	/* Only requests are handled by kernel now. */
	if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
		DEBUGP("received non-request message\n");
		return 0;
	}

	/* All the messages must at least contain nfgenmsg */
	if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) {
		DEBUGP("received message was too short\n");
		return 0;
	}

	type = nlh->nlmsg_type;
	ss = nfnetlink_get_subsys(type);
	if (!ss) {
#ifdef CONFIG_KMOD
		/* don't call nfnl_shunlock, since it would reenter
		 * with further packet processing */
		up(&nfnl_sem);
		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
		nfnl_shlock();
		ss = nfnetlink_get_subsys(type);
		if (!ss)
#endif
			goto err_inval;
	}

	nc = nfnetlink_find_client(type, ss);
	if (!nc) {
		DEBUGP("unable to find client for type %d\n", type);
		goto err_inval;
	}

	{
		u_int16_t attr_count = 
			ss->cb[NFNL_MSG_TYPE(nlh->nlmsg_type)].attr_count;
		struct nfattr *cda[attr_count];

		memset(cda, 0, sizeof(struct nfattr *) * attr_count);
		
		err = nfnetlink_check_attributes(ss, nlh, cda);
		if (err < 0)
			goto err_inval;

		DEBUGP("calling handler\n");
		err = nc->call(nfnl, skb, nlh, cda, errp);
		*errp = err;
		return err;
	}

err_inval:
	DEBUGP("returning -EINVAL\n");
	*errp = -EINVAL;
	return -1;
}