File:  [ELWIX - Embedded LightWeight unIX -] / libaitio / src / bpf.c
Revision 1.1.2.10: download - view: text, annotated - select for diffs - revision graph
Tue Jun 25 09:29:34 2013 UTC (11 years ago) by misho
Branches: io5_4
adds zeroing

#include "global.h"


/*
 * io_get1stiface() - Get first interface of host
 *
 * @szIface = interface string buffer
 * @iflen = size of interface buffer
 * return: -1 error or 0 ok
 */
int
io_get1stiface(char *szIface, int iflen)
{
	struct ifaddrs *ifa;

	if (!szIface || !iflen)
		return -1;

	getifaddrs(&ifa);
	strlcpy(szIface, ifa->ifa_name, iflen);
	freeifaddrs(ifa);
	return 0;
}

/*
 * io_etherClose() - Close BPF interface
 *
 * @eth = bpf handle
 * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
 * return: none
 */
void
io_etherClose(int eth, void **zcbuf)
{
#ifdef __FreeBSD__
	struct bpf_zbuf *zbuf = NULL;
#endif

	if (eth > STDERR_FILENO)
		close(eth);

	if (zcbuf && *zcbuf) {
#ifdef __FreeBSD__
		zbuf = *zcbuf;
		munmap(zbuf->bz_bufb, zbuf->bz_buflen);
		munmap(zbuf->bz_bufa, zbuf->bz_buflen);
		e_free(*zcbuf);
		*zcbuf = NULL;
#endif
	}
}

#ifdef __FreeBSD__
static inline struct bpf_zbuf *
allocZCbuf(u_int len)
{
	struct bpf_zbuf *zbuf = NULL;

	zbuf = e_malloc(sizeof(struct bpf_zbuf));
	if (!zbuf) {
		io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
		return NULL;
	} else
		memset(zbuf, 0, sizeof(struct bpf_zbuf));

	zbuf->bz_buflen = roundup(len, getpagesize());
	zbuf->bz_bufa = mmap(NULL, zbuf->bz_buflen, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
	if (zbuf->bz_bufa == MAP_FAILED) {
		LOGERR;
		e_free(zbuf);
		return NULL;
	} else
		memset(zbuf->bz_bufa, 0, zbuf->bz_buflen);
	zbuf->bz_bufb = mmap(NULL, zbuf->bz_buflen, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
	if (zbuf->bz_bufb == MAP_FAILED) {
		LOGERR;
		munmap(zbuf->bz_bufa, zbuf->bz_buflen);
		e_free(zbuf);
		return NULL;
	} else
		memset(zbuf->bz_bufb, 0, zbuf->bz_buflen);

	return zbuf;
}
#endif

/*
 * io_etherOpen() - Open BPF interface to device
 *
 * @csIface = interface name
 * @flags = open flags
 * @whdr = with complete headers
 * @buflen = buffer length
 * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
 * return: -1 error or >-1 bpf handle
 */
int
io_etherOpen(const char *csIface, int flags, int whdr, u_int *buflen, void **zcbuf)
{
	int n = 1, eth = -1;
	register int i;
	char szStr[STRSIZ];
	struct ifreq ifr;

	for (i = 0; i < BPF_DEV_MAX; i++) {
		memset(szStr, 0, sizeof szStr);
		snprintf(szStr, sizeof szStr, "/dev/bpf%d", i);
		eth = open(szStr, flags);
		if (eth > STDERR_FILENO)
			break;
	}
	if (eth < 3) {
		LOGERR;
		return -1;
	}

	if (ioctl(eth, BIOCIMMEDIATE, &n) == -1) {
		LOGERR;
		close(eth);
		return -1;
	}
	if (whdr && ioctl(eth, BIOCSHDRCMPLT, &n) == -1) {
		LOGERR;
		close(eth);
		return -1;
	}
	if (buflen && *buflen) {
		if (!zcbuf) {
			if (ioctl(eth, BIOCSBLEN, buflen) == -1) {
				LOGERR;
				close(eth);
				return -1;
			}
		} else {
#ifdef __FreeBSD__
			n = BPF_BUFMODE_ZBUF;
			if (ioctl(eth, BIOCSETBUFMODE, &n) == -1) {
				LOGERR;
				close(eth);
				return -1;
			}
			if (ioctl(eth, BIOCGETZMAX, &n) == -1) {
				LOGERR;
				close(eth);
				return -1;
			} else
				*buflen = MIN(n, *buflen);
			if (!(*zcbuf = allocZCbuf(*buflen))) {
				close(eth);
				return -1;
			}
			if (ioctl(eth, BIOCSETZBUF, (struct bpf_zbuf*) *zcbuf) == -1) {
				LOGERR;
				io_etherClose(eth, zcbuf);
				return -1;
			}
#endif
		}
	}
	if (csIface)
		strlcpy(szStr, csIface, sizeof szStr);
	else if (io_get1stiface(szStr, sizeof szStr) == -1) {
		io_etherClose(eth, zcbuf);
		return -1;
	}
	memset(&ifr, 0, sizeof ifr);
	strlcpy(ifr.ifr_name, szStr, sizeof ifr.ifr_name);
	if (ioctl(eth, BIOCSETIF, &ifr) == -1) {
		LOGERR;
		io_etherClose(eth, zcbuf);
		return -1;
	}

	return eth;
}

/*
 * io_etherSend() - Send packet to bpf
 *
 * @eth = bpf handle
 * @buf = buffer
 * @buflen = buffer length
 * return: -1 error or !=-1 written bytes
 */
ssize_t
io_etherSend(int eth, const void *buf, size_t buflen)
{
	ssize_t wlen = 0;

	if (!buf || !buflen) {
		io_SetErr(EINVAL, "invalid arguments");
		return -1;
	}

	wlen = write(eth, buf, buflen);
	if (wlen == -1)
		LOGERR;
	return wlen;
}

#ifdef __FreeBSD__
static inline void
ackZCbuf(struct bpf_zbuf_header * __restrict bzh)
{
	atomic_store_rel_int(&bzh->bzh_user_gen, bzh->bzh_kernel_gen);
}

static inline int
chkZCbuf(struct bpf_zbuf_header * __restrict bzh)
{
	/* return true if userspace owns buffer, and false otherwise. */
	return (bzh->bzh_user_gen != atomic_load_acq_int(&bzh->bzh_kernel_gen));
}

static inline ssize_t
nextZCbuf(void ** __restrict zcache, struct bpf_zbuf * __restrict zbuf, 
		const void * __restrict buf)
{
	ssize_t rlen = -1;
	struct bpf_zbuf_header *bzh;

	if (!*zcache || *zcache == zbuf->bz_bufb) {
		bzh = (struct bpf_zbuf_header *) zbuf->bz_bufa;
		if (chkZCbuf(bzh)) {
			rlen = atomic_load_acq_int(&bzh->bzh_kernel_len);
			*zcache = zbuf->bz_bufa;
			if (buf)
				buf = ((caddr_t) *zcache) + sizeof(struct bpf_zbuf_header);
			ackZCbuf(bzh);
		} else
			io_SetErr(EAGAIN, "kernel owns the buffer");
	} else if (*zcache == zbuf->bz_bufa) {
		bzh = (struct bpf_zbuf_header *) zbuf->bz_bufb;
		if (chkZCbuf(bzh)) {
			rlen = atomic_load_acq_int(&bzh->bzh_kernel_len);
			*zcache = zbuf->bz_bufb;
			if (buf)
				buf = ((caddr_t) *zcache) + sizeof(struct bpf_zbuf_header);
			ackZCbuf(bzh);
		} else
			io_SetErr(EAGAIN, "kernel owns the buffer");
	}

	return rlen;
}
#endif

/*
 * io_etherRecv() - Receive packet from bpf
 *
 * @eth = bpf handle
 * @buf = buffer
 * @buflen = buffer length
 * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
 * return: -1 error or !=-1 readed bytes
 */
ssize_t
io_etherRecv(int eth, void * __restrict buf, size_t buflen, void * __restrict zcbuf)
{
	ssize_t rlen = 0;
	void **zcache = NULL;

	if (!buf || !buflen) {
		io_SetErr(EINVAL, "invalid arguments");
		return -1;
	}

	if (!zcbuf) {
		rlen = read(eth, buf, buflen);
		if (rlen == -1)
			LOGERR;
	} else {
#ifdef __FreeBSD__
		rlen = nextZCbuf(zcache, (struct bpf_zbuf*) zcbuf, buf);
#else
		io_SetErr(ENOTSUP, "bpf zero copy buffer mode is not supported");
#endif
	}

	return rlen;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>