[efi] Do not raise TPL within EFI_DRIVER_BINDING_PROTOCOL.Supported()
[ipxe.git] / src / util / hijack.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <stdarg.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <libgen.h>
9 #include <signal.h>
10 #include <net/if.h>
11 #include <net/ethernet.h>
12 #include <sys/select.h>
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <sys/un.h>
16 #include <syslog.h>
17 #include <getopt.h>
18 #include <pcap.h>
19
20 #define SNAPLEN 1600
21
22 /*
23 * FIXME: is there a way to detect the version of the libpcap library?
24 * Version 0.9 has pcap_inject; version 0.8 doesn't, but both report
25 * their version number as 2.4.
26 */
27 #define HAVE_PCAP_INJECT 0
28
29 struct hijack {
30 pcap_t *pcap;
31 int fd;
32 int datalink;
33 int filtered;
34 unsigned long rx_count;
35 unsigned long tx_count;
36 };
37
38 struct hijack_listener {
39 struct sockaddr_un sun;
40 int fd;
41 };
42
43 struct hijack_options {
44 char interface[IF_NAMESIZE];
45 int daemonise;
46 };
47
48 static int daemonised = 0;
49
50 static int signalled = 0;
51
52 static void flag_signalled ( int signal __attribute__ (( unused )) ) {
53 signalled = 1;
54 }
55
56 #if ! HAVE_PCAP_INJECT
57 /**
58 * Substitute for pcap_inject(), if this version of libpcap doesn't
59 * have it. Will almost certainly only work under Linux.
60 *
61 */
62 int pcap_inject ( pcap_t *pcap, const void *data, size_t len ) {
63 int fd;
64 char *errbuf = pcap_geterr ( pcap );
65
66 fd = pcap_get_selectable_fd ( pcap );
67 if ( fd < 0 ) {
68 snprintf ( errbuf, PCAP_ERRBUF_SIZE,
69 "could not get file descriptor" );
70 return -1;
71 }
72 if ( write ( fd, data, len ) != len ) {
73 snprintf ( errbuf, PCAP_ERRBUF_SIZE,
74 "could not write data: %s", strerror ( errno ) );
75 return -1;
76 }
77 return len;
78 }
79 #endif /* ! HAVE_PCAP_INJECT */
80
81 /**
82 * Log error message
83 *
84 */
85 static __attribute__ (( format ( printf, 2, 3 ) )) void
86 logmsg ( int level, const char *format, ... ) {
87 va_list ap;
88
89 va_start ( ap, format );
90 if ( daemonised ) {
91 vsyslog ( ( LOG_DAEMON | level ), format, ap );
92 } else {
93 vfprintf ( stderr, format, ap );
94 }
95 va_end ( ap );
96 }
97
98 /**
99 * Open pcap device
100 *
101 */
102 static int hijack_open ( const char *interface, struct hijack *hijack ) {
103 char errbuf[PCAP_ERRBUF_SIZE];
104
105 /* Open interface via pcap */
106 errbuf[0] = '\0';
107 hijack->pcap = pcap_open_live ( interface, SNAPLEN, 1, 0, errbuf );
108 if ( ! hijack->pcap ) {
109 logmsg ( LOG_ERR, "Failed to open %s: %s\n",
110 interface, errbuf );
111 goto err;
112 }
113 if ( errbuf[0] )
114 logmsg ( LOG_WARNING, "Warning: %s\n", errbuf );
115
116 /* Set capture interface to non-blocking mode */
117 if ( pcap_setnonblock ( hijack->pcap, 1, errbuf ) < 0 ) {
118 logmsg ( LOG_ERR, "Could not make %s non-blocking: %s\n",
119 interface, errbuf );
120 goto err;
121 }
122
123 /* Get file descriptor for select() */
124 hijack->fd = pcap_get_selectable_fd ( hijack->pcap );
125 if ( hijack->fd < 0 ) {
126 logmsg ( LOG_ERR, "Cannot get selectable file descriptor "
127 "for %s\n", interface );
128 goto err;
129 }
130
131 /* Get link layer type */
132 hijack->datalink = pcap_datalink ( hijack->pcap );
133
134 return 0;
135
136 err:
137 if ( hijack->pcap )
138 pcap_close ( hijack->pcap );
139 return -1;
140 }
141
142 /**
143 * Close pcap device
144 *
145 */
146 static void hijack_close ( struct hijack *hijack ) {
147 pcap_close ( hijack->pcap );
148 }
149
150 /**
151 * Install filter for hijacked connection
152 *
153 */
154 static int hijack_install_filter ( struct hijack *hijack,
155 char *filter ) {
156 struct bpf_program program;
157
158 /* Compile filter */
159 if ( pcap_compile ( hijack->pcap, &program, filter, 1, 0 ) < 0 ) {
160 logmsg ( LOG_ERR, "could not compile filter \"%s\": %s\n",
161 filter, pcap_geterr ( hijack->pcap ) );
162 goto err_nofree;
163 }
164
165 /* Install filter */
166 if ( pcap_setfilter ( hijack->pcap, &program ) < 0 ) {
167 logmsg ( LOG_ERR, "could not install filter \"%s\": %s\n",
168 filter, pcap_geterr ( hijack->pcap ) );
169 goto err;
170 }
171
172 logmsg ( LOG_INFO, "using filter \"%s\"\n", filter );
173
174 pcap_freecode ( &program );
175 return 0;
176
177 err:
178 pcap_freecode ( &program );
179 err_nofree:
180 return -1;
181 }
182
183 /**
184 * Set up filter for hijacked ethernet connection
185 *
186 */
187 static int hijack_filter_ethernet ( struct hijack *hijack, const char *buf,
188 size_t len ) {
189 char filter[55]; /* see format string */
190 struct ether_header *ether_header = ( struct ether_header * ) buf;
191 unsigned char *hwaddr = ether_header->ether_shost;
192
193 if ( len < sizeof ( *ether_header ) )
194 return -1;
195
196 snprintf ( filter, sizeof ( filter ), "broadcast or multicast or "
197 "ether host %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0],
198 hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
199
200 return hijack_install_filter ( hijack, filter );
201 }
202
203 /**
204 * Set up filter for hijacked connection
205 *
206 */
207 static int hijack_filter ( struct hijack *hijack, const char *buf,
208 size_t len ) {
209 switch ( hijack->datalink ) {
210 case DLT_EN10MB:
211 return hijack_filter_ethernet ( hijack, buf, len );
212 default:
213 logmsg ( LOG_ERR, "unsupported protocol %s: cannot filter\n",
214 ( pcap_datalink_val_to_name ( hijack->datalink ) ?
215 pcap_datalink_val_to_name ( hijack->datalink ) :
216 "UNKNOWN" ) );
217 /* Return success so we don't get called again */
218 return 0;
219 }
220 }
221
222 /**
223 * Forward data from hijacker
224 *
225 */
226 static ssize_t forward_from_hijacker ( struct hijack *hijack, int fd ) {
227 char buf[SNAPLEN];
228 ssize_t len;
229
230 /* Read packet from hijacker */
231 len = read ( fd, buf, sizeof ( buf ) );
232 if ( len < 0 ) {
233 logmsg ( LOG_ERR, "read from hijacker failed: %s\n",
234 strerror ( errno ) );
235 return -1;
236 }
237 if ( len == 0 )
238 return 0;
239
240 /* Set up filter if not already in place */
241 if ( ! hijack->filtered ) {
242 if ( hijack_filter ( hijack, buf, len ) == 0 )
243 hijack->filtered = 1;
244 }
245
246 /* Transmit packet to network */
247 if ( pcap_inject ( hijack->pcap, buf, len ) != len ) {
248 logmsg ( LOG_ERR, "write to hijacked port failed: %s\n",
249 pcap_geterr ( hijack->pcap ) );
250 return -1;
251 }
252
253 hijack->tx_count++;
254 return len;
255 };
256
257 /**
258 * Forward data to hijacker
259 *
260 */
261 static ssize_t forward_to_hijacker ( int fd, struct hijack *hijack ) {
262 struct pcap_pkthdr *pkt_header;
263 const unsigned char *pkt_data;
264 ssize_t len;
265
266 /* Receive packet from network */
267 if ( pcap_next_ex ( hijack->pcap, &pkt_header, &pkt_data ) < 0 ) {
268 logmsg ( LOG_ERR, "read from hijacked port failed: %s\n",
269 pcap_geterr ( hijack->pcap ) );
270 return -1;
271 }
272 if ( pkt_header->caplen != pkt_header->len ) {
273 logmsg ( LOG_ERR, "read partial packet (%d of %d bytes)\n",
274 pkt_header->caplen, pkt_header->len );
275 return -1;
276 }
277 if ( pkt_header->caplen == 0 )
278 return 0;
279 len = pkt_header->caplen;
280
281 /* Write packet to hijacker */
282 if ( write ( fd, pkt_data, len ) != len ) {
283 logmsg ( LOG_ERR, "write to hijacker failed: %s\n",
284 strerror ( errno ) );
285 return -1;
286 }
287
288 hijack->rx_count++;
289 return len;
290 };
291
292
293 /**
294 * Run hijacker
295 *
296 */
297 static int run_hijacker ( const char *interface, int fd ) {
298 struct hijack hijack;
299 fd_set fdset;
300 int max_fd;
301 ssize_t len;
302
303 logmsg ( LOG_INFO, "new connection for %s\n", interface );
304
305 /* Open connection to network */
306 memset ( &hijack, 0, sizeof ( hijack ) );
307 if ( hijack_open ( interface, &hijack ) < 0 )
308 goto err;
309
310 /* Do the forwarding */
311 max_fd = ( ( fd > hijack.fd ) ? fd : hijack.fd );
312 while ( 1 ) {
313 /* Wait for available data */
314 FD_ZERO ( &fdset );
315 FD_SET ( fd, &fdset );
316 FD_SET ( hijack.fd, &fdset );
317 if ( select ( ( max_fd + 1 ), &fdset, NULL, NULL, 0 ) < 0 ) {
318 logmsg ( LOG_ERR, "select failed: %s\n",
319 strerror ( errno ) );
320 goto err;
321 }
322 if ( FD_ISSET ( fd, &fdset ) ) {
323 len = forward_from_hijacker ( &hijack, fd );
324 if ( len < 0 )
325 goto err;
326 if ( len == 0 )
327 break;
328 }
329 if ( FD_ISSET ( hijack.fd, &fdset ) ) {
330 len = forward_to_hijacker ( fd, &hijack );
331 if ( len < 0 )
332 goto err;
333 if ( len == 0 )
334 break;
335 }
336 }
337
338 hijack_close ( &hijack );
339 logmsg ( LOG_INFO, "closed connection for %s\n", interface );
340 logmsg ( LOG_INFO, "received %ld packets, sent %ld packets\n",
341 hijack.rx_count, hijack.tx_count );
342
343 return 0;
344
345 err:
346 if ( hijack.pcap )
347 hijack_close ( &hijack );
348 return -1;
349 }
350
351 /**
352 * Open listener socket
353 *
354 */
355 static int open_listener ( const char *interface,
356 struct hijack_listener *listener ) {
357
358 /* Create socket */
359 listener->fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 );
360 if ( listener->fd < 0 ) {
361 logmsg ( LOG_ERR, "Could not create socket: %s\n",
362 strerror ( errno ) );
363 goto err;
364 }
365
366 /* Bind to local filename */
367 listener->sun.sun_family = AF_UNIX,
368 snprintf ( listener->sun.sun_path, sizeof ( listener->sun.sun_path ),
369 "/var/run/hijack-%s", interface );
370 if ( bind ( listener->fd, ( struct sockaddr * ) &listener->sun,
371 sizeof ( listener->sun ) ) < 0 ) {
372 logmsg ( LOG_ERR, "Could not bind socket to %s: %s\n",
373 listener->sun.sun_path, strerror ( errno ) );
374 goto err;
375 }
376
377 /* Set as a listening socket */
378 if ( listen ( listener->fd, 0 ) < 0 ) {
379 logmsg ( LOG_ERR, "Could not listen to %s: %s\n",
380 listener->sun.sun_path, strerror ( errno ) );
381 goto err;
382 }
383
384 return 0;
385
386 err:
387 if ( listener->fd >= 0 )
388 close ( listener->fd );
389 return -1;
390 }
391
392 /**
393 * Listen on listener socket
394 *
395 */
396 static int listen_for_hijackers ( struct hijack_listener *listener,
397 const char *interface ) {
398 int fd;
399 pid_t child;
400 int rc;
401
402 logmsg ( LOG_INFO, "Listening on %s\n", listener->sun.sun_path );
403
404 while ( ! signalled ) {
405 /* Accept new connection, interruptibly */
406 siginterrupt ( SIGINT, 1 );
407 siginterrupt ( SIGHUP, 1 );
408 fd = accept ( listener->fd, NULL, 0 );
409 siginterrupt ( SIGINT, 0 );
410 siginterrupt ( SIGHUP, 0 );
411 if ( fd < 0 ) {
412 if ( errno == EINTR ) {
413 continue;
414 } else {
415 logmsg ( LOG_ERR, "accept failed: %s\n",
416 strerror ( errno ) );
417 goto err;
418 }
419 }
420
421 /* Fork child process */
422 child = fork();
423 if ( child < 0 ) {
424 logmsg ( LOG_ERR, "fork failed: %s\n",
425 strerror ( errno ) );
426 goto err;
427 }
428 if ( child == 0 ) {
429 /* I am the child; run the hijacker */
430 rc = run_hijacker ( interface, fd );
431 close ( fd );
432 exit ( rc );
433 }
434
435 close ( fd );
436 }
437
438 logmsg ( LOG_INFO, "Stopped listening on %s\n",
439 listener->sun.sun_path );
440 return 0;
441
442 err:
443 if ( fd >= 0 )
444 close ( fd );
445 return -1;
446 }
447
448 /**
449 * Close listener socket
450 *
451 */
452 static void close_listener ( struct hijack_listener *listener ) {
453 close ( listener->fd );
454 unlink ( listener->sun.sun_path );
455 }
456
457 /**
458 * Print usage
459 *
460 */
461 static void usage ( char **argv ) {
462 logmsg ( LOG_ERR,
463 "Usage: %s [options]\n"
464 "\n"
465 "Options:\n"
466 " -h|--help Print this help message\n"
467 " -i|--interface intf Use specified network interface\n"
468 " -n|--nodaemon Run in foreground\n",
469 argv[0] );
470 }
471
472 /**
473 * Parse command-line options
474 *
475 */
476 static int parse_options ( int argc, char **argv,
477 struct hijack_options *options ) {
478 static struct option long_options[] = {
479 { "interface", 1, NULL, 'i' },
480 { "nodaemon", 0, NULL, 'n' },
481 { "help", 0, NULL, 'h' },
482 { },
483 };
484 int c;
485
486 /* Set default options */
487 memset ( options, 0, sizeof ( *options ) );
488 strncpy ( options->interface, "eth0", sizeof ( options->interface ) );
489 options->daemonise = 1;
490
491 /* Parse command-line options */
492 while ( 1 ) {
493 int option_index = 0;
494
495 c = getopt_long ( argc, argv, "i:hn", long_options,
496 &option_index );
497 if ( c < 0 )
498 break;
499
500 switch ( c ) {
501 case 'i':
502 strncpy ( options->interface, optarg,
503 sizeof ( options->interface ) );
504 break;
505 case 'n':
506 options->daemonise = 0;
507 break;
508 case 'h':
509 usage( argv );
510 return -1;
511 case '?':
512 /* Unrecognised option */
513 return -1;
514 default:
515 logmsg ( LOG_ERR, "Unrecognised option '-%c'\n", c );
516 return -1;
517 }
518 }
519
520 /* Check there's nothing left over on the command line */
521 if ( optind != argc ) {
522 usage ( argv );
523 return -1;
524 }
525
526 return 0;
527 }
528
529 /**
530 * Daemonise
531 *
532 */
533 static int daemonise ( const char *interface ) {
534 char pidfile[16 + IF_NAMESIZE + 4]; /* "/var/run/hijack-<intf>.pid" */
535 char pid[16];
536 int pidlen;
537 int fd = -1;
538
539 /* Daemonise */
540 if ( daemon ( 0, 0 ) < 0 ) {
541 logmsg ( LOG_ERR, "Could not daemonise: %s\n",
542 strerror ( errno ) );
543 goto err;
544 }
545 daemonised = 1; /* Direct messages to syslog now */
546
547 /* Open pid file */
548 snprintf ( pidfile, sizeof ( pidfile ), "/var/run/hijack-%s.pid",
549 interface );
550 fd = open ( pidfile, ( O_WRONLY | O_CREAT | O_TRUNC ),
551 ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) );
552 if ( fd < 0 ) {
553 logmsg ( LOG_ERR, "Could not open %s for writing: %s\n",
554 pidfile, strerror ( errno ) );
555 goto err;
556 }
557
558 /* Write pid to file */
559 pidlen = snprintf ( pid, sizeof ( pid ), "%d\n", getpid() );
560 if ( write ( fd, pid, pidlen ) != pidlen ) {
561 logmsg ( LOG_ERR, "Could not write %s: %s\n",
562 pidfile, strerror ( errno ) );
563 goto err;
564 }
565
566 close ( fd );
567 return 0;
568
569 err:
570 if ( fd >= 0 )
571 close ( fd );
572 return -1;
573 }
574
575 int main ( int argc, char **argv ) {
576 struct hijack_options options;
577 struct hijack_listener listener;
578 struct sigaction sa;
579
580 /* Parse command-line options */
581 if ( parse_options ( argc, argv, &options ) < 0 )
582 exit ( 1 );
583
584 /* Set up syslog connection */
585 openlog ( basename ( argv[0] ), LOG_PID, LOG_DAEMON );
586
587 /* Set up listening socket */
588 if ( open_listener ( options.interface, &listener ) < 0 )
589 exit ( 1 );
590
591 /* Daemonise on demand */
592 if ( options.daemonise ) {
593 if ( daemonise ( options.interface ) < 0 )
594 exit ( 1 );
595 }
596
597 /* Avoid creating zombies */
598 memset ( &sa, 0, sizeof ( sa ) );
599 sa.sa_handler = SIG_IGN;
600 sa.sa_flags = SA_RESTART | SA_NOCLDWAIT;
601 if ( sigaction ( SIGCHLD, &sa, NULL ) < 0 ) {
602 logmsg ( LOG_ERR, "Could not set SIGCHLD handler: %s",
603 strerror ( errno ) );
604 exit ( 1 );
605 }
606
607 /* Set 'signalled' flag on SIGINT or SIGHUP */
608 sa.sa_handler = flag_signalled;
609 sa.sa_flags = SA_RESTART | SA_RESETHAND;
610 if ( sigaction ( SIGINT, &sa, NULL ) < 0 ) {
611 logmsg ( LOG_ERR, "Could not set SIGINT handler: %s",
612 strerror ( errno ) );
613 exit ( 1 );
614 }
615 if ( sigaction ( SIGHUP, &sa, NULL ) < 0 ) {
616 logmsg ( LOG_ERR, "Could not set SIGHUP handler: %s",
617 strerror ( errno ) );
618 exit ( 1 );
619 }
620
621 /* Listen for hijackers */
622 if ( listen_for_hijackers ( &listener, options.interface ) < 0 )
623 exit ( 1 );
624
625 close_listener ( &listener );
626
627 return 0;
628 }