Example #1
0
static int __nfnl_handle_msg(struct nfnl_handle *h, struct nlmsghdr *nlh,
			     int len)
{
	struct nfnl_subsys_handle *ssh;
	uint8_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);
	uint8_t subsys_id = NFNL_SUBSYS_ID(nlh->nlmsg_type);
	int err = 0;

	if (subsys_id > NFNL_MAX_SUBSYS)
		return -1;

	ssh = &h->subsys[subsys_id];

	if (nlh->nlmsg_len < NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct nfgenmsg))))
		return -1;

	if (type >= ssh->cb_count)
		return -1;

	if (ssh->cb[type].attr_count) {
		struct nfattr *nfa[ssh->cb[type].attr_count];

		err = nfnl_check_attributes(h, nlh, nfa);
		if (err < 0)
			return err;
		if (ssh->cb[type].call)
			return ssh->cb[type].call(nlh, nfa, ssh->cb[type].data);
	}
	return 0;
}
Example #2
0
static int events_cb(const struct nlmsghdr *nlh, void *data)
{
        int event = NFNL_MSG_TYPE(nlh->nlmsg_type);
        struct callback_data *cb = data;
        int err = MNL_CB_OK;

        if (!cb || cb->type == CALLBACK_RETURN_NONE)
                return err;

	DBG("handle event %s", event_to_str(event));

        switch(event) {
	case NFT_MSG_NEWCHAIN:
		err = chain_cb(nlh, event, cb);
		break;

        case NFT_MSG_NEWRULE:
		err = rule_cb(nlh, event, cb);
		break;
        default:
		DBG("unhandled event type %s", event_to_str(event));
                break;
        }

        return err;
}
Example #3
0
static int events_cb(const struct nlmsghdr *nlh, void *data)
{
	int ret = MNL_CB_OK;
	int event = NFNL_MSG_TYPE(nlh->nlmsg_type);
	int type = *((int *)data);

	switch(event) {
	case NFT_MSG_NEWTABLE:
	case NFT_MSG_DELTABLE:
		ret = table_cb(nlh, event, type);
		break;
	case NFT_MSG_NEWCHAIN:
	case NFT_MSG_DELCHAIN:
		ret = chain_cb(nlh, event, type);
		break;
	case NFT_MSG_NEWRULE:
	case NFT_MSG_DELRULE:
		ret = rule_cb(nlh, event, type);
		break;
	case NFT_MSG_NEWSET:
	case NFT_MSG_DELSET:
		ret = set_cb(nlh, event, type);
		break;
	case NFT_MSG_NEWSETELEM:
	case NFT_MSG_DELSETELEM:
		ret = setelem_cb(nlh, event, type);
		break;
	case NFT_MSG_NEWGEN:
		ret = gen_cb(nlh, event, type);
		break;
	}

	return ret;
}
Example #4
0
static int nfct_parse(ginkgo_msg *msg)
{
    struct nlmsghdr *nlh = NL_HEADER(msg);
    nfct_msg *ctmsg = NFCT_MSG(msg);
    __u16 nlm_type = nlh->nlmsg_type;
    __u16 type = NFNL_MSG_TYPE(nlh->nlmsg_type);
    __u16 subsys = NFNL_SUBSYS_ID(nlh->nlmsg_type);

    if(nlm_type <= NLMSG_MIN_TYPE)
        return -1;

    ctmsg->subsys = subsys;
    ctmsg->type = type;
    ctmsg->entry = NULL;
    if(ctmsg->subsys != NFNL_SUBSYS_CTNETLINK)  {
        PR_INFO("not supported subsys message type parsing:%d %d", subsys, type);
        return 0;
    }
    switch(type)  {
    case IPCTNL_MSG_CT_NEW:
    case IPCTNL_MSG_CT_DELETE:
        return nfct_parse_ct(ctmsg, nlh);
    default:
        PR_INFO("unpexpected ct msg type:%d", type);
        break;
    }
    return 0;
}
Example #5
0
/* Process one complete nfnetlink message. */
static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
	const struct nfnl_callback *nc;
	const struct nfnetlink_subsystem *ss;
	int type, err;

#if 0
	if (security_netlink_recv(skb, CAP_NET_ADMIN))
		return -EPERM;
#endif	

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

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

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

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

		if (likely(nlh->nlmsg_len >= min_len)) {
			struct nlattr *attr = (void *)nlh + NLMSG_ALIGN(min_len);
			int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);

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

		err = nc->call(nfnl, skb, nlh, (struct nfattr **)cda);
		if (err == -EAGAIN)
			goto replay;
		return err;
	}
}
Example #6
0
static inline const struct nfnl_callback *
nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss)
{
	u_int8_t cb_id = NFNL_MSG_TYPE(type);

	if (cb_id >= ss->cb_count)
		return NULL;

	return &ss->cb[cb_id];
}
Example #7
0
int nfnl_check_attributes(const struct nfnl_handle *h,
			 const struct nlmsghdr *nlh,
			 struct nfattr *nfa[])
{
	assert(h);
	assert(nlh);
	assert(nfa);

	int min_len;
	uint8_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);
	uint8_t subsys_id = NFNL_SUBSYS_ID(nlh->nlmsg_type);
	const struct nfnl_subsys_handle *ssh;
	struct nfnl_callback *cb;

	if (subsys_id > NFNL_MAX_SUBSYS)
		return -EINVAL;

	ssh = &h->subsys[subsys_id];
 	cb = &ssh->cb[type];

#if 1
	/* checks need to be enabled as soon as this is called from
	 * somebody else than __nfnl_handle_msg */
	if (type >= ssh->cb_count)
		return -EINVAL;

	min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
	if (nlh->nlmsg_len < min_len)
		return -EINVAL;
#endif
	memset(nfa, 0, sizeof(struct nfattr *) * cb->attr_count);

	if (nlh->nlmsg_len > min_len) {
		struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);

		while (NFA_OK(attr, attrlen)) {
			unsigned int flavor = NFA_TYPE(attr);
			if (flavor) {
				if (flavor > cb->attr_count) {
					/* we have received an attribute from
					 * the kernel which we don't understand
					 * yet. We have to silently ignore this
					 * for the sake of future compatibility */
					attr = NFA_NEXT(attr, attrlen);
					continue;
				}
				nfa[flavor - 1] = attr;
			}
			attr = NFA_NEXT(attr, attrlen);
		}
	}

	return 0;
}
Example #8
0
static inline struct nfnl_callback *
nfnetlink_find_client(u_int16_t type, struct nfnetlink_subsystem *ss)
{
	u_int8_t cb_id = NFNL_MSG_TYPE(type);
	
	if (cb_id >= ss->cb_count) {
		DEBUGP("msgtype %u >= %u, returning\n", type, ss->cb_count);
		return NULL;
	}

	return &ss->cb[cb_id];
}
Example #9
0
/* Process one complete nfnetlink message. */
static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
	struct nfnl_callback *nc;
	struct nfnetlink_subsystem *ss;
	int type, err;

	if (security_netlink_recv(skb, CAP_NET_ADMIN))
		return -EPERM;

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

	type = nlh->nlmsg_type;
	ss = nfnetlink_get_subsys(type);
	if (!ss) {
#ifdef CONFIG_KMOD
		/* don't call nfnl_unlock, since it would reenter
		 * with further packet processing */
		__nfnl_unlock();
		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
		nfnl_lock();
		ss = nfnetlink_get_subsys(type);
		if (!ss)
#endif
			return -EINVAL;
	}

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

	{
		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)
			return err;
		return nc->call(nfnl, skb, nlh, cda);
	}
}
Example #10
0
/**
 * nfnetlink_check_attributes - check and parse nfnetlink attributes
 *
 * subsys: nfnl subsystem for which this message is to be parsed
 * nlmsghdr: netlink message to be checked/parsed
 * cda: array of pointers, needs to be at least subsys->attr_count big
 *
 */
static int
nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys,
			   struct nlmsghdr *nlh, struct nfattr *cda[])
{
	int min_len;
	u_int16_t attr_count;
	u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);

	if (unlikely(cb_id >= subsys->cb_count)) {
		DEBUGP("msgtype %u >= %u, returning\n",
			cb_id, subsys->cb_count);
		return -EINVAL;
	}

	min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
	if (unlikely(nlh->nlmsg_len < min_len))
		return -EINVAL;

	attr_count = subsys->cb[cb_id].attr_count;
	memset(cda, 0, sizeof(struct nfattr *) * attr_count);

	/* check attribute lengths. */
	if (likely(nlh->nlmsg_len > min_len)) {
		struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);

		while (NFA_OK(attr, attrlen)) {
			unsigned flavor = NFA_TYPE(attr);
			if (flavor) {
				if (flavor > attr_count)
					return -EINVAL;
				cda[flavor - 1] = attr;
			}
			attr = NFA_NEXT(attr, attrlen);
		}
	}

	/* implicit: if nlmsg_len == min_len, we return 0, and an empty
	 * (zeroed) cda[] array. The message is valid, but empty. */

        return 0;
}
Example #11
0
/**
 * nfnetlink_check_attributes - check and parse nfnetlink attributes
 *
 * subsys: nfnl subsystem for which this message is to be parsed
 * nlmsghdr: netlink message to be checked/parsed
 * cda: array of pointers, needs to be at least subsys->attr_count big
 *
 */
static int
nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys,
			   struct nlmsghdr *nlh, struct nfattr *cda[])
{
	int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
	u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
	u_int16_t attr_count = subsys->cb[cb_id].attr_count;

	/* check attribute lengths. */
	if (likely(nlh->nlmsg_len > min_len)) {
		struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
		int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
		nfattr_parse(cda, attr_count, attr, attrlen);
	}

	/* implicit: if nlmsg_len == min_len, we return 0, and an empty
	 * (zeroed) cda[] array. The message is valid, but empty. */

	return 0;
}
Example #12
0
static int nfct_response(ginkgo_msg *msg, ginkgo_msg *rsp)
{
    int val = nl_response(msg, rsp);

    /**
     * IPCTNL_MSG_CT_GET non-dump type request return netlink messages
     * with NLM_F_MULTI set, but will not have subsequent NLMSG_DONE
     * message.
     */
    if(val == GINKGO_RESP_CONT)  {
        struct nlmsghdr *nlh = NL_HEADER(msg);
        __u16 type = NFNL_MSG_TYPE(nlh->nlmsg_type);
        __u16 subsys = NFNL_SUBSYS_ID(nlh->nlmsg_type);

        if(subsys == NFNL_SUBSYS_CTNETLINK
           && type == IPCTNL_MSG_CT_GET
           && (! (nlh->nlmsg_flags & NLM_F_DUMP)))
            return GINKGO_RESP_DONE;
    }
    return val;
}
Example #13
0
static int
nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb,
		const struct nlmsghdr *nlh, const struct nlattr * const tb[])
{
	int ret = 0, target;
	struct nfgenmsg *nfmsg;
	const char *fmt;
	const char *name;
	u32 rev;
	struct sk_buff *skb2;

	if (tb[NFTA_COMPAT_NAME] == NULL ||
	    tb[NFTA_COMPAT_REV] == NULL ||
	    tb[NFTA_COMPAT_TYPE] == NULL)
		return -EINVAL;

	name = nla_data(tb[NFTA_COMPAT_NAME]);
	rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV]));
	target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE]));

	nfmsg = nlmsg_data(nlh);

	switch(nfmsg->nfgen_family) {
	case AF_INET:
		fmt = "ipt_%s";
		break;
	case AF_INET6:
		fmt = "ip6t_%s";
		break;
	case NFPROTO_BRIDGE:
		fmt = "ebt_%s";
		break;
	default:
		pr_err("nft_compat: unsupported protocol %d\n",
			nfmsg->nfgen_family);
		return -EINVAL;
	}

	try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name,
						 rev, target, &ret),
						 fmt, name);

	if (ret < 0)
		return ret;

	skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (skb2 == NULL)
		return -ENOMEM;

	/* include the best revision for this extension in the message */
	if (nfnl_compat_fill_info(skb2, NETLINK_CB(skb).portid,
				  nlh->nlmsg_seq,
				  NFNL_MSG_TYPE(nlh->nlmsg_type),
				  NFNL_MSG_COMPAT_GET,
				  nfmsg->nfgen_family,
				  name, ret, target) <= 0) {
		kfree_skb(skb2);
		return -ENOSPC;
	}

	ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid,
				MSG_DONTWAIT);
	if (ret > 0)
		ret = 0;

	return ret == -EAGAIN ? -ENOBUFS : ret;
}
Example #14
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 #15
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;
}
Example #16
0
File: nfnl.c Project: Domikk/libnl
/**
 * Get netfilter message type from message
 * @arg nlh	netlink messsage header
 */
uint8_t nfnlmsg_subtype(struct nlmsghdr *nlh)
{
	return NFNL_MSG_TYPE(nlh->nlmsg_type);
}
Example #17
0
static int
netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
{
	struct pcap_netfilter *handlep = handle->priv;
	const unsigned char *buf;
	int count = 0;
	int len;

	/* ignore interrupt system call error */
	do {
		len = recv(handle->fd, handle->buffer, handle->bufsize, 0);
		if (handle->break_loop) {
			handle->break_loop = 0;
			return -2;
		}
	} while ((len == -1) && (errno == EINTR));

	if (len < 0) {
		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't receive packet %d:%s", errno, pcap_strerror(errno));
		return -1;
	}

	buf = handle->buffer;
	while (len >= NLMSG_SPACE(0)) {
		const struct nlmsghdr *nlh = (const struct nlmsghdr *) buf;
		u_int32_t msg_len;
		nftype_t type = OTHER;

		if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len) {
			snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len);
			return -1;
		}

		if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG && 
			NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET) 
				type = NFLOG;

		if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE && 
			NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET)
				type = NFQUEUE;

		if (type != OTHER) {
			const unsigned char *payload = NULL;
			struct pcap_pkthdr pkth;

			const struct nfgenmsg *nfg;
			int id = 0;

			if (handle->linktype != DLT_NFLOG) {
				const struct nfattr *payload_attr = NULL;

				if (nlh->nlmsg_len < HDR_LENGTH) {
					snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len);
					return -1;
				}

				nfg = NLMSG_DATA(nlh);
				if (nlh->nlmsg_len > HDR_LENGTH) {
					struct nfattr *attr = NFM_NFA(nfg);
					int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH);

					while (NFA_OK(attr, attr_len)) {
						if (type == NFQUEUE) {
							switch (NFA_TYPE(attr)) {
								case NFQA_PACKET_HDR:
									{
										const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr);

										id = ntohl(pkt_hdr->packet_id);
										break;
									}
								case NFQA_PAYLOAD:
									payload_attr = attr;
									break;
							}

						} else if (type == NFLOG) {
							switch (NFA_TYPE(attr)) {
								case NFULA_PAYLOAD:
									payload_attr = attr;
									break;
							}
						}
						attr = NFA_NEXT(attr, attr_len);
					}
				}

				if (payload_attr) {
					payload = NFA_DATA(payload_attr);
					pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr);
				}

			} else {
				payload = NLMSG_DATA(nlh);
				pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr));
			}

			if (payload) {
				/* pkth.caplen = min (payload_len, handle->snapshot); */

				gettimeofday(&pkth.ts, NULL);
				if (handle->fcode.bf_insns == NULL ||
						bpf_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) 
				{
					handlep->packets_read++;
					callback(user, &pkth, payload);
					count++;
				}
			}

			if (type == NFQUEUE) {
				/* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */
				nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT);
			}
		}

		msg_len = NLMSG_ALIGN(nlh->nlmsg_len);
		if (msg_len > len)
			msg_len = len;

		len -= msg_len;
		buf += msg_len;
	}
	return count;
}
static int
netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
{
	struct pcap_netfilter *handlep = handle->priv;
	register u_char *bp, *ep;
	int count = 0;
	ssize_t len;

	/*
	 * Has "pcap_breakloop()" been called?
	 */
	if (handle->break_loop) {
		/*
		 * Yes - clear the flag that indicates that it
		 * has, and return PCAP_ERROR_BREAK to indicate
		 * that we were told to break out of the loop.
		 */
		handle->break_loop = 0;
		return PCAP_ERROR_BREAK;
	}
	len = handle->cc;
	if (len == 0) {
		/*
		 * The buffer is empty; refill it.
		 *
		 * We ignore EINTR, as that might just be due to a signal
		 * being delivered - if the signal should interrupt the
		 * loop, the signal handler should call pcap_breakloop()
		 * to set handle->break_loop (we ignore it on other
		 * platforms as well).
		 */
		do {
			len = recv(handle->fd, handle->buffer, handle->bufsize, 0);
			if (handle->break_loop) {
				handle->break_loop = 0;
				return PCAP_ERROR_BREAK;
			}
			if (errno == ENOBUFS)
				handlep->packets_nobufs++;
		} while ((len == -1) && (errno == EINTR || errno == ENOBUFS));

		if (len < 0) {
			pcap_fmt_errmsg_for_errno(handle->errbuf,
			    PCAP_ERRBUF_SIZE, errno, "Can't receive packet");
			return PCAP_ERROR;
		}

		bp = (unsigned char *)handle->buffer;
	} else
		bp = handle->bp;
	ep = bp + len;
	while (bp < ep) {
		const struct nlmsghdr *nlh = (const struct nlmsghdr *) bp;
		uint32_t msg_len;
		nftype_t type = OTHER;
		/*
		 * Has "pcap_breakloop()" been called?
		 * If so, return immediately - if we haven't read any
		 * packets, clear the flag and return PCAP_ERROR_BREAK
		 * to indicate that we were told to break out of the loop,
		 * otherwise leave the flag set, so that the *next* call
		 * will break out of the loop without having read any
		 * packets, and return the number of packets we've
		 * processed so far.
		 */
		if (handle->break_loop) {
			handle->bp = bp;
			handle->cc = (int)(ep - bp);
			if (count == 0) {
				handle->break_loop = 0;
				return PCAP_ERROR_BREAK;
			} else
				return count;
		}
		if (ep - bp < NLMSG_SPACE(0)) {
			/*
			 * There's less than one netlink message left
			 * in the buffer.  Give up.
			 */
			break;
		}

		if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || (u_int)len < nlh->nlmsg_len) {
			pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %zd) (nlmsg_len: %u)", len, nlh->nlmsg_len);
			return -1;
		}

		if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG &&
		    NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET)
			type = NFLOG;
		else if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_QUEUE &&
		         NFNL_MSG_TYPE(nlh->nlmsg_type) == NFQNL_MSG_PACKET)
			type = NFQUEUE;

		if (type != OTHER) {
			const unsigned char *payload = NULL;
			struct pcap_pkthdr pkth;

			const struct nfgenmsg *nfg = NULL;
			int id = 0;

			if (handle->linktype != DLT_NFLOG) {
				const struct nfattr *payload_attr = NULL;

				if (nlh->nlmsg_len < HDR_LENGTH) {
					pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len);
					return -1;
				}

				nfg = NLMSG_DATA(nlh);
				if (nlh->nlmsg_len > HDR_LENGTH) {
					struct nfattr *attr = NFM_NFA(nfg);
					int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH);

					while (NFA_OK(attr, attr_len)) {
						if (type == NFQUEUE) {
							switch (NFA_TYPE(attr)) {
								case NFQA_PACKET_HDR:
									{
										const struct nfqnl_msg_packet_hdr *pkt_hdr = (const struct nfqnl_msg_packet_hdr *) NFA_DATA(attr);

										id = ntohl(pkt_hdr->packet_id);
										break;
									}
								case NFQA_PAYLOAD:
									payload_attr = attr;
									break;
							}

						} else if (type == NFLOG) {
							switch (NFA_TYPE(attr)) {
								case NFULA_PAYLOAD:
									payload_attr = attr;
									break;
							}
						}
						attr = NFA_NEXT(attr, attr_len);
					}
				}

				if (payload_attr) {
					payload = NFA_DATA(payload_attr);
					pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr);
				}

			} else {
				payload = NLMSG_DATA(nlh);
				pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr));
			}

			if (payload) {
				/* pkth.caplen = min (payload_len, handle->snapshot); */

				gettimeofday(&pkth.ts, NULL);
				if (handle->fcode.bf_insns == NULL ||
						pcap_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen))
				{
					handlep->packets_read++;
					callback(user, &pkth, payload);
					count++;
				}
			}

			if (type == NFQUEUE) {
				/* XXX, possible responses: NF_DROP, NF_ACCEPT, NF_STOLEN, NF_QUEUE, NF_REPEAT, NF_STOP */
				/* if type == NFQUEUE, handle->linktype is always != DLT_NFLOG,
				   so nfg is always initialized to NLMSG_DATA(nlh). */
				if (nfg != NULL)
					nfqueue_send_verdict(handle, ntohs(nfg->res_id), id, NF_ACCEPT);
			}
		}

		msg_len = NLMSG_ALIGN(nlh->nlmsg_len);
		/*
		 * If the message length would run past the end of the
		 * buffer, truncate it to the remaining space in the
		 * buffer.
		 */
		if (msg_len > ep - bp)
			msg_len = (uint32_t)(ep - bp);

		bp += msg_len;
		if (count >= max_packets && !PACKET_COUNT_IS_UNLIMITED(max_packets)) {
			handle->bp = bp;
			handle->cc = (int)(ep - bp);
			if (handle->cc < 0)
				handle->cc = 0;
			return count;
		}
	}

	handle->cc = 0;
	return count;
}
Example #19
0
/* On error, -1 is returned and errno is set appropiately. On success, 
 * 0 is returned if there is no more data to process, >0 if there is
 * more data to process */
static int nfnl_step(struct nfnl_handle *h, struct nlmsghdr *nlh)
{
	struct nfnl_subsys_handle *ssh;
	uint8_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);
	uint8_t subsys_id = NFNL_SUBSYS_ID(nlh->nlmsg_type);

	/* Is this an error message? */
	if (nfnl_is_error(h, nlh)) {
		/* This is an ACK */
		if (errno == 0)
			return 0;
		/* This an error message */
		return -1;
	}
	
	/* nfnetlink sanity checks: check for nfgenmsg size */
	if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) {
		errno = ENOSPC;
		return -1;
	}

	if (subsys_id > NFNL_MAX_SUBSYS) {
		errno = ENOENT;
		return -1;
	}

	ssh = &h->subsys[subsys_id];
	if (!ssh) {
		errno = ENOENT;
		return -1;
	}

	if (type >= ssh->cb_count) {
		errno = ENOENT;
		return -1;
	}

	if (ssh->cb[type].attr_count) {
		int err;
		struct nfattr *tb[ssh->cb[type].attr_count];
		struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
		int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
		int len = nlh->nlmsg_len - NLMSG_ALIGN(min_len);

		err = nfnl_parse_attr(tb, ssh->cb[type].attr_count, attr, len);
		if (err == -1)
			return -1;

		if (ssh->cb[type].call) {
			/*
			 * On error, the callback returns NFNL_CB_FAILURE and
			 * errno must be explicitely set. On success, 
			 * NFNL_CB_STOP is returned and we're done, otherwise 
			 * NFNL_CB_CONTINUE means that we want to continue 
			 * data processing.
			 */
			return ssh->cb[type].call(nlh,
						  tb,
						  ssh->cb[type].data);
		}
	}
	/* no callback set, continue data processing */
	return 1;
}
Example #20
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);
}
static int
nflog_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
{
	const unsigned char *buf;
	int count = 0;
	int len;

	/* ignore interrupt system call error */
	do {
		len = recv(handle->fd, handle->buffer, handle->bufsize, 0);
		if (handle->break_loop) {
			handle->break_loop = 0;
			return -2;
		}
	} while ((len == -1) && (errno == EINTR));

	if (len < 0) {
		snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't receive packet %d:%s", errno, pcap_strerror(errno));
		return -1;
	}

	buf = handle->buffer;
	while (len >= NLMSG_SPACE(0)) {
		const struct nlmsghdr *nlh = (const struct nlmsghdr *) buf;
		u_int32_t msg_len;

		if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len) {
			snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len);
			return -1;
		}

		if (NFNL_SUBSYS_ID(nlh->nlmsg_type) == NFNL_SUBSYS_ULOG &&
			NFNL_MSG_TYPE(nlh->nlmsg_type) == NFULNL_MSG_PACKET)
		{
			const unsigned char *payload = NULL;
			struct pcap_pkthdr pkth;

			if (handle->linktype != DLT_NFLOG) {
				const struct nfattr *payload_attr = NULL;

				if (nlh->nlmsg_len < HDR_LENGTH) {
					snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len);
					return -1;
				}

				if (nlh->nlmsg_len > HDR_LENGTH) {
					struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
					int attr_len = nlh->nlmsg_len - NLMSG_ALIGN(HDR_LENGTH);

					while (NFA_OK(attr, attr_len)) {
						switch (NFA_TYPE(attr)) {
							case NFULA_PAYLOAD:
								payload_attr = attr;
								break;
						}
						attr = NFA_NEXT(attr, attr_len);
					}
				}

				if (payload_attr) {
					payload = NFA_DATA(payload_attr);
					pkth.len = pkth.caplen = NFA_PAYLOAD(payload_attr);
				}

			} else {
				payload = NLMSG_DATA(nlh);
				pkth.caplen = pkth.len = nlh->nlmsg_len-NLMSG_ALIGN(sizeof(struct nlmsghdr));
			}

			if (payload) {
				/* pkth.caplen = min (payload_len, handle->snapshot); */

				gettimeofday(&pkth.ts, NULL);
				if (handle->fcode.bf_insns == NULL ||
						bpf_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen))
				{
					handle->md.packets_read++;
					callback(user, &pkth, payload);
					count++;
				}
			}
		}

		msg_len = NLMSG_ALIGN(nlh->nlmsg_len);
		if (msg_len > len)
			msg_len = len;

		len -= msg_len;
		buf += msg_len;
	}
	return count;
}