#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 eth = -1;
register int i;
char szStr[STRSIZ];
struct ifreq ifr;
u_int n = 1;
#ifndef __FreeBSD__
if (zcbuf) {
io_SetErr(ENOTSUP, "bpf zero copy buffer mode is not supported");
return -1;
}
#endif
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 (!zcbuf) {
if (ioctl(eth, BIOCGBLEN, &n) == -1) {
LOGERR;
close(eth);
return -1;
} else
n = (buflen && *buflen) ? *buflen : n;
if (ioctl(eth, BIOCSBLEN, &n) == -1) {
LOGERR;
close(eth);
return -1;
}
if (buflen && *buflen)
*buflen = n;
} 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
n = (buflen && *buflen) ? *buflen : n;
if (!(*zcbuf = allocZCbuf(n))) {
close(eth);
return -1;
}
if (ioctl(eth, BIOCSETZBUF, *zcbuf) == -1) {
LOGERR;
io_etherClose(eth, zcbuf);
return -1;
}
if (buflen && *buflen)
*buflen = n;
#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;
}
n = 1;
if (whdr && ioctl(eth, BIOCSHDRCMPLT, &n) == -1) {
LOGERR;
io_etherClose(eth, zcbuf);
return -1;
}
if (ioctl(eth, BIOCIMMEDIATE, &n) == -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 *bzh)
{
atomic_store_rel_int(&bzh->bzh_user_gen, bzh->bzh_kernel_gen);
}
static inline int
chkZCbuf(struct bpf_zbuf_header *bzh)
{
/* return true if userspace owns buffer, and false otherwise. */
return (bzh->bzh_user_gen != atomic_load_acq_int(&bzh->bzh_kernel_gen));
}
static ssize_t
nextZCbuf(int eth, struct bpf_zbuf * __restrict zbuf, void * __restrict buf, size_t buflen)
{
ssize_t rlen = 0;
struct bpf_zbuf bz;
struct bpf_zbuf_header *bzh;
off_t pos = 0;
bzh = (struct bpf_zbuf_header *) zbuf->bz_bufa;
if (chkZCbuf(bzh)) {
rlen = MIN(atomic_load_acq_int(&bzh->bzh_kernel_len), buflen);
memcpy(buf + pos, zbuf->bz_bufa + sizeof(struct bpf_zbuf_header), rlen);
ackZCbuf(bzh);
pos += rlen;
}
bzh = (struct bpf_zbuf_header *) zbuf->bz_bufb;
if (chkZCbuf(bzh)) {
rlen = MIN(atomic_load_acq_int(&bzh->bzh_kernel_len), buflen);
memcpy(buf + pos, zbuf->bz_bufb + sizeof(struct bpf_zbuf_header), rlen);
ackZCbuf(bzh);
pos += rlen;
}
if (!pos) {
if ((rlen = ioctl(eth, BIOCROTZBUF, &bz)) == -1)
LOGERR;
} else
rlen = pos;
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;
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(eth, (struct bpf_zbuf*) zcbuf, buf, buflen);
if (!rlen)
rlen = nextZCbuf(eth, (struct bpf_zbuf*) zcbuf, buf, buflen);
#else
rlen = -1;
io_SetErr(ENOTSUP, "bpf zero copy buffer mode is not supported");
#endif
}
return rlen;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>