static void parse_proto(struct nfattr *cda[], struct nfct_tuple *tuple)
{
	if (cda[CTA_IP_V4_SRC-1])
		tuple->src.v4 = *(u_int32_t *)NFA_DATA(cda[CTA_IP_V4_SRC-1]);

	if (cda[CTA_IP_V4_DST-1])
		tuple->dst.v4 = *(u_int32_t *)NFA_DATA(cda[CTA_IP_V4_DST-1]);
}
static void parse_proto(struct nfattr *cda[], struct nfct_tuple *tuple)
{
	if (cda[CTA_PROTO_SRC_PORT-1])
		tuple->l4src.sctp.port =
			*(u_int16_t *)NFA_DATA(cda[CTA_PROTO_SRC_PORT-1]);
	if (cda[CTA_PROTO_DST_PORT-1])
		tuple->l4dst.sctp.port =
			*(u_int16_t *)NFA_DATA(cda[CTA_PROTO_DST_PORT-1]);
}
Beispiel #3
0
void __nfa_fill(struct sk_buff *skb, int attrtype, int attrlen,
		const void *data)
{
	struct nfattr *nfa;
	int size = NFA_LENGTH(attrlen);

	nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size));
	nfa->nfa_type = attrtype;
	nfa->nfa_len  = size;
	memcpy(NFA_DATA(nfa), data, attrlen);
	memset(NFA_DATA(nfa) + attrlen, 0, NFA_ALIGN(size) - size);
}
static int ipv4_nfattr_to_tuple(struct nfattr *tb[],
				struct nf_conntrack_tuple *t)
{
	if (!tb[CTA_IP_V4_SRC-1] || !tb[CTA_IP_V4_DST-1])
		return -EINVAL;

	if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
		return -EINVAL;

	t->src.u3.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
	t->dst.u3.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]);

	return 0;
}
static void parse_proto(struct nfattr *cda[], struct nfct_tuple *tuple)
{
	if (cda[CTA_PROTO_ICMP_TYPE-1])
		tuple->l4dst.icmp.type =
			*(u_int8_t *)NFA_DATA(cda[CTA_PROTO_ICMP_TYPE-1]);

	if (cda[CTA_PROTO_ICMP_CODE-1])
		tuple->l4dst.icmp.code =
			*(u_int8_t *)NFA_DATA(cda[CTA_PROTO_ICMP_CODE-1]);

	if (cda[CTA_PROTO_ICMP_ID-1])
		tuple->l4src.icmp.id =
			*(u_int16_t *)NFA_DATA(cda[CTA_PROTO_ICMP_ID-1]);
}
/**
 * nfexp_parse_expect - translate a netlink message to a conntrack object
 * \param type do the translation iif the message type is of a certain type
 * \param nlh pointer to the netlink message
 * \param exp pointer to the conntrack object
 *
 * This is a low level function, use it in case that you require to work
 * with netlink details via libnfnetlink. Otherwise, we suggest you to
 * use the high level API.
 * 
 * The message types are:
 * 
 * NFEXP_T_NEW: parse messages with new conntracks
 * NFEXP_T_UPDATE: parse messages with conntrack updates
 * NFEXP_T_DESTROY: parse messages with conntrack destroy 
 * NFEXP_T_ALL: all message types
 * 
 * The message type is a flag, therefore the can be combined, ie.
 * NFEXP_T_NEW | NFEXP_T_DESTROY to parse only new and destroy messages
 * 
 * On error, NFEXP_T_ERROR is returned and errno is set appropiately. If 
 * the message received is not of the requested type then 0 is returned, 
 * otherwise this function returns the message type parsed.
 */
int nfexp_parse_expect(enum nf_conntrack_msg_type type,
		       const struct nlmsghdr *nlh,
		       struct nf_expect *exp)
{
	unsigned int flags;
	int len = nlh->nlmsg_len;
	struct nfgenmsg *nfhdr = NLMSG_DATA(nlh);
	struct nfattr *cda[CTA_EXPECT_MAX];

	assert(nlh != NULL);
	assert(exp != NULL);

	len -= NLMSG_LENGTH(sizeof(struct nfgenmsg));
	if (len < 0) {
		errno = EINVAL;
		return NFCT_T_ERROR;
	}

	flags = __parse_expect_message_type(nlh);
	if (!(flags & type))
		return 0;

	nfnl_parse_attr(cda, CTA_EXPECT_MAX, NFA_DATA(nfhdr), len);

	__parse_expect(nlh, cda, exp);

	return flags;
}
static int ipv6_nfattr_to_tuple(struct nfattr *tb[],
				struct nf_conntrack_tuple *t)
{
	if (!tb[CTA_IP_V6_SRC-1] || !tb[CTA_IP_V6_DST-1])
		return -EINVAL;

	if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
		return -EINVAL;

	memcpy(&t->src.u3.ip6, NFA_DATA(tb[CTA_IP_V6_SRC-1]),
	       sizeof(u_int32_t) * 4);
	memcpy(&t->dst.u3.ip6, NFA_DATA(tb[CTA_IP_V6_DST-1]),
	       sizeof(u_int32_t) * 4);

	return 0;
}
static int nfattr_to_tcp(struct nfattr *cda[], struct nf_conn *ct)
{
	struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1];
	struct nfattr *tb[CTA_PROTOINFO_TCP_MAX];

	/* updates could not contain anything about the private
	 * protocol info, in that case skip the parsing */
	if (!attr)
		return 0;

	nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);

	if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp))
		return -EINVAL;

	if (!tb[CTA_PROTOINFO_TCP_STATE-1])
		return -EINVAL;

	write_lock_bh(&tcp_lock);
	ct->proto.tcp.state =
		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
	write_unlock_bh(&tcp_lock);

	return 0;
}
static int icmp_nfattr_to_tuple(struct nfattr *tb[],
				struct ip_conntrack_tuple *tuple)
{
	if (!tb[CTA_PROTO_ICMP_TYPE-1]
	    || !tb[CTA_PROTO_ICMP_CODE-1]
	    || !tb[CTA_PROTO_ICMP_ID-1])
		return -EINVAL;

	tuple->dst.u.icmp.type = 
			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
	tuple->dst.u.icmp.code =
			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
	tuple->src.u.icmp.id =
			*(u_int16_t *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);

	if (tuple->dst.u.icmp.type >= sizeof(invmap)
	    || !invmap[tuple->dst.u.icmp.type])
		return -EINVAL;

	return 0;
}
static int icmp_nfattr_to_tuple(struct nfattr *tb[],
				struct nf_conntrack_tuple *tuple)
{
	if (!tb[CTA_PROTO_ICMP_TYPE-1]
	    || !tb[CTA_PROTO_ICMP_CODE-1]
	    || !tb[CTA_PROTO_ICMP_ID-1])
		return -EINVAL;

	if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
		return -EINVAL;

	tuple->dst.u.icmp.type =
			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
	tuple->dst.u.icmp.code =
			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
	tuple->src.u.icmp.id =
			*(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);

	if (tuple->dst.u.icmp.type >= sizeof(invmap)
	    || !invmap[tuple->dst.u.icmp.type])
		return -EINVAL;

	return 0;
}
static int
nfct_parse_secctx(const struct nlattr *attr, struct nf_conntrack *ct)
{
	struct nlattr *tb[CTA_SECCTX_MAX+1] = {};

	if (mnl_attr_parse_nested(attr, nfct_parse_secctx_attr_cb, tb) < 0)
		return -1;

	if (!tb[CTA_SECCTX_NAME])
		return 0;

	ct->secctx = strdup(NFA_DATA(tb[CTA_SECCTX_NAME]));
	if (ct->secctx)
		set_bit(ATTR_SECCTX, ct->head.set);

	return 0;
}
Beispiel #12
0
/**
 * nfnl_addattr_l - Add variable length attribute to nlmsghdr
 * @n: netlink message header to which attribute is to be added
 * @maxlen: maximum length of netlink message header
 * @type: type of new attribute
 * @data: content of new attribute
 * @len: attribute length
 */
int nfnl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
		   int alen)
{
	int len = NFA_LENGTH(alen);
	struct nfattr *nfa;

	assert(n);
	assert(maxlen > 0);
	assert(type >= 0);

	if ((NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
		errno = ENOSPC;
		return -1;
	}

	nfa = NLMSG_TAIL(n);
	nfa->nfa_type = type;
	nfa->nfa_len = len;
	memcpy(NFA_DATA(nfa), data, alen);
	n->nlmsg_len = (NLMSG_ALIGN(n->nlmsg_len) + NFA_ALIGN(len));
	return 0;
}
Beispiel #13
0
/**
 * nfnl_nfa_addattr_l - Add variable length attribute to struct nfattr
 *
 * @nfa: struct nfattr
 * @maxlen: maximal length of nfattr buffer
 * @type: type for new attribute
 * @data: content of new attribute
 * @alen: length of new attribute
 *
 */
int nfnl_nfa_addattr_l(struct nfattr *nfa, int maxlen, int type, 
		       const void *data, int alen)
{
	struct nfattr *subnfa;
	int len = NFA_LENGTH(alen);

	assert(nfa);
	assert(maxlen > 0);
	assert(type >= 0);

	if (NFA_ALIGN(nfa->nfa_len) + len > maxlen) {
		errno = ENOSPC;
		return -1;
	}

	subnfa = (struct nfattr *)(((char *)nfa) + NFA_ALIGN(nfa->nfa_len));
	subnfa->nfa_type = type;
	subnfa->nfa_len = len;
	memcpy(NFA_DATA(subnfa), data, alen);
	nfa->nfa_len = NFA_ALIGN(nfa->nfa_len) + len;

	return 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;
}
Beispiel #15
0
/**
 * nfnl_listen: listen for one or more netlink messages
 * @nfnhl: libnfnetlink handle
 * @handler: callback function to be called for every netlink message
 *          - the callback handler should normally return 0
 *          - but may return a negative error code which will cause
 *            nfnl_listen to return immediately with the same error code
 *          - or return a postivie error code which will cause 
 *            nfnl_listen to return after it has finished processing all
 *            the netlink messages in the current packet
 *          Thus a positive error code will terminate nfnl_listen "soon"
 *          without any loss of data, a negative error code will terminate
 *          nfnl_listen "very soon" and throw away data already read from
 *          the netlink socket.
 * @jarg: opaque argument passed on to callback
 *
 * This function is used to receive and process messages coming from an open
 * nfnetlink handler like events or information request via nfnl_send().
 *
 * On error, -1 is returned, unfortunately errno is not always set
 * appropiately. For that reason, the use of this function is DEPRECATED. 
 * Please, use nfnl_receive_process() instead.
 */
int nfnl_listen(struct nfnl_handle *nfnlh,
		int (*handler)(struct sockaddr_nl *, struct nlmsghdr *n,
			       void *), void *jarg)
{
	struct sockaddr_nl nladdr;
	char buf[NFNL_BUFFSIZE] __attribute__ ((aligned));
	struct iovec iov;
	int remain;
	struct nlmsghdr *h;
	struct nlmsgerr *msgerr;
	int quit=0;

	struct msghdr msg = {
		.msg_name    = &nladdr,
		.msg_namelen = sizeof(nladdr),
		.msg_iov     = &iov,
		.msg_iovlen  = 1,
	};

	memset(&nladdr, 0, sizeof(nladdr));
	nladdr.nl_family = AF_NETLINK;
	iov.iov_base = buf;
	iov.iov_len = sizeof(buf);

	while (! quit) {
		remain = recvmsg(nfnlh->fd, &msg, 0);
		if (remain < 0) {
			if (errno == EINTR)
				continue;
			/* Bad file descriptor */
			else if (errno == EBADF)
				break;
			else if (errno == EAGAIN)
				break;
			nfnl_error("recvmsg overrun: %s", strerror(errno));
			continue;
		}
		if (remain == 0) {
			nfnl_error("EOF on netlink");
			return -1;
		}
		if (msg.msg_namelen != sizeof(nladdr)) {
			nfnl_error("Bad sender address len (%d)",
				   msg.msg_namelen);
			return -1;
		}

		for (h = (struct nlmsghdr *)buf; remain >= sizeof(*h);) {
			int err;
			int len = h->nlmsg_len;
			int l = len - sizeof(*h);

			if (l < 0 || len > remain) {
				if (msg.msg_flags & MSG_TRUNC) {
					nfnl_error("MSG_TRUNC");
					return -1;
				}
				nfnl_error("Malformed msg (len=%d)", len);
				return -1;
			}

			/* end of messages reached, let's return */
			if (h->nlmsg_type == NLMSG_DONE)
				return 0;

			/* Break the loop if success is explicitely
			 * reported via NLM_F_ACK flag set */
			if (h->nlmsg_type == NLMSG_ERROR) {
				msgerr = NLMSG_DATA(h);
				return msgerr->error;
			}

			err = handler(&nladdr, h, jarg);
			if (err < 0)
				return err;
			quit |= err;
		
			/* FIXME: why not _NEXT macros, etc.? */
			//h = NLMSG_NEXT(h, remain);
			remain -= NLMSG_ALIGN(len);
			h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
		}
		if (msg.msg_flags & MSG_TRUNC) {
			nfnl_error("MSG_TRUNC");
			continue;
		}
		if (remain) {
			nfnl_error("remnant size %d", remain);
			return -1;
		}
	}

	return quit;
}

/**
 * nfnl_talk - send a request and then receive and process messages returned
 * @nfnlh: nfnetelink handler
 * @n: netlink message that contains the request
 * @peer: peer PID
 * @groups: netlink groups
 * @junk: callback called if out-of-sequence messages were received
 * @jarg: data for the junk callback
 *
 * This function is used to request an action that does not returns any
 * information. On error, a negative value is returned, errno could be
 * set appropiately. For that reason, the use of this function is DEPRECATED.
 * Please, use nfnl_query() instead.
 */
int nfnl_talk(struct nfnl_handle *nfnlh, struct nlmsghdr *n, pid_t peer,
	      unsigned groups, struct nlmsghdr *answer,
	      int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
	      void *jarg)
{
	char buf[NFNL_BUFFSIZE] __attribute__ ((aligned));
	struct sockaddr_nl nladdr;
	struct nlmsghdr *h;
	unsigned int seq;
	int status;
	struct iovec iov = {
		n, n->nlmsg_len
	};
	struct msghdr msg = {
		.msg_name    = &nladdr,
		.msg_namelen = sizeof(nladdr),
		.msg_iov     = &iov,
		.msg_iovlen  = 1,
	};

	memset(&nladdr, 0, sizeof(nladdr));
	nladdr.nl_family = AF_NETLINK;
	nladdr.nl_pid = peer;
	nladdr.nl_groups = groups;

	n->nlmsg_seq = seq = ++nfnlh->seq;
	/* FIXME: why ? */
	if (!answer)
		n->nlmsg_flags |= NLM_F_ACK;

	status = sendmsg(nfnlh->fd, &msg, 0);
	if (status < 0) {
		nfnl_error("sendmsg(netlink) %s", strerror(errno));
		return -1;
	}
	iov.iov_base = buf;
	iov.iov_len = sizeof(buf);

	while (1) {
		status = recvmsg(nfnlh->fd, &msg, 0);
		if (status < 0) {
			if (errno == EINTR)
				continue;
			nfnl_error("recvmsg over-run");
			continue;
		}
		if (status == 0) {
			nfnl_error("EOF on netlink");
			return -1;
		}
		if (msg.msg_namelen != sizeof(nladdr)) {
			nfnl_error("Bad sender address len %d",
				   msg.msg_namelen);
			return -1;
		}

		for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) {
			int len = h->nlmsg_len;
			int l = len - sizeof(*h);
			int err;

			if (l < 0 || len > status) {
				if (msg.msg_flags & MSG_TRUNC) {
					nfnl_error("Truncated message\n");
					return -1;
				}
				nfnl_error("Malformed message: len=%d\n", len);
				return -1; /* FIXME: libnetlink exits here */
			}

			if (h->nlmsg_pid != nfnlh->local.nl_pid ||
			    h->nlmsg_seq != seq) {
				if (junk) {
					err = junk(&nladdr, h, jarg);
					if (err < 0)
						return err;
				}
				goto cont;
			}

			if (h->nlmsg_type == NLMSG_ERROR) {
				struct nlmsgerr *err = NLMSG_DATA(h);
				if (l < sizeof(struct nlmsgerr))
					nfnl_error("ERROR truncated\n");
				else {
					errno = -err->error;
					if (errno == 0) {
						if (answer)
							memcpy(answer, h, h->nlmsg_len);
						return 0;
					}
					perror("NFNETLINK answers");
				}
				return err->error;
			}
			if (answer) {
				memcpy(answer, h, h->nlmsg_len);
				return 0;
			}

			nfnl_error("Unexpected reply!\n");
cont:
			status -= NLMSG_ALIGN(len);
			h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
		}
		if (msg.msg_flags & MSG_TRUNC) {
			nfnl_error("Messages truncated\n");
			continue;
		}
		if (status)
			nfnl_error("Remnant of size %d\n", status);
	}
}

/**
 * nfnl_addattr_l - Add variable length attribute to nlmsghdr
 * @n: netlink message header to which attribute is to be added
 * @maxlen: maximum length of netlink message header
 * @type: type of new attribute
 * @data: content of new attribute
 * @len: attribute length
 */
int nfnl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
		   int alen)
{
	int len = NFA_LENGTH(alen);
	struct nfattr *nfa;

	assert(n);
	assert(maxlen > 0);
	assert(type >= 0);

	if ((NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
		errno = ENOSPC;
		return -1;
	}

	nfa = NLMSG_TAIL(n);
	nfa->nfa_type = type;
	nfa->nfa_len = len;
	memcpy(NFA_DATA(nfa), data, alen);
	memset((uint8_t *)nfa + nfa->nfa_len, 0, NFA_ALIGN(alen) - alen);
	n->nlmsg_len = (NLMSG_ALIGN(n->nlmsg_len) + NFA_ALIGN(len));
	return 0;
}
ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h,
				     const unsigned char *buf,
				     size_t len)
{
	struct nlmsghdr *nlh;
	struct nfattr *tb[NFULA_MAX];
	struct nfulnl_msg_packet_hdr *hdr;

	if (!h->last_nlh) {
		printf("first\n");
		nlh = nfnl_get_msg_first(nflog_nfnlh(h->nfulh), buf, len);
	}else {
next_msg:	printf("next\n");
		nlh = nfnl_get_msg_next(nflog_nfnlh(h->nfulh), buf, len);
	}
	h->last_nlh = nlh;

	if (!nlh)
		return NULL;

	nfnl_parse_attr(tb, NFULA_MAX, NFM_NFA(NLMSG_DATA(nlh)),
			NFM_PAYLOAD(nlh));
	
	if (!tb[NFULA_PACKET_HDR-1])
		goto next_msg;

	/* now build the fake ulog_packet_msg */
	hdr = NFA_DATA(tb[NFULA_PACKET_HDR-1]);
	h->upmsg.hook = hdr->hook;

	if (tb[NFULA_MARK-1])
		h->upmsg.mark = ntohl(*(u_int32_t *)NFA_DATA(tb[NFULA_MARK-1]));
	else
		h->upmsg.mark = 0;

	if (tb[NFULA_TIMESTAMP]) {
		/* FIXME: 64bit network-to-host */
		h->upmsg.timestamp_sec = h->upmsg.timestamp_usec = 0;
	} else
		h->upmsg.timestamp_sec = h->upmsg.timestamp_usec = 0;

	if (tb[NFULA_IFINDEX_INDEV-1]) {
		/* FIXME: ifindex lookup */	
		h->upmsg.indev_name[0] = '\0';
	} else
		h->upmsg.indev_name[0] = '\0';

	if (tb[NFULA_IFINDEX_OUTDEV-1]) {
		/* FIXME: ifindex lookup */	
		h->upmsg.outdev_name[0] = '\0';
	} else
		h->upmsg.outdev_name[0] = '\0';

	if (tb[NFULA_HWADDR-1]) {
		struct nfulnl_msg_packet_hw *phw = NFA_DATA(tb[NFULA_HWADDR-1]);
		h->upmsg.mac_len = ntohs(phw->hw_addrlen);
		memcpy(h->upmsg.mac, phw->hw_addr, 8);
	} else
		h->upmsg.mac_len = 0;

	if (tb[NFULA_PREFIX-1]) {
		int plen = NFA_PAYLOAD(tb[NFULA_PREFIX-1]);
		if (ULOG_PREFIX_LEN < plen)
			plen = ULOG_PREFIX_LEN;
		memcpy(h->upmsg.prefix, NFA_DATA(tb[NFULA_PREFIX-1]), plen);
		h->upmsg.prefix[ULOG_PREFIX_LEN-1] = '\0';
	}

	if (tb[NFULA_PAYLOAD-1]) {
		memcpy(h->upmsg.payload, NFA_DATA(tb[NFULA_PAYLOAD-1]),
			NFA_PAYLOAD(tb[NFULA_PAYLOAD-1]));
		h->upmsg.data_len = NFA_PAYLOAD(tb[NFULA_PAYLOAD-1]);
	} else
		h->upmsg.data_len = 0;
	
	return &h->upmsg;
}
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;
}
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;
}
/* This is an inline function, we don't really care about a long
 * list of arguments */
static inline int 
__build_packet_message(struct nfulnl_instance *inst,
			const struct sk_buff *skb, 
			unsigned int data_len,
			unsigned int pf,
			unsigned int hooknum,
			const struct net_device *indev,
			const struct net_device *outdev,
			const struct nf_loginfo *li,
			const char *prefix)
{
	unsigned char *old_tail;
	struct nfulnl_msg_packet_hdr pmsg;
	struct nlmsghdr *nlh;
	struct nfgenmsg *nfmsg;
	u_int32_t tmp_uint;

	UDEBUG("entered\n");
		
	old_tail = inst->skb->tail;
	nlh = NLMSG_PUT(inst->skb, 0, 0, 
			NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET,
			sizeof(struct nfgenmsg));
	nfmsg = NLMSG_DATA(nlh);
	nfmsg->nfgen_family = pf;
	nfmsg->version = NFNETLINK_V0;
	nfmsg->res_id = htons(inst->group_num);

	pmsg.hw_protocol	= htons(skb->protocol);
	pmsg.hook		= hooknum;

	NFA_PUT(inst->skb, NFULA_PACKET_HDR, sizeof(pmsg), &pmsg);

	if (prefix) {
		int slen = strlen(prefix);
		if (slen > NFULNL_PREFIXLEN)
			slen = NFULNL_PREFIXLEN;
		NFA_PUT(inst->skb, NFULA_PREFIX, slen, prefix);
	}

	if (indev) {
		tmp_uint = htonl(indev->ifindex);
#ifndef CONFIG_BRIDGE_NETFILTER
		NFA_PUT(inst->skb, NFULA_IFINDEX_INDEV, sizeof(tmp_uint),
			&tmp_uint);
#else
		if (pf == PF_BRIDGE) {
			/* Case 1: outdev is physical input device, we need to
			 * look for bridge group (when called from
			 * netfilter_bridge) */
			NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSINDEV,
				sizeof(tmp_uint), &tmp_uint);
			/* this is the bridge group "brX" */
			tmp_uint = htonl(indev->br_port->br->dev->ifindex);
			NFA_PUT(inst->skb, NFULA_IFINDEX_INDEV,
				sizeof(tmp_uint), &tmp_uint);
		} else {
			/* Case 2: indev is bridge group, we need to look for
			 * physical device (when called from ipv4) */
			NFA_PUT(inst->skb, NFULA_IFINDEX_INDEV,
				sizeof(tmp_uint), &tmp_uint);
			if (skb->nf_bridge && skb->nf_bridge->physindev) {
				tmp_uint = 
				    htonl(skb->nf_bridge->physindev->ifindex);
				NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSINDEV,
					sizeof(tmp_uint), &tmp_uint);
			}
		}
#endif
	}

	if (outdev) {
		tmp_uint = htonl(outdev->ifindex);
#ifndef CONFIG_BRIDGE_NETFILTER
		NFA_PUT(inst->skb, NFULA_IFINDEX_OUTDEV, sizeof(tmp_uint),
			&tmp_uint);
#else
		if (pf == PF_BRIDGE) {
			/* Case 1: outdev is physical output device, we need to
			 * look for bridge group (when called from
			 * netfilter_bridge) */
			NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSOUTDEV,
				sizeof(tmp_uint), &tmp_uint);
			/* this is the bridge group "brX" */
			tmp_uint = htonl(outdev->br_port->br->dev->ifindex);
			NFA_PUT(inst->skb, NFULA_IFINDEX_OUTDEV,
				sizeof(tmp_uint), &tmp_uint);
		} else {
			/* Case 2: indev is a bridge group, we need to look
			 * for physical device (when called from ipv4) */
			NFA_PUT(inst->skb, NFULA_IFINDEX_OUTDEV,
				sizeof(tmp_uint), &tmp_uint);
			if (skb->nf_bridge) {
				tmp_uint = 
				    htonl(skb->nf_bridge->physoutdev->ifindex);
				NFA_PUT(inst->skb, NFULA_IFINDEX_PHYSOUTDEV,
					sizeof(tmp_uint), &tmp_uint);
			}
		}
#endif
	}

	if (skb->nfmark) {
		tmp_uint = htonl(skb->nfmark);
		NFA_PUT(inst->skb, NFULA_MARK, sizeof(tmp_uint), &tmp_uint);
	}

	if (indev && skb->dev && skb->dev->hard_header_parse) {
		struct nfulnl_msg_packet_hw phw;

		phw.hw_addrlen = 
			skb->dev->hard_header_parse((struct sk_buff *)skb, 
						    phw.hw_addr);
		phw.hw_addrlen = htons(phw.hw_addrlen);
		NFA_PUT(inst->skb, NFULA_HWADDR, sizeof(phw), &phw);
	}

	if (skb->tstamp.off_sec) {
		struct nfulnl_msg_packet_timestamp ts;

		ts.sec = cpu_to_be64(skb->tstamp.off_sec);
		ts.usec = cpu_to_be64(skb->tstamp.off_usec);

		NFA_PUT(inst->skb, NFULA_TIMESTAMP, sizeof(ts), &ts);
	}

	/* UID */
	if (skb->sk) {
		read_lock_bh(&skb->sk->sk_callback_lock);
		if (skb->sk->sk_socket && skb->sk->sk_socket->file) {
			u_int32_t uid = htonl(skb->sk->sk_socket->file->f_uid);
			/* need to unlock here since NFA_PUT may goto */
			read_unlock_bh(&skb->sk->sk_callback_lock);
			NFA_PUT(inst->skb, NFULA_UID, sizeof(uid), &uid);
		} else
			read_unlock_bh(&skb->sk->sk_callback_lock);
	}

	/* local sequence number */
	if (inst->flags & NFULNL_CFG_F_SEQ) {
		tmp_uint = htonl(inst->seq++);
		NFA_PUT(inst->skb, NFULA_SEQ, sizeof(tmp_uint), &tmp_uint);
	}
	/* global sequence number */
	if (inst->flags & NFULNL_CFG_F_SEQ_GLOBAL) {
		tmp_uint = atomic_inc_return(&global_seq);
		NFA_PUT(inst->skb, NFULA_SEQ_GLOBAL, sizeof(tmp_uint), &tmp_uint);
	}

	if (data_len) {
		struct nfattr *nfa;
		int size = NFA_LENGTH(data_len);

		if (skb_tailroom(inst->skb) < (int)NFA_SPACE(data_len)) {
			printk(KERN_WARNING "nfnetlink_log: no tailroom!\n");
			goto nlmsg_failure;
		}

		nfa = (struct nfattr *)skb_put(inst->skb, NFA_ALIGN(size));
		nfa->nfa_type = NFULA_PAYLOAD;
		nfa->nfa_len = size;

		if (skb_copy_bits(skb, 0, NFA_DATA(nfa), data_len))
			BUG();
	}
		
	nlh->nlmsg_len = inst->skb->tail - old_tail;
	return 0;

nlmsg_failure:
	UDEBUG("nlmsg_failure\n");
nfattr_failure:
	PRINTR(KERN_ERR "nfnetlink_log: error creating log nlmsg\n");
	return -1;
}