Example #1
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);
}
Example #2
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;
}
Example #3
0
/**
 * nfnl_build_nfa_iovec - Build two iovec's from tag, length and value
 *
 * @iov: pointer to array of two 'struct iovec' (caller-allocated)
 * @nfa: pointer to 'struct nfattr' (caller-allocated)
 * @type: type (tag) of attribute
 * @len: length of value
 * @val: pointer to buffer containing 'value'
 *
 */ 
void nfnl_build_nfa_iovec(struct iovec *iov, struct nfattr *nfa, 
			  uint16_t type, uint32_t len, unsigned char *val)
{
	assert(iov);
	assert(nfa);

        /* Set the attribut values */ 
        nfa->nfa_len = sizeof(struct nfattr) + len;
        nfa->nfa_type = type;

	iov[0].iov_base = nfa;
	iov[0].iov_len = sizeof(*nfa);
	iov[1].iov_base = val;
	iov[1].iov_len = NFA_ALIGN(len);
}
Example #4
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;
}
Example #5
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;
}
Example #6
0
/* 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;
}