1 /** @file lldp_bpf_framer.c
2 *
3 * See LICENSE file for more info.
4 *
5 * Authors: Terry Simons (terry.simons@gmail.com)
6 *
7 **/
8
9 #include <sys/types.h>
10 #include "lldp_bpf_framer.h"
11 #include "lldp_port.h"
12 #include "lldp_debug.h"
13 #include "platform/framehandler.h"
14
15 #include "bpflib.h"
16 #include <fcntl.h>
17
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ifaddrs.h>
25 #include <netdb.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28
29 // BPF includes:
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/ioctl.h>
33 #include <net/bpf.h>
34 #include <sys/socket.h>
35 #include <net/if.h>
36 #ifndef NETBSD
37 #include <net/ethernet.h>
38 #endif // !NETBSD
39 // End BPF includes
40
41 #include <netinet/in.h>
42 #include <net/if_dl.h>
43
44 #define XENOSOCK -1;
45
46 #define TRUE 1
47 #define FALSE 0
48
lldp_write(struct lldp_port * lldp_port)49 ssize_t lldp_write(struct lldp_port *lldp_port) {
50
51 // Write the frame to the wire.
52 return write(lldp_port->socket, lldp_port->tx.frame, lldp_port->tx.sendsize);
53 }
54
lldp_read(struct lldp_port * lldp_port)55 ssize_t lldp_read(struct lldp_port *lldp_port) {
56 // allocate the bpf_buf to recieve as many packets as will fit in lldp_port->mtu
57 // which is the bpf internal buffer size.
58 struct bpf_hdr *bpf_buf = malloc(lldp_port->mtu);
59
60 lldp_port->rx.recvsize = read(lldp_port->socket, bpf_buf, lldp_port->mtu);
61
62 // Allocate the buffer to be the length of the captured packet
63 uint8_t *frame_buffer = malloc(bpf_buf->bh_caplen);
64
65 //XXX: BUG HERE - We could actually have more than one packet in bpf_buf
66 // we should process bpf_buf in a loop until we have processed all
67 // of the packets in the buffer. This would mean changing lldp_port->rx
68 // so that there was a linked list of packets in frame so that the next
69 // code section could process all the packets in the queue.
70 //
71 // However the chance of more than one packet being in the buffer is low
72 // and we can safely drop any other frames as well.
73 if(frame_buffer) {
74 debug_printf(DEBUG_INT, "(%s) Raw BPF Frame with BPF header: \n", lldp_port->if_name);
75 debug_printf(DEBUG_INT, "BPF Header Length: %d\n", bpf_buf->bh_hdrlen);
76 debug_hex_dump(DEBUG_INT, (uint8_t *)bpf_buf, lldp_port->rx.recvsize);
77
78 // Copy the captured data to the buffer, NOTE this may not be the whole packet!!!
79 memcpy(frame_buffer, ((char*) bpf_buf + bpf_buf->bh_hdrlen), bpf_buf->bh_caplen);
80
81 debug_printf(DEBUG_INT, "(%s) Raw BPF Frame without BPF header: \n", lldp_port->if_name);
82 debug_hex_dump(DEBUG_INT, (uint8_t *)frame_buffer, bpf_buf->bh_caplen);
83 // Correct the rx.recvsize to reflect the lenght of the packet without the bpf_hdr
84 lldp_port->rx.recvsize = bpf_buf->bh_caplen;
85
86 // Free the tmp buffer
87 free(bpf_buf);
88
89 // Now free the old buffer
90 free(lldp_port->rx.frame);
91
92 // Now assign the new buffer
93 lldp_port->rx.frame = frame_buffer;
94 } else {
95 debug_printf(DEBUG_NORMAL, "Couldn't malloc! Skipping frame to prevent leak...\n");
96 }
97
98 return(lldp_port->rx.recvsize);
99 }
100
init_multi(const char * ifname,int add)101 int init_multi(const char *ifname, int add)
102 {
103 struct ifreq ifr;
104 int s,n;
105 const char *addr = "01.80.C2.00.00.0E";
106 unsigned int e1,e2,e3,e4,e5,e6;
107 struct sockaddr_dl *dlp = NULL;
108 unsigned char *bp = NULL;
109
110 s = socket(PF_INET, SOCK_DGRAM, 0);
111 if (s < 0) {
112 perror("socket");
113 return -1;
114 }
115
116 if( (n = sscanf( addr, "%x.%x.%x.%x.%x.%x",
117 &e1, &e2, &e3, &e4, &e5, &e6 )) != 6 )
118 {
119 printf( "bad args\n" );
120 return -1;
121 }
122
123 dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
124 dlp->sdl_len = sizeof(struct sockaddr_dl);
125 dlp->sdl_family = AF_LINK;
126 dlp->sdl_index = 0;
127 dlp->sdl_nlen = 0;
128 dlp->sdl_alen = 6;
129 dlp->sdl_slen = 0;
130
131 // NB: This is to silence compiler warnints, but what's goin on here?
132 bp = (unsigned char *)LLADDR(dlp);
133
134 bp[0] = e1;
135 bp[1] = e2;
136 bp[2] = e3;
137 bp[3] = e4;
138 bp[4] = e5;
139 bp[5] = e6;
140
141 if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
142 perror("ioctl[SIOC{ADD/DEL}MULTI]");
143 close(s);
144 return -1;
145 }
146 close(s);
147
148 return 0;
149 }
150
socketInitializeLLDP(struct lldp_port * lldp_port)151 int socketInitializeLLDP(struct lldp_port *lldp_port) {
152 if(lldp_port->if_name == NULL) {
153 debug_printf(DEBUG_NORMAL, "Got NULL interface in %s():%d\n", __FUNCTION__, __LINE__);
154 exit(-1);
155 }
156
157 if(!lldp_port->if_index > 0) {
158 debug_printf(DEBUG_NORMAL, "'%s' does not appear to be a valid interface name!\n", lldp_port->if_name);
159 return XENOSOCK;
160 }
161
162 debug_printf(DEBUG_INT, "'%s' is index %d\n", lldp_port->if_name, lldp_port->if_index);
163
164 lldp_port->socket = bpf_new();
165
166 if(lldp_port->socket < 0) {
167 debug_printf(DEBUG_NORMAL, "[Error] (%d) : %s (%s:%d)\n", errno, strerror(errno), __FUNCTION__, __LINE__);
168 return XENOSOCK;
169 }
170
171
172 // This is necessary for Mac OS X at least...
173 if(bpf_set_immediate(lldp_port->socket, 1) > 0) {
174 debug_printf(DEBUG_NORMAL, "[Error] (%d) : %s (%s:%d)\n", errno, strerror(errno), __FUNCTION__, __LINE__);
175 bpf_dispose(lldp_port->socket);
176 return XENOSOCK;
177 }
178
179 struct bpf_insn progcodes[] = {
180 BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), // inspect ethernet_frame_type
181 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x88CC, 0, 1), // if EAPOL frame, continue with next instruction, else jump
182 BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
183 BPF_STMT(BPF_RET+BPF_K, 0)
184 };
185
186 struct bpf_program prog = {
187 4,
188 (struct bpf_insn*) &progcodes
189 };
190
191 if(ioctl(lldp_port->socket, BIOCSETF, &prog) < 0) {
192 debug_printf(DEBUG_NORMAL, "[Error] (%d) : %s (%s:%d)\n", errno, strerror(errno), __FUNCTION__, __LINE__);
193 bpf_dispose(lldp_port->socket);
194 return XENOSOCK;
195 }
196
197 if(bpf_setif(lldp_port->socket, lldp_port->if_name) < 0) {
198 debug_printf(DEBUG_NORMAL, "[Error] (%d) : %s (%s:%d)\n", errno, strerror(errno), __FUNCTION__, __LINE__);
199 bpf_dispose(lldp_port->socket);
200 return XENOSOCK;
201 }
202
203 // Enable multicast on the interface
204 if(init_multi(lldp_port->if_name, 1) < 0) {
205 // Setting multicast failed, so let's try falling back to promiscuous mode
206 // as a last resort...
207 debug_printf(DEBUG_NORMAL, "Unable to set multicast mode, trying promiscuous... ");
208 if(bpf_set_promiscuous(lldp_port->socket) == 0) {
209 debug_printf(DEBUG_NORMAL, "Success!\n");
210 } else {
211 debug_printf(DEBUG_NORMAL, "Failure!\n");
212 }
213 }
214
215 // Set the socket to be non-blocking
216 if (fcntl(lldp_port->socket, F_SETFL, O_NONBLOCK) > 0)
217 {
218 debug_printf(DEBUG_NORMAL, "[Error] (%d) : %s (%s:%d)\n", errno, strerror(errno), __FUNCTION__, __LINE__);
219 bpf_dispose(lldp_port->socket);
220 return XENOSOCK;
221 }
222
223 // Get the size of the BPF buffer
224 if (bpf_get_blen(lldp_port->socket, &lldp_port->mtu) < 0) {
225 debug_printf(DEBUG_NORMAL, "[Error] (%d) : %s (%s:%d)\n", errno, strerror(errno), __FUNCTION__, __LINE__);
226 bpf_dispose(lldp_port->socket);
227 return XENOSOCK;
228 }
229
230 // Set the following to 1 to enable lldpd to see sent frames.
231 bpf_see_sent(lldp_port->socket, 0);
232
233 _getmac(lldp_port->source_mac, lldp_port->if_name);
234
235 _getip(lldp_port->source_ipaddr, lldp_port->if_name);
236
237 debug_printf(DEBUG_NORMAL, "%s MTU: %d\n", lldp_port->if_name, lldp_port->mtu);
238
239 if(lldp_port->mtu > 0) {
240 lldp_port->rx.frame = malloc(lldp_port->mtu);
241 lldp_port->tx.frame = malloc(lldp_port->mtu);
242 } else {
243 debug_printf(DEBUG_NORMAL, "Frame buffer MTU is 0, ditching interface.\n");
244 bpf_dispose(lldp_port->socket);
245 return XENOSOCK;
246 }
247
248 return 0;
249 }
250
251 /***********************************************
252 * Get the MAC address of an interface
253 ***********************************************/
_getmac(uint8_t * dest,char * ifname)254 int _getmac(uint8_t *dest, char *ifname) {
255
256 struct ifaddrs *ifap;
257
258 memset(dest, 0x0, 6);
259
260 if (getifaddrs(&ifap) == 0) {
261 struct ifaddrs *p;
262 for (p = ifap; p; p = p->ifa_next) {
263 if (p->ifa_addr->sa_family == AF_LINK && strcmp(p->ifa_name, ifname) == 0) {
264 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
265 memcpy(dest, sdp->sdl_data + sdp->sdl_nlen, 6);
266 printf("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
267 p->ifa_name,
268 dest[0],
269 dest[1],
270 dest[2],
271 dest[3],
272 dest[4],
273 dest[5]);
274 freeifaddrs(ifap);
275 return TRUE;
276 }
277 }
278 freeifaddrs(ifap);
279 }
280
281 return FALSE;
282 }
283
284 /***********************************************
285 * Get the IP address of an interface
286 ***********************************************/
_getip(uint8_t * dest,char * ifname)287 int _getip(uint8_t *dest, char *ifname) {
288
289 struct ifaddrs *ifap;
290
291 if (getifaddrs(&ifap) == 0) {
292 struct ifaddrs *p;
293 for (p = ifap; p; p = p->ifa_next) {
294 if (p->ifa_addr->sa_family == AF_INET && strcmp(p->ifa_name, ifname) == 0) {
295 memcpy(dest, &p->ifa_addr->sa_data[2], 4);
296 printf("%s IP: %d.%d.%d.%d\n",
297 p->ifa_name,
298 dest[0],
299 dest[1],
300 dest[2],
301 dest[3]);
302 freeifaddrs(ifap);
303 return TRUE;
304 }
305 }
306 freeifaddrs(ifap);
307 }
308
309 return FALSE;
310 }
311
312 // Dunno if there's a better way to handle this... maybe an OS specific event?
refreshInterfaceData(struct lldp_port * lldp_port)313 void refreshInterfaceData(struct lldp_port *lldp_port) {
314 _getmac(lldp_port->source_mac, lldp_port->if_name);
315
316 _getip(lldp_port->source_ipaddr, lldp_port->if_name);
317 }
318
socketCleanupLLDP(struct lldp_port * lldp_port)319 void socketCleanupLLDP(struct lldp_port *lldp_port)
320 {
321 //init_multi(lldp_port->if_name, 0);
322 bpf_dispose(lldp_port->socket);
323 }
324