1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "lldpd.h"
19 #include <unistd.h>
20 #include <errno.h>
21 #include <net/bpf.h>
22
23 struct bpf_buffer {
24 size_t len; /* Total length of the buffer */
25 struct bpf_hdr data[0];
26 };
27
28 int
ifbpf_phys_init(struct lldpd * cfg,struct lldpd_hardware * hardware)29 ifbpf_phys_init(struct lldpd *cfg,
30 struct lldpd_hardware *hardware)
31 {
32 struct bpf_buffer *buffer = NULL;
33 int fd = -1;
34
35 log_debug("interfaces", "initialize ethernet device %s",
36 hardware->h_ifname);
37 if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
38 return -1;
39
40 /* Allocate receive buffer */
41 hardware->h_data = buffer =
42 malloc(ETHER_MAX_LEN + BPF_WORDALIGN(sizeof(struct bpf_hdr)) + sizeof(struct bpf_buffer));
43 if (buffer == NULL) {
44 log_warn("interfaces",
45 "unable to allocate buffer space for BPF on %s",
46 hardware->h_ifname);
47 goto end;
48 }
49 buffer->len = ETHER_MAX_LEN + BPF_WORDALIGN(sizeof(struct bpf_hdr));
50
51 /* Setup multicast */
52 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
53
54 hardware->h_sendfd = fd; /* Send */
55
56 levent_hardware_add_fd(hardware, fd); /* Receive */
57 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname,
58 fd);
59 return 0;
60
61 end:
62 if (fd >= 0) close(fd);
63 free(buffer);
64 hardware->h_data = NULL;
65 return -1;
66 }
67
68 /* Ethernet send/receive through BPF */
69 static int
ifbpf_eth_send(struct lldpd * cfg,struct lldpd_hardware * hardware,char * buffer,size_t size)70 ifbpf_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
71 char *buffer, size_t size)
72 {
73 log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
74 hardware->h_ifname, hardware->h_sendfd);
75 return write(hardware->h_sendfd,
76 buffer, size);
77 }
78
79 static int
ifbpf_eth_recv(struct lldpd * cfg,struct lldpd_hardware * hardware,int fd,char * buffer,size_t size)80 ifbpf_eth_recv(struct lldpd *cfg,
81 struct lldpd_hardware *hardware,
82 int fd, char *buffer, size_t size)
83 {
84 struct bpf_buffer *bpfbuf = hardware->h_data;
85 struct bpf_hdr *bh;
86 log_debug("interfaces", "receive PDU from ethernet device %s",
87 hardware->h_ifname);
88
89 /* We assume we have only receive one packet (unbuffered mode). Dunno if
90 * this is correct. */
91 if (read(fd, bpfbuf->data, bpfbuf->len) == -1) {
92 if (errno == ENETDOWN) {
93 log_debug("interfaces", "error while receiving frame on %s (network down)",
94 hardware->h_ifname);
95 } else {
96 log_warn("interfaces", "error while receiving frame on %s",
97 hardware->h_ifname);
98 hardware->h_rx_discarded_cnt++;
99 }
100 return -1;
101 }
102 bh = (struct bpf_hdr*)bpfbuf->data;
103 if (bh->bh_caplen < size)
104 size = bh->bh_caplen;
105 memcpy(buffer, (char *)bpfbuf->data + bh->bh_hdrlen, size);
106
107 return size;
108 }
109
110 static int
ifbpf_eth_close(struct lldpd * cfg,struct lldpd_hardware * hardware)111 ifbpf_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
112 {
113 log_debug("interfaces", "close ethernet device %s",
114 hardware->h_ifname);
115 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
116 return 0;
117 }
118
119 struct lldpd_ops bpf_ops = {
120 .send = ifbpf_eth_send,
121 .recv = ifbpf_eth_recv,
122 .cleanup = ifbpf_eth_close,
123 };
124