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