Annotation of libaitio/src/bpf.c, revision 1.3.8.3
1.2 misho 1: /*************************************************************************
2: * (C) 2013 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
1.3.8.3 ! misho 6: * $Id: bpf.c,v 1.3.8.2 2013/10/21 15:39:15 misho Exp $
1.2 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: /*
50: * io_get1stiface() - Get first interface of host
51: *
52: * @szIface = interface string buffer
53: * @iflen = size of interface buffer
54: * return: -1 error or 0 ok
55: */
56: int
57: io_get1stiface(char *szIface, int iflen)
58: {
59: struct ifaddrs *ifa;
60:
61: if (!szIface || !iflen)
62: return -1;
63:
64: getifaddrs(&ifa);
65: strlcpy(szIface, ifa->ifa_name, iflen);
66: freeifaddrs(ifa);
67: return 0;
68: }
69:
70: /*
71: * io_etherClose() - Close BPF interface
72: *
73: * @eth = bpf handle
74: * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
75: * return: none
76: */
77: void
78: io_etherClose(int eth, void **zcbuf)
79: {
80: #ifdef __FreeBSD__
81: struct bpf_zbuf *zbuf = NULL;
82: #endif
83:
84: if (eth > STDERR_FILENO)
85: close(eth);
86:
87: if (zcbuf && *zcbuf) {
88: #ifdef __FreeBSD__
89: zbuf = *zcbuf;
90: munmap(zbuf->bz_bufb, zbuf->bz_buflen);
91: munmap(zbuf->bz_bufa, zbuf->bz_buflen);
92: e_free(*zcbuf);
93: *zcbuf = NULL;
94: #endif
95: }
96: }
97:
98: #ifdef __FreeBSD__
99: static inline struct bpf_zbuf *
100: allocZCbuf(u_int len)
101: {
102: struct bpf_zbuf *zbuf = NULL;
103:
104: zbuf = e_malloc(sizeof(struct bpf_zbuf));
105: if (!zbuf) {
106: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
107: return NULL;
108: } else
109: memset(zbuf, 0, sizeof(struct bpf_zbuf));
110:
111: zbuf->bz_buflen = roundup(len, getpagesize());
112: zbuf->bz_bufa = mmap(NULL, zbuf->bz_buflen, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
113: if (zbuf->bz_bufa == MAP_FAILED) {
114: LOGERR;
115: e_free(zbuf);
116: return NULL;
117: } else
118: memset(zbuf->bz_bufa, 0, zbuf->bz_buflen);
119: zbuf->bz_bufb = mmap(NULL, zbuf->bz_buflen, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
120: if (zbuf->bz_bufb == MAP_FAILED) {
121: LOGERR;
122: munmap(zbuf->bz_bufa, zbuf->bz_buflen);
123: e_free(zbuf);
124: return NULL;
125: } else
126: memset(zbuf->bz_bufb, 0, zbuf->bz_buflen);
127:
128: return zbuf;
129: }
130: #endif
131:
132: /*
133: * io_etherOpen() - Open BPF interface to device
134: *
135: * @csIface = interface name
136: * @flags = open flags
137: * @whdr = with complete headers
1.3 misho 138: * @wdlt = with data link type
1.2 misho 139: * @buflen = buffer length
140: * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
141: * return: -1 error or >-1 bpf handle
142: */
143: int
1.3 misho 144: io_etherOpen(const char *csIface, int flags, int whdr, int wdlt,
145: u_int *buflen, void **zcbuf)
1.2 misho 146: {
147: int eth = -1;
148: register int i;
149: char szStr[STRSIZ];
150: struct ifreq ifr;
151: u_int n = 1;
152:
153: #ifndef __FreeBSD__
154: if (zcbuf) {
155: io_SetErr(ENOTSUP, "bpf zero copy buffer mode is not supported");
156: return -1;
157: }
158: #endif
159:
160: for (i = 0; i < BPF_DEV_MAX; i++) {
161: memset(szStr, 0, sizeof szStr);
162: snprintf(szStr, sizeof szStr, "/dev/bpf%d", i);
163: eth = open(szStr, flags);
164: if (eth > STDERR_FILENO)
165: break;
166: }
167: if (eth < 3) {
168: LOGERR;
169: return -1;
170: }
171:
1.3.8.3 ! misho 172: if (csIface)
! 173: strlcpy(szStr, csIface, sizeof szStr);
! 174: else if (io_get1stiface(szStr, sizeof szStr) == -1) {
! 175: close(eth);
! 176: return -1;
! 177: }
! 178:
! 179: n = 1;
! 180: if (whdr && ioctl(eth, BIOCSHDRCMPLT, &n) == -1) {
! 181: LOGERR;
! 182: close(eth);
! 183: return -1;
! 184: }
! 185: if (wdlt && ioctl(eth, BIOCSDLT, &wdlt) == -1) {
! 186: LOGERR;
! 187: close(eth);
! 188: return -1;
! 189: }
! 190: if (ioctl(eth, BIOCIMMEDIATE, &n) == -1) {
! 191: LOGERR;
! 192: close(eth);
! 193: return -1;
! 194: }
! 195:
1.2 misho 196: if (!zcbuf) {
1.3.8.3 ! misho 197: n = (buflen && *buflen) ? *buflen : getpagesize();
1.2 misho 198: if (ioctl(eth, BIOCSBLEN, &n) == -1) {
199: LOGERR;
200: close(eth);
201: return -1;
202: }
203: } else {
204: #ifdef __FreeBSD__
205: n = BPF_BUFMODE_ZBUF;
206: if (ioctl(eth, BIOCSETBUFMODE, &n) == -1) {
207: LOGERR;
208: close(eth);
209: return -1;
210: }
211: if (ioctl(eth, BIOCGETZMAX, &n) == -1) {
212: LOGERR;
213: close(eth);
214: return -1;
215: } else
216: n = (buflen && *buflen) ? *buflen : n;
217: if (!(*zcbuf = allocZCbuf(n))) {
218: close(eth);
219: return -1;
220: }
221: if (ioctl(eth, BIOCSETZBUF, *zcbuf) == -1) {
222: LOGERR;
223: io_etherClose(eth, zcbuf);
224: return -1;
225: }
226: if (buflen && *buflen)
227: *buflen = n;
228: #endif
229: }
230:
231: memset(&ifr, 0, sizeof ifr);
232: strlcpy(ifr.ifr_name, szStr, sizeof ifr.ifr_name);
233: if (ioctl(eth, BIOCSETIF, &ifr) == -1) {
234: LOGERR;
235: io_etherClose(eth, zcbuf);
236: return -1;
237: }
238:
1.3.8.3 ! misho 239: if (buflen && *buflen) {
! 240: if (ioctl(eth, BIOCGBLEN, &n) == -1) {
! 241: LOGERR;
! 242: io_etherClose(eth, zcbuf);
! 243: return -1;
! 244: } else
! 245: *buflen = n;
1.2 misho 246: }
247:
248: return eth;
249: }
250:
251: /*
252: * io_etherSend() - Send packet to bpf
253: *
254: * @eth = bpf handle
255: * @buf = buffer
256: * @buflen = buffer length
257: * return: -1 error or !=-1 written bytes
258: */
259: ssize_t
260: io_etherSend(int eth, const void *buf, size_t buflen)
261: {
262: ssize_t wlen = 0;
263:
264: if (!buf || !buflen) {
265: io_SetErr(EINVAL, "invalid arguments");
266: return -1;
267: }
268:
269: wlen = write(eth, buf, buflen);
270: if (wlen == -1)
271: LOGERR;
272: return wlen;
273: }
274:
275: #ifdef __FreeBSD__
276: static inline void
277: ackZCbuf(struct bpf_zbuf_header *bzh)
278: {
279: atomic_store_rel_int(&bzh->bzh_user_gen, bzh->bzh_kernel_gen);
280: }
281:
282: static inline int
283: chkZCbuf(struct bpf_zbuf_header *bzh)
284: {
285: /* return true if userspace owns buffer, and false otherwise. */
286: return (bzh->bzh_user_gen != atomic_load_acq_int(&bzh->bzh_kernel_gen));
287: }
288:
289: static ssize_t
290: nextZCbuf(int eth, struct bpf_zbuf * __restrict zbuf, void * __restrict buf, size_t buflen)
291: {
292: ssize_t rlen = 0;
293: struct bpf_zbuf bz;
294: struct bpf_zbuf_header *bzh;
295: off_t pos = 0;
296:
297: bzh = (struct bpf_zbuf_header *) zbuf->bz_bufa;
298: if (chkZCbuf(bzh)) {
299: rlen = MIN(atomic_load_acq_int(&bzh->bzh_kernel_len), buflen);
300: memcpy(buf + pos, zbuf->bz_bufa + sizeof(struct bpf_zbuf_header), rlen);
301: ackZCbuf(bzh);
302: pos += rlen;
303: }
304: bzh = (struct bpf_zbuf_header *) zbuf->bz_bufb;
305: if (chkZCbuf(bzh)) {
306: rlen = MIN(atomic_load_acq_int(&bzh->bzh_kernel_len), buflen);
307: memcpy(buf + pos, zbuf->bz_bufb + sizeof(struct bpf_zbuf_header), rlen);
308: ackZCbuf(bzh);
309: pos += rlen;
310: }
311:
312: if (!pos) {
313: if ((rlen = ioctl(eth, BIOCROTZBUF, &bz)) == -1)
314: LOGERR;
315: } else
316: rlen = pos;
317: return rlen;
318: }
319: #endif
320:
321: /*
322: * io_etherRecv() - Receive packet from bpf
323: *
324: * @eth = bpf handle
325: * @buf = buffer
326: * @buflen = buffer length
327: * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
328: * return: -1 error or !=-1 readed bytes
329: */
330: ssize_t
331: io_etherRecv(int eth, void * __restrict buf, size_t buflen, void * __restrict zcbuf)
332: {
333: ssize_t rlen = 0;
1.3 misho 334: struct bpf_hdr *h;
1.2 misho 335:
336: if (!buf || !buflen) {
337: io_SetErr(EINVAL, "invalid arguments");
338: return -1;
339: }
340:
341: if (!zcbuf) {
342: rlen = read(eth, buf, buflen);
1.3.8.2 misho 343: if (rlen == -1) {
1.2 misho 344: LOGERR;
1.3.8.2 misho 345: return -1;
346: }
1.2 misho 347: } else {
348: #ifdef __FreeBSD__
349: rlen = nextZCbuf(eth, (struct bpf_zbuf*) zcbuf, buf, buflen);
350: if (!rlen)
351: rlen = nextZCbuf(eth, (struct bpf_zbuf*) zcbuf, buf, buflen);
352: #else
353: rlen = -1;
354: io_SetErr(ENOTSUP, "bpf zero copy buffer mode is not supported");
355: #endif
356: }
357:
1.3 misho 358: h = (struct bpf_hdr*) buf;
359: rlen -= h->bh_hdrlen;
360:
361: if (h->bh_caplen != rlen) {
362: if (h->bh_caplen < rlen)
363: rlen = h->bh_caplen;
364: else {
365: io_SetErr(EIO, "Captured %d bytes should be at most %d bytes",
366: h->bh_caplen, rlen);
367: return -1;
368: }
369: }
370:
371: memmove(buf, buf + h->bh_hdrlen, rlen);
1.2 misho 372: return rlen;
373: }
1.3.8.1 misho 374:
375: /*
376: * io_etherFilter() - BPF filter routine
377: *
378: * @eth = bpf handle
379: * @io = filter direction
380: * (IO_ETHER_FILTER_PROMISC|IO_ETHER_FILTER_NOTREAD|IO_ETHER_FILTER_READ|IO_ETHER_FILTER_WRITE)
381: * @insn = BPF filter instruction array
382: * @insnlen = Length of BPF filter instruction array
383: * return: -1 error or 0 ok
384: */
385: int
386: io_etherFilter(int eth, int io, struct bpf_insn * __restrict insn, size_t insnlen)
387: {
388: int ret = 0;
389: struct bpf_program fcode = { 0 };
390:
391: if (io != IO_ETHER_FILTER_PROMISC && (!insn || !insnlen)) {
392: io_SetErr(EINVAL, "invalid arguments");
393: return -1;
394: }
395:
396: switch (io) {
397: case IO_ETHER_FILTER_PROMISC: /* promiscuous mode */
398: ret = ioctl(eth, BIOCPROMISC, NULL);
399: break;
400: case IO_ETHER_FILTER_NOTREAD: /* read not filter */
401: fcode.bf_len = insnlen / sizeof(struct bpf_insn);
402: fcode.bf_insns = insn;
403: ret = ioctl(eth, BIOCSETFNR, &fcode);
404: break;
405: case IO_ETHER_FILTER_READ: /* read filter */
406: fcode.bf_len = insnlen / sizeof(struct bpf_insn);
407: fcode.bf_insns = insn;
408: ret = ioctl(eth, BIOCSETF, &fcode);
409: break;
410: case IO_ETHER_FILTER_WRITE: /* write filter */
411: fcode.bf_len = insnlen / sizeof(struct bpf_insn);
412: fcode.bf_insns = insn;
413: ret = ioctl(eth, BIOCSETWF, &fcode);
414: break;
415: }
416:
417: if (ret == -1)
418: LOGERR;
419: return ret;
420: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>