Annotation of libaitio/src/bpf.c, revision 1.6.6.1
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.6.6.1 ! misho 6: * $Id: bpf.c,v 1.6 2014/02/08 22:06:17 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:
1.6 misho 15: Copyright 2004 - 2014
1.2 misho 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: /*
1.5 misho 50: * io_getmaciface() - Get MAC address from interface name
51: *
52: * @csIface = interface name
53: * @ea = ethernet address
54: * return: -1 error, 0 ok or 1 not found
55: */
56: int
57: io_getmaciface(const char *csIface, ether_addr_t * __restrict ea)
58: {
59: struct ifaddrs *ifa, *ifp;
60: struct sockaddr_dl *dl;
61: int ret = 1;
62:
63: if (!csIface || !ea)
64: return -1;
65: else
66: memset(ea, 0, sizeof(ether_addr_t));
67:
68: getifaddrs(&ifa);
69: for (ifp = ifa; ifp; ifp = ifp->ifa_next)
70: if (!strcmp(csIface, ifp->ifa_name) && ifp->ifa_addr &&
71: ifp->ifa_addr->sa_family == AF_LINK) {
72: dl = (struct sockaddr_dl*) ifp->ifa_addr;
73: if ((dl->sdl_type == IFT_ETHER || dl->sdl_type == IFT_L2VLAN ||
74: dl->sdl_type == IFT_BRIDGE) &&
75: dl->sdl_alen == ETHER_ADDR_LEN) {
76: memcpy(ea, LLADDR(dl), sizeof(ether_addr_t));
77: ret = 0;
78: break;
79: }
80: }
81: freeifaddrs(ifa);
82:
83: return ret;
84: }
85:
86: /*
1.2 misho 87: * io_etherClose() - Close BPF interface
88: *
89: * @eth = bpf handle
90: * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
91: * return: none
92: */
93: void
94: io_etherClose(int eth, void **zcbuf)
95: {
1.6 misho 96: #if defined(__FreeBSD__) && defined(ZCBUF_ENABLE)
1.2 misho 97: struct bpf_zbuf *zbuf = NULL;
98: #endif
99:
100: if (eth > STDERR_FILENO)
101: close(eth);
102:
103: if (zcbuf && *zcbuf) {
1.6 misho 104: #if defined(__FreeBSD__) && defined(ZCBUF_ENABLE)
1.2 misho 105: zbuf = *zcbuf;
106: munmap(zbuf->bz_bufb, zbuf->bz_buflen);
107: munmap(zbuf->bz_bufa, zbuf->bz_buflen);
108: e_free(*zcbuf);
109: *zcbuf = NULL;
110: #endif
111: }
112: }
113:
1.6 misho 114: #if defined(__FreeBSD__) && defined(ZCBUF_ENABLE)
1.2 misho 115: static inline struct bpf_zbuf *
116: allocZCbuf(u_int len)
117: {
118: struct bpf_zbuf *zbuf = NULL;
119:
120: zbuf = e_malloc(sizeof(struct bpf_zbuf));
121: if (!zbuf) {
122: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
123: return NULL;
124: } else
125: memset(zbuf, 0, sizeof(struct bpf_zbuf));
126:
127: zbuf->bz_buflen = roundup(len, getpagesize());
128: zbuf->bz_bufa = mmap(NULL, zbuf->bz_buflen, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
129: if (zbuf->bz_bufa == MAP_FAILED) {
130: LOGERR;
131: e_free(zbuf);
132: return NULL;
133: } else
134: memset(zbuf->bz_bufa, 0, zbuf->bz_buflen);
135: zbuf->bz_bufb = mmap(NULL, zbuf->bz_buflen, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
136: if (zbuf->bz_bufb == MAP_FAILED) {
137: LOGERR;
138: munmap(zbuf->bz_bufa, zbuf->bz_buflen);
139: e_free(zbuf);
140: return NULL;
141: } else
142: memset(zbuf->bz_bufb, 0, zbuf->bz_buflen);
143:
144: return zbuf;
145: }
146: #endif
147:
148: /*
149: * io_etherOpen() - Open BPF interface to device
150: *
151: * @csIface = interface name
152: * @flags = open flags
153: * @whdr = with complete headers
1.3 misho 154: * @wdlt = with data link type
1.2 misho 155: * @buflen = buffer length
156: * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
157: * return: -1 error or >-1 bpf handle
158: */
159: int
1.6 misho 160: io_etherOpen(const char *csIface, int flags, u_int whdr, u_int wdlt,
1.3 misho 161: u_int *buflen, void **zcbuf)
1.2 misho 162: {
163: int eth = -1;
164: register int i;
165: char szStr[STRSIZ];
166: struct ifreq ifr;
167: u_int n = 1;
168:
1.6 misho 169: #if !defined(__FreeBSD__) || !defined(ZCBUF_ENABLE)
1.2 misho 170: if (zcbuf) {
171: io_SetErr(ENOTSUP, "bpf zero copy buffer mode is not supported");
172: return -1;
173: }
174: #endif
175:
176: for (i = 0; i < BPF_DEV_MAX; i++) {
177: memset(szStr, 0, sizeof szStr);
178: snprintf(szStr, sizeof szStr, "/dev/bpf%d", i);
179: eth = open(szStr, flags);
180: if (eth > STDERR_FILENO)
181: break;
182: }
183: if (eth < 3) {
184: LOGERR;
185: return -1;
186: }
187:
1.4 misho 188: if (csIface)
189: strlcpy(szStr, csIface, sizeof szStr);
190: else if (io_get1stiface(szStr, sizeof szStr) == -1) {
191: close(eth);
192: return -1;
193: }
194:
195: n = 1;
196: if (whdr && ioctl(eth, BIOCSHDRCMPLT, &n) == -1) {
197: LOGERR;
198: close(eth);
199: return -1;
200: }
201: if (ioctl(eth, BIOCIMMEDIATE, &n) == -1) {
202: LOGERR;
203: close(eth);
204: return -1;
205: }
206:
1.2 misho 207: if (!zcbuf) {
208: if (ioctl(eth, BIOCGBLEN, &n) == -1) {
209: LOGERR;
210: close(eth);
211: return -1;
212: } else
1.4 misho 213: n = (buflen && *buflen) ? *buflen : getpagesize();
214:
1.2 misho 215: if (ioctl(eth, BIOCSBLEN, &n) == -1) {
216: LOGERR;
217: close(eth);
218: return -1;
219: }
1.4 misho 220: if (buflen)
1.2 misho 221: *buflen = n;
222: } else {
1.6 misho 223: #if defined(__FreeBSD__) && defined(ZCBUF_ENABLE)
1.2 misho 224: n = BPF_BUFMODE_ZBUF;
225: if (ioctl(eth, BIOCSETBUFMODE, &n) == -1) {
226: LOGERR;
227: close(eth);
228: return -1;
229: }
230: if (ioctl(eth, BIOCGETZMAX, &n) == -1) {
231: LOGERR;
232: close(eth);
233: return -1;
234: } else
235: n = (buflen && *buflen) ? *buflen : n;
236: if (!(*zcbuf = allocZCbuf(n))) {
237: close(eth);
238: return -1;
239: }
240: if (ioctl(eth, BIOCSETZBUF, *zcbuf) == -1) {
241: LOGERR;
242: io_etherClose(eth, zcbuf);
243: return -1;
244: }
245: if (buflen && *buflen)
246: *buflen = n;
247: #endif
248: }
249:
250: memset(&ifr, 0, sizeof ifr);
251: strlcpy(ifr.ifr_name, szStr, sizeof ifr.ifr_name);
252: if (ioctl(eth, BIOCSETIF, &ifr) == -1) {
253: LOGERR;
254: io_etherClose(eth, zcbuf);
255: return -1;
256: }
257:
1.6 misho 258: n = wdlt;
259: if (wdlt && ioctl(eth, BIOCSDLT, &n) == -1) {
260: LOGERR;
261: close(eth);
262: return -1;
263: }
264:
1.2 misho 265: return eth;
266: }
267:
268: /*
269: * io_etherSend() - Send packet to bpf
270: *
271: * @eth = bpf handle
272: * @buf = buffer
273: * @buflen = buffer length
274: * return: -1 error or !=-1 written bytes
275: */
276: ssize_t
277: io_etherSend(int eth, const void *buf, size_t buflen)
278: {
279: ssize_t wlen = 0;
280:
281: if (!buf || !buflen) {
282: io_SetErr(EINVAL, "invalid arguments");
283: return -1;
284: }
285:
286: wlen = write(eth, buf, buflen);
287: if (wlen == -1)
288: LOGERR;
289: return wlen;
290: }
291:
1.6 misho 292: #if defined(__FreeBSD__) && defined(ZCBUF_ENABLE)
1.2 misho 293: static inline void
294: ackZCbuf(struct bpf_zbuf_header *bzh)
295: {
296: atomic_store_rel_int(&bzh->bzh_user_gen, bzh->bzh_kernel_gen);
297: }
298:
299: static inline int
300: chkZCbuf(struct bpf_zbuf_header *bzh)
301: {
302: /* return true if userspace owns buffer, and false otherwise. */
303: return (bzh->bzh_user_gen != atomic_load_acq_int(&bzh->bzh_kernel_gen));
304: }
305:
306: static ssize_t
307: nextZCbuf(int eth, struct bpf_zbuf * __restrict zbuf, void * __restrict buf, size_t buflen)
308: {
309: ssize_t rlen = 0;
310: struct bpf_zbuf bz;
311: struct bpf_zbuf_header *bzh;
312: off_t pos = 0;
313:
314: bzh = (struct bpf_zbuf_header *) zbuf->bz_bufa;
315: if (chkZCbuf(bzh)) {
316: rlen = MIN(atomic_load_acq_int(&bzh->bzh_kernel_len), buflen);
317: memcpy(buf + pos, zbuf->bz_bufa + sizeof(struct bpf_zbuf_header), rlen);
318: ackZCbuf(bzh);
319: pos += rlen;
320: }
321: bzh = (struct bpf_zbuf_header *) zbuf->bz_bufb;
322: if (chkZCbuf(bzh)) {
323: rlen = MIN(atomic_load_acq_int(&bzh->bzh_kernel_len), buflen);
324: memcpy(buf + pos, zbuf->bz_bufb + sizeof(struct bpf_zbuf_header), rlen);
325: ackZCbuf(bzh);
326: pos += rlen;
327: }
328:
329: if (!pos) {
330: if ((rlen = ioctl(eth, BIOCROTZBUF, &bz)) == -1)
331: LOGERR;
332: } else
333: rlen = pos;
334: return rlen;
335: }
336: #endif
337:
338: /*
339: * io_etherRecv() - Receive packet from bpf
340: *
341: * @eth = bpf handle
342: * @buf = buffer
343: * @buflen = buffer length
344: * @zcbuf = zero copy buffer, if BPF supports it and isn't NULL
345: * return: -1 error or !=-1 readed bytes
346: */
347: ssize_t
348: io_etherRecv(int eth, void * __restrict buf, size_t buflen, void * __restrict zcbuf)
349: {
350: ssize_t rlen = 0;
1.3 misho 351: struct bpf_hdr *h;
1.2 misho 352:
353: if (!buf || !buflen) {
354: io_SetErr(EINVAL, "invalid arguments");
355: return -1;
356: }
357:
358: if (!zcbuf) {
359: rlen = read(eth, buf, buflen);
1.4 misho 360: if (rlen == -1) {
1.2 misho 361: LOGERR;
1.4 misho 362: return -1;
363: }
1.2 misho 364: } else {
1.6 misho 365: #if defined(__FreeBSD__) && defined(ZCBUF_ENABLE)
1.2 misho 366: rlen = nextZCbuf(eth, (struct bpf_zbuf*) zcbuf, buf, buflen);
367: if (!rlen)
368: rlen = nextZCbuf(eth, (struct bpf_zbuf*) zcbuf, buf, buflen);
369: #else
370: rlen = -1;
371: io_SetErr(ENOTSUP, "bpf zero copy buffer mode is not supported");
372: #endif
373: }
374:
1.3 misho 375: h = (struct bpf_hdr*) buf;
376: rlen -= h->bh_hdrlen;
377:
378: if (h->bh_caplen != rlen) {
379: if (h->bh_caplen < rlen)
380: rlen = h->bh_caplen;
381: else {
382: io_SetErr(EIO, "Captured %d bytes should be at most %d bytes",
383: h->bh_caplen, rlen);
384: return -1;
385: }
386: }
387:
388: memmove(buf, buf + h->bh_hdrlen, rlen);
1.2 misho 389: return rlen;
390: }
1.4 misho 391:
392: /*
393: * io_etherFilter() - BPF filter routine
394: *
395: * @eth = bpf handle
396: * @io = filter direction
397: * (IO_ETHER_FILTER_PROMISC|IO_ETHER_FILTER_NOTREAD|IO_ETHER_FILTER_READ|IO_ETHER_FILTER_WRITE)
398: * @insn = BPF filter instruction array
399: * @insnlen = Length of BPF filter instruction array
400: * return: -1 error or 0 ok
401: */
402: int
403: io_etherFilter(int eth, int io, struct bpf_insn * __restrict insn, size_t insnlen)
404: {
405: int ret = 0;
406: struct bpf_program fcode = { 0 };
407:
408: if (io != IO_ETHER_FILTER_PROMISC && (!insn || !insnlen)) {
409: io_SetErr(EINVAL, "invalid arguments");
410: return -1;
411: }
412:
413: switch (io) {
414: case IO_ETHER_FILTER_PROMISC: /* promiscuous mode */
415: ret = ioctl(eth, BIOCPROMISC, NULL);
416: break;
417: case IO_ETHER_FILTER_NOTREAD: /* read not filter */
418: fcode.bf_len = insnlen / sizeof(struct bpf_insn);
419: fcode.bf_insns = insn;
420: ret = ioctl(eth, BIOCSETFNR, &fcode);
421: break;
422: case IO_ETHER_FILTER_READ: /* read filter */
423: fcode.bf_len = insnlen / sizeof(struct bpf_insn);
424: fcode.bf_insns = insn;
425: ret = ioctl(eth, BIOCSETF, &fcode);
426: break;
427: case IO_ETHER_FILTER_WRITE: /* write filter */
428: fcode.bf_len = insnlen / sizeof(struct bpf_insn);
429: fcode.bf_insns = insn;
430: ret = ioctl(eth, BIOCSETWF, &fcode);
431: break;
432: }
433:
434: if (ret == -1)
435: LOGERR;
436: return ret;
437: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>