]>
Commit | Line | Data |
---|---|---|
c3e340b6 VB |
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> | |
92a6138d | 20 | #include <errno.h> |
c3e340b6 VB |
21 | #include <net/bpf.h> |
22 | ||
23 | struct bpf_buffer { | |
8b549648 | 24 | size_t len; /* Total length of the buffer */ |
eeb66a08 | 25 | struct bpf_hdr data[0]; |
c3e340b6 VB |
26 | }; |
27 | ||
28 | int | |
8b549648 | 29 | ifbpf_phys_init(struct lldpd *cfg, struct lldpd_hardware *hardware) |
c3e340b6 VB |
30 | { |
31 | struct bpf_buffer *buffer = NULL; | |
32 | int fd = -1; | |
33 | ||
8b549648 | 34 | log_debug("interfaces", "initialize ethernet device %s", hardware->h_ifname); |
8b50be7f | 35 | if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1) |
c3e340b6 VB |
36 | return -1; |
37 | ||
38 | /* Allocate receive buffer */ | |
8b549648 VB |
39 | hardware->h_data = buffer = malloc(ETHER_MAX_LEN + |
40 | BPF_WORDALIGN(sizeof(struct bpf_hdr)) + sizeof(struct bpf_buffer)); | |
c3e340b6 | 41 | if (buffer == NULL) { |
8b549648 | 42 | log_warn("interfaces", "unable to allocate buffer space for BPF on %s", |
c3e340b6 VB |
43 | hardware->h_ifname); |
44 | goto end; | |
45 | } | |
7b4eb194 | 46 | buffer->len = ETHER_MAX_LEN + BPF_WORDALIGN(sizeof(struct bpf_hdr)); |
c3e340b6 VB |
47 | |
48 | /* Setup multicast */ | |
49 | interfaces_setup_multicast(cfg, hardware->h_ifname, 0); | |
50 | ||
51 | hardware->h_sendfd = fd; /* Send */ | |
52 | ||
53 | levent_hardware_add_fd(hardware, fd); /* Receive */ | |
54 | log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname, | |
55 | fd); | |
56 | return 0; | |
57 | ||
58 | end: | |
59 | if (fd >= 0) close(fd); | |
60 | free(buffer); | |
61 | hardware->h_data = NULL; | |
62 | return -1; | |
63 | } | |
64 | ||
65 | /* Ethernet send/receive through BPF */ | |
66 | static int | |
8b549648 VB |
67 | ifbpf_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, char *buffer, |
68 | size_t size) | |
c3e340b6 VB |
69 | { |
70 | log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)", | |
71 | hardware->h_ifname, hardware->h_sendfd); | |
8b549648 | 72 | return write(hardware->h_sendfd, buffer, size); |
c3e340b6 VB |
73 | } |
74 | ||
75 | static int | |
8b549648 VB |
76 | ifbpf_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd, char *buffer, |
77 | size_t size) | |
c3e340b6 VB |
78 | { |
79 | struct bpf_buffer *bpfbuf = hardware->h_data; | |
80 | struct bpf_hdr *bh; | |
81 | log_debug("interfaces", "receive PDU from ethernet device %s", | |
82 | hardware->h_ifname); | |
83 | ||
84 | /* We assume we have only receive one packet (unbuffered mode). Dunno if | |
85 | * this is correct. */ | |
86 | if (read(fd, bpfbuf->data, bpfbuf->len) == -1) { | |
afd49b83 | 87 | if (errno == ENETDOWN) { |
8b549648 VB |
88 | log_debug("interfaces", |
89 | "error while receiving frame on %s (network down)", | |
afd49b83 VB |
90 | hardware->h_ifname); |
91 | } else { | |
92 | log_warn("interfaces", "error while receiving frame on %s", | |
93 | hardware->h_ifname); | |
94 | hardware->h_rx_discarded_cnt++; | |
95 | } | |
c3e340b6 VB |
96 | return -1; |
97 | } | |
8b549648 VB |
98 | bh = (struct bpf_hdr *)bpfbuf->data; |
99 | if (bh->bh_caplen < size) size = bh->bh_caplen; | |
eeb66a08 | 100 | memcpy(buffer, (char *)bpfbuf->data + bh->bh_hdrlen, size); |
c3e340b6 VB |
101 | |
102 | return size; | |
103 | } | |
104 | ||
105 | static int | |
106 | ifbpf_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware) | |
107 | { | |
8b549648 | 108 | log_debug("interfaces", "close ethernet device %s", hardware->h_ifname); |
c3e340b6 VB |
109 | interfaces_setup_multicast(cfg, hardware->h_ifname, 1); |
110 | return 0; | |
111 | } | |
112 | ||
113 | struct lldpd_ops bpf_ops = { | |
114 | .send = ifbpf_eth_send, | |
115 | .recv = ifbpf_eth_recv, | |
116 | .cleanup = ifbpf_eth_close, | |
117 | }; |