2 * Copyright (c) 2007, Swedish Institute of Computer Science.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * This file is part of the Contiki operating system.
31 * Author: Oliver Schmidt <ol.sc@web.de>
33 * $Id: wpcapslip.c,v 1.3 2008/06/19 07:52:28 adamdunkels Exp $
42 #else /* __CYGWIN__ */
44 #endif /* __CYGWIN__ */
51 #include <sys/types.h>
52 #include <sys/select.h>
59 #include <sys/ioctl.h>
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
68 void wpcap_start(char *ethifaddr
, char *netaddr
, char *netmask
, int logging
);
70 void wpcap_send(void *buf
, int len
);
72 uint16_t wpcap_poll(char **buf
);
74 #include "net/tcpdump.h"
75 static int should_print
= 0;
79 /*---------------------------------------------------------------------------*/
81 print_packet(uint8_t *packet
, int len
)
85 tcpdump_format(packet
, len
, buf
, sizeof(buf
));
89 /*---------------------------------------------------------------------------*/
91 /*---------------------------------------------------------------------------*/
95 fprintf(stderr
, "signal %d\n", signo
);
96 exit(0); /* exit(0) will call cleanup() */
98 /*---------------------------------------------------------------------------*/
100 #define SLIP_ESC 0333
101 #define SLIP_ESC_END 0334
102 #define SLIP_ESC_ESC 0335
105 u_int8_t ip_vhl
; /* version and header length */
109 u_int8_t ip_tos
; /* type of service */
110 u_int16_t ip_len
; /* total length */
111 u_int16_t ip_id
; /* identification */
112 u_int16_t ip_off
; /* fragment offset field */
113 #define IP_RF 0x8000 /* reserved fragment flag */
114 #define IP_DF 0x4000 /* dont fragment flag */
115 #define IP_MF 0x2000 /* more fragments flag */
116 #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
117 u_int8_t ip_ttl
; /* time to live */
118 u_int8_t ip_p
; /* protocol */
119 u_int16_t ip_sum
; /* checksum */
120 u_int32_t ip_src
, ip_dst
; /* source and dest address */
121 u_int16_t uh_sport
; /* source port */
122 u_int16_t uh_dport
; /* destination port */
123 u_int16_t uh_ulen
; /* udp length */
124 u_int16_t uh_sum
; /* udp checksum */
127 static int ip_id
, last_id
;
130 ssystem(const char *fmt
, ...) __attribute__((__format__ (__printf__
, 1, 2)));
133 ssystem(const char *fmt
, ...)
138 vsnprintf(cmd
, sizeof(cmd
), fmt
, ap
);
146 is_sensible_string(const unsigned char *s
, int len
)
149 for(i
= 1; i
< len
; i
++) {
150 if(s
[i
] == 0 || s
[i
] == '\r' || s
[i
] == '\n' || s
[i
] == '\t') {
152 } else if(s
[i
] < ' ' || '~' < s
[i
]) {
161 ip4sum(u_int16_t sum
, const void *_p
, u_int16_t len
)
164 const u_int8_t
*p
= _p
;
165 const u_int8_t
*end
= p
+ len
;
168 t
= (p
[0] << 8) + p
[1];
182 chksum(const void *p
, uint16_t len
)
184 uint16_t sum
= ip4sum(0, p
, len
);
185 return (sum
== 0) ? 0xffff : htons(sum
);
189 check_ip(const struct ip
*ip
, unsigned ip_len
)
191 u_int16_t sum
, ip_hl
;
193 /* Check IP version and length. */
194 if((ip
->ip_vhl
& IP_V
) != IP_V4
) {
198 if(ntohs(ip
->ip_len
) > ip_len
) {
202 if(ntohs(ip
->ip_len
) < ip_len
) {
206 /* Check IP header. */
207 ip_hl
= 4*(ip
->ip_vhl
& IP_HL
);
208 sum
= ip4sum(0, ip
, ip_hl
);
209 if(sum
!= 0xffff && sum
!= 0x0) {
213 if(ip
->ip_p
== 6 || ip
->ip_p
== 17) { /* Check TCP or UDP header. */
214 u_int16_t tcp_len
= ip_len
- ip_hl
;
216 /* Sum pseudoheader. */
217 sum
= ip
->ip_p
+ tcp_len
; /* proto and len, no carry */
218 sum
= ip4sum(sum
, &ip
->ip_src
, 8); /* src and dst */
220 /* Sum TCP/UDP header and data. */
221 sum
= ip4sum(sum
, (u_int8_t
*)ip
+ ip_hl
, tcp_len
);
223 /* Failed checksum test? */
224 if(sum
!= 0xffff && sum
!= 0x0) {
225 if(ip
->ip_p
== 6) { /* TCP == 6 */
228 /* Deal with disabled UDP checksums. */
229 if(ip
->uh_sum
!= 0) {
234 } else if (ip
->ip_p
== 1) { /* ICMP */
235 u_int16_t icmp_len
= ip_len
- ip_hl
;
237 sum
= ip4sum(0, (u_int8_t
*)ip
+ ip_hl
, icmp_len
);
238 if(sum
!= 0xffff && sum
!= 0x0) {
246 * Read from serial, when we have a packet write it to tun. No output
247 * buffering, input buffered by stdio.
250 serial_to_wpcap(FILE *inslip
)
253 unsigned char inbuf
[2000];
256 static int inbufptr
= 0;
262 ret
= fread(&c
, 1, 1, inslip
);
263 if(ret
== -1 || ret
== 0) err(1, "serial_to_tun: read");
268 if(inbufptr
>= sizeof(uip
.inbuf
)) {
271 ret
= fread(&c
, 1, 1, inslip
);
276 err(1, "serial_to_tun: read");
281 fprintf(stderr
, "serial_to_tun: EOF\n");
284 /* fprintf(stderr, ".");*/
291 #define DEBUG_LINE_MARKER '\r'
293 ecode
= check_ip(&uip
.iphdr
, inbufptr
);
294 if(ecode
< 0 && inbufptr
== 8 && strncmp(uip
.inbuf
, "=IPA", 4) == 0) {
295 static struct in_addr ipa
;
298 if(memcmp(&ipa
, &uip
.inbuf
[4], sizeof(ipa
)) == 0) {
303 if(ipa
.s_addr
!= 0) {
306 ssystem("route delete -net %s netmask %s dev %s",
307 inet_ntoa(ipa
), "255.255.255.255", tundev
);
309 ssystem("route delete -net %s -netmask %s -interface %s",
310 inet_ntoa(ipa
), "255.255.255.255", tundev
);
315 memcpy(&ipa
, &uip
.inbuf
[4], sizeof(ipa
));
316 if(ipa
.s_addr
!= 0) {
319 ssystem("route add -net %s netmask %s dev %s",
320 inet_ntoa(ipa
), "255.255.255.255", tundev
);
322 ssystem("route add -net %s -netmask %s -interface %s",
323 inet_ntoa(ipa
), "255.255.255.255", tundev
);
328 } else if(ecode
< 0) {
330 * If sensible ASCII string, print it as debug info!
332 /* printf("----------------------------------\n");*/
333 if(uip
.inbuf
[0] == DEBUG_LINE_MARKER
) {
334 fwrite(uip
.inbuf
+ 1, inbufptr
- 1, 1, stderr
);
335 } else if(is_sensible_string(uip
.inbuf
, inbufptr
)) {
336 fwrite(uip
.inbuf
, inbufptr
, 1, stderr
);
339 "serial_to_tun: drop packet len=%d ecode=%d\n",
349 struct ip
*ip
= (void *)uip
.inbuf
;
350 if(ip
->ip_p
== 17 && ip
->ip_dst
== 0xffffffff /* UDP and broadcast */
351 && ip
->uh_sport
== ntohs(BOOTPC
) && ip
->uh_dport
== ntohs(BOOTPS
)) {
352 relay_dhcp_to_server(ip
, inbufptr
);
357 /* if(write(outfd, uip.inbuf, inbufptr) != inbufptr) {
358 err(1, "serial_to_tun: write");
360 /* printf("Sending to wpcap\n");*/
361 print_packet(uip
.inbuf
, inbufptr
);
362 wpcap_send(uip
.inbuf
, inbufptr
);
363 /* printf("After sending to wpcap\n");*/
369 if(fread(&c
, 1, 1, inslip
) != 1) {
371 /* Put ESC back and give up! */
372 ungetc(SLIP_ESC
, inslip
);
386 uip
.inbuf
[inbufptr
++] = c
;
392 /*---------------------------------------------------------------------------*/
393 unsigned char slip_buf
[2000];
394 int slip_end
, slip_begin
;
395 /*---------------------------------------------------------------------------*/
397 slip_send(int fd
, unsigned char c
)
399 if(slip_end
>= sizeof(slip_buf
)) {
400 err(1, "slip_send overflow");
402 slip_buf
[slip_end
] = c
;
405 /*---------------------------------------------------------------------------*/
409 return slip_end
== 0;
411 /*---------------------------------------------------------------------------*/
413 slip_flushbuf(int fd
)
420 n
= write(fd
, slip_buf
+ slip_begin
, (slip_end
- slip_begin
));
422 if(n
== -1 && errno
!= EAGAIN
) {
423 err(1, "slip_flushbuf write failed");
425 PROGRESS("Q"); /* Outqueueis full! */
428 if(slip_begin
== slip_end
) {
429 slip_begin
= slip_end
= 0;
433 /*---------------------------------------------------------------------------*/
435 write_to_serial(int outfd
, void *inbuf
, int len
)
439 struct ip
*iphdr
= inbuf
;
444 ecode
= check_ip(inbuf
, len
);
446 fprintf(stderr
, "write_to_serial: drop packet %d\n", ecode
);
450 if(iphdr
->ip_id
== 0 && iphdr
->ip_off
& IP_DF
) {
451 uint16_t nid
= htons(ip_id
++);
453 nid
= ~nid
; /* negate */
454 iphdr
->ip_sum
+= nid
; /* add */
455 if(iphdr
->ip_sum
< nid
) { /* 1-complement overflow? */
458 ecode
= check_ip(inbuf
, len
);
460 fprintf(stderr
, "write_to_serial: drop packet %d\n", ecode
);
466 iphdr
->ip_ttl
= htons(htons(iphdr
->ip_ttl
) + 1);
467 if(iphdr
->ip_ttl
== 0) {
468 fprintf(stderr
, "Packet with ttl %d dropped\n", iphdr
->ip_ttl
);
472 iphdr
->ip_sum
= ~chksum(iphdr
, 4 * (iphdr
->ip_vhl
& IP_HL
));
473 ecode
= check_ip(inbuf
, len
);
475 fprintf(stderr
, "write_to_serial: drop packet %d\n", ecode
);
479 /* It would be ``nice'' to send a SLIP_END here but it's not
482 /* slip_send(outfd, SLIP_END); */
484 for(i
= 0; i
< len
; i
++) {
487 slip_send(outfd
, SLIP_ESC
);
488 slip_send(outfd
, SLIP_ESC_END
);
491 slip_send(outfd
, SLIP_ESC
);
492 slip_send(outfd
, SLIP_ESC_ESC
);
495 slip_send(outfd
, p
[i
]);
500 slip_send(outfd
, SLIP_END
);
501 /* printf("slip end\n");*/
504 /*---------------------------------------------------------------------------*/
506 * Read from tun, write to slip.
510 tun_to_serial(int infd
, int outfd
)
513 unsigned char inbuf
[2000];
518 if((size
= read(infd
, uip
.inbuf
, 2000)) == -1) {
519 err(1, "tun_to_serial: read");
522 write_to_serial(outfd
, uip
.inbuf
, size
);
525 /*---------------------------------------------------------------------------*/
527 #define BAUDRATE B115200
529 speed_t b_rate
= BAUDRATE
;
536 t
->c_iflag
&= ~(IMAXBEL
|IXOFF
|INPCK
|BRKINT
|PARMRK
|ISTRIP
|INLCR
|IGNCR
|ICRNL
|IXON
|IGNPAR
);
537 t
->c_iflag
|= IGNBRK
;
538 t
->c_oflag
&= ~OPOST
;
539 t
->c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|ICANON
|ISIG
|IEXTEN
|NOFLSH
|TOSTOP
);
540 t
->c_cflag
&= ~(CSIZE
|PARENB
);
541 t
->c_cflag
|= CS8
|CREAD
;
549 struct termios options
;
550 speed_t speed
= b_rate
;
552 /* if(fcntl(fd, F_SETFL, 0) < 0) {
553 perror("could not set fcntl");
557 if(tcgetattr(fd
, &options
) < 0) {
558 perror("could not get options");
562 cfsetispeed(&options
, speed
);
563 cfsetospeed(&options
, speed
);
564 /* Enable the receiver and set local mode */
565 options
.c_cflag
|= (CLOCAL
| CREAD
);
566 /* Mask the character size bits and turn off (odd) parity */
567 options
.c_cflag
&= ~(CSIZE
|PARENB
|PARODD
);
568 /* Select 8 data bits */
569 options
.c_cflag
|= CS8
;
572 options
.c_lflag
&= ~(ICANON
| ECHO
| ECHOE
| ISIG
);
574 options
.c_oflag
&= ~OPOST
;
576 if (tcsetattr(fd
, TCSANOW
, &options
) < 0) {
577 perror("could not set options");
583 speed_t speed
= b_rate
;
586 if(tcflush(fd
, TCIOFLUSH
) == -1) err(1, "tcflush");
588 if(tcgetattr(fd
, &tty
) == -1) err(1, "tcgetattr");
592 /* Nonblocking read. */
595 tty
.c_cflag
&= ~CRTSCTS
;
596 tty
.c_cflag
&= ~HUPCL
;
597 tty
.c_cflag
&= ~CLOCAL
;
599 cfsetispeed(&tty
, speed
);
600 cfsetospeed(&tty
, speed
);
602 if(tcsetattr(fd
, TCSAFLUSH
, &tty
) == -1) err(1, "tcsetattr");
605 /* Nonblocking read and write. */
606 /* if(fcntl(fd, F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl"); */
608 tty
.c_cflag
|= CLOCAL
;
609 if(tcsetattr(fd
, TCSAFLUSH
, &tty
) == -1) err(1, "tcsetattr");
612 if(ioctl(fd
, TIOCMBIS
, &i
) == -1) err(1, "ioctl");
615 usleep(10*1000); /* Wait for hardware 10ms. */
617 /* Flush input and output buffers. */
618 if(tcflush(fd
, TCIOFLUSH
) == -1) err(1, "tcflush");
621 /*---------------------------------------------------------------------------*/
623 devopen(const char *dev
, int flags
)
628 return open(t
, flags
);
630 /*---------------------------------------------------------------------------*/
631 /*const char *ipaddr;*/
632 /*const char *netmask;*/
633 static int got_sigalarm
;
640 /*---------------------------------------------------------------------------*/
645 #define TIMEOUT (997*1000)
647 #define TIMEOUT (2451*1000)
649 ualarm(TIMEOUT
, TIMEOUT
);
652 /*---------------------------------------------------------------------------*/
654 main(int argc
, char **argv
)
661 const char *siodev
= NULL
;
662 const char *dhcp_server
= NULL
;
663 /* u_int16_t myport = BOOTPS, dhport = BOOTPS;*/
668 ip_id
= getpid() * time(NULL
);
670 setvbuf(stdout
, NULL
, _IOLBF
, 0); /* Line buffered output. */
672 while((c
= getopt(argc
, argv
, "B:D:hls:t:T")) != -1) {
675 baudrate
= atoi(optarg
);
679 dhcp_server
= optarg
;
683 if(strncmp("/dev/", optarg
, 5) == 0) {
692 if(strncmp("/dev/", optarg
, 5) == 0) {
693 strcpy(tundev
, optarg
+ 5);
695 strcpy(tundev
, optarg
);
711 err(1, "usage: wpcapslip [-B baudrate] [-s siodev] [-D dhcp-server] [-T] ipaddress netmask [dhcp-server]");
715 argc
-= (optind
- 1);
716 argv
+= (optind
- 1);
719 err(1, "usage: wpcapslip [-s siodev] [-D dhcp-server] [-T] <IP address of local Ethernet card> <IP address of SLIP network> <netmask of SLIP network>");
723 wpcap_start(argv
[1], argv
[2], argv
[3], logging
);
724 /* netaddr = inet_addr(ipaddr) & inet_addr(netmask);*/
728 break; /* Use default. */
745 err(1, "unknown baudrate %d", baudrate
);
750 * Set up DHCP relay agent socket and find the address of this relay
755 dhcp_server
= argv
[3];
757 if(dhcp_server
!= NULL
) {
758 struct sockaddr_in myaddr
;
762 if(strchr(dhcp_server
, ':') != NULL
) {
763 dhport
= atoi(strchr(dhcp_server
, ':') + 1);
765 *strchr(dhcp_server
, ':') = '\0';
767 a
= inet_addr(dhcp_server
);
769 err(1, "illegal dhcp-server address");
772 dhaddr
.sin_len
= sizeof(dhaddr
);
774 dhaddr
.sin_family
= AF_INET
;
775 dhaddr
.sin_port
= htons(dhport
);
776 dhaddr
.sin_addr
.s_addr
= a
;
778 dhsock
= socket(AF_INET
, SOCK_DGRAM
, 0);
782 memset(&myaddr
, 0x0, sizeof(myaddr
));
784 myaddr
.sin_len
= sizeof(myaddr
);
786 myaddr
.sin_family
= AF_INET
;
787 myaddr
.sin_addr
.s_addr
= INADDR_ANY
;
788 myaddr
.sin_port
= htons(myport
);
789 if(bind(dhsock
, (struct sockaddr
*)&myaddr
, sizeof(myaddr
)) < 0) {
790 err(1, "bind dhcp-relay");
793 if(connect(dhsock
, (struct sockaddr
*)&dhaddr
, sizeof(dhaddr
)) < 0) {
794 err(1, "connect to dhcp-server");
797 len
= sizeof(myaddr
);
798 if(getsockname(dhsock
, (struct sockaddr
*)&myaddr
, &len
) < 0) {
799 err(1, "getsockname dhsock");
802 giaddr
= myaddr
.sin_addr
.s_addr
;
805 * Don't want connected socket.
808 dhsock
= socket(AF_INET
, SOCK_DGRAM
, 0);
812 myaddr
.sin_family
= AF_INET
;
813 myaddr
.sin_addr
.s_addr
= INADDR_ANY
;
814 myaddr
.sin_port
= htons(myport
);
815 if(bind(dhsock
, (struct sockaddr
*)&myaddr
, sizeof(myaddr
)) < 0) {
816 err(1, "bind dhcp-relay");
818 fprintf(stderr
, "DHCP server at %s:%d\n", dhcp_server
, dhport
);
823 slipfd
= devopen(siodev
, O_RDWR
| O_NONBLOCK
| O_NOCTTY
| O_NDELAY
| O_DIRECT
| O_SYNC
);
825 err(1, "can't open siodev ``/dev/%s''", siodev
);
828 static const char *siodevs
[] = {
829 "ttyUSB0", "cuaU0", "ucom0" /* linux, fbsd6, fbsd5 */
832 for(i
= 0; i
< 3; i
++) {
834 slipfd
= devopen(siodev
, O_RDWR
| O_NONBLOCK
);
840 err(1, "can't open siodev");
843 fprintf(stderr
, "slip started on ``/dev/%s''\n", siodev
);
845 slip_send(slipfd
, SLIP_END
);
846 inslip
= fdopen(slipfd
, "r");
848 err(1, "main: fdopen");
851 /* tunfd = tun_alloc(tundev);
852 if(tunfd == -1) err(1, "main: open");
853 fprintf(stderr, "opened device ``/dev/%s''\n", tundev);*/
856 signal(SIGHUP
, sigcleanup
);
857 signal(SIGTERM
, sigcleanup
);
858 signal(SIGINT
, sigcleanup
);
859 signal(SIGALRM
, sigalarm
);
860 /* ifconf(tundev, ipaddr, netmask);*/
869 slip_send(slipfd
, '?');
870 slip_send(slipfd
, 'I');
871 slip_send(slipfd
, 'P');
872 slip_send(slipfd
, 'A');
873 slip_send(slipfd
, SLIP_END
);
877 if(!slip_empty()) { /* Anything to flush? */
878 FD_SET(slipfd
, &wset
);
881 FD_SET(slipfd
, &rset
); /* Read from slip ASAP! */
886 /* We only have one packet at a time queued for slip output. */
888 /* FD_SET(tunfd, &rset);
889 if(tunfd > maxfd) maxfd = tunfd;
891 FD_SET(dhsock, &rset);
892 if(dhsock > maxfd) maxfd = dhsock;
900 ret
= wpcap_poll(&pbuf
);
902 struct ip
*iphdr
= (struct ip
*)pbuf
;
903 if(iphdr
->ip_id
!= last_id
) {
904 last_id
= iphdr
->ip_id
;
905 /* printf("------ wpcap_poll ret %d\n", ret);*/
906 print_packet(pbuf
, ret
);
907 write_to_serial(slipfd
, pbuf
, ret
);
908 slip_flushbuf(slipfd
);
913 printf("!slip_empty\n");*/
919 ret
= select(maxfd
+ 1, &rset
, &wset
, NULL
, &tv
);
921 if(ret
== -1 && errno
!= EINTR
) {
924 if(FD_ISSET(slipfd
, &rset
)) {
925 /* printf("serial_to_wpcap\n");*/
926 /*serial_to_tun(inslip, tunfd);*/
927 serial_to_wpcap(inslip
);
928 /* printf("End of serial_to_wpcap\n");*/
931 if(FD_ISSET(slipfd
, &wset
)) {
932 /* printf("slip_flushbuf\n");*/
933 slip_flushbuf(slipfd
);
937 /* if(slip_empty() && FD_ISSET(tunfd, &rset)) {
938 tun_to_serial(tunfd, slipfd);
939 slip_flushbuf(slipfd);
944 if(dhsock
!= -1 && slip_empty() && FD_ISSET(dhsock
, &rset
)) {
945 relay_dhcp_to_client(slipfd
);
946 slip_flushbuf(slipfd
);
952 /*---------------------------------------------------------------------------*/