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