Contiki-NG
slip-dev.c
1 /*
2  * Copyright (c) 2001, Adam Dunkels.
3  * Copyright (c) 2009, 2010 Joakim Eriksson, Niclas Finne, Dogan Yazar.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote
15  * products derived from this software without specific prior
16  * written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 
32  /* Below define allows importing saved output into Wireshark as "Raw IP" packet type */
33 #define WIRESHARK_IMPORT_FORMAT 1
34 #include "contiki.h"
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <time.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <termios.h>
46 #include <signal.h>
47 #include <sys/ioctl.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <netdb.h>
52 #include <err.h>
53 
54 #include "net/netstack.h"
55 #include "net/packetbuf.h"
56 #include "cmd.h"
57 #include "border-router-cmds.h"
58 
59 extern int slip_config_verbose;
60 extern int slip_config_flowcontrol;
61 extern const char *slip_config_siodev;
62 extern const char *slip_config_host;
63 extern const char *slip_config_port;
64 extern uint16_t slip_config_basedelay;
65 extern speed_t slip_config_b_rate;
66 
67 #ifdef SLIP_DEV_CONF_SEND_DELAY
68 #define SEND_DELAY SLIP_DEV_CONF_SEND_DELAY
69 #else
70 #define SEND_DELAY 0
71 #endif
72 
73 int devopen(const char *dev, int flags);
74 
75 static FILE *inslip;
76 
77 /* for statistics */
78 long slip_sent = 0;
79 long slip_received = 0;
80 
81 int slipfd = 0;
82 
83 #define PROGRESS(s) do { } while(0)
84 
85 #define SLIP_END 0300
86 #define SLIP_ESC 0333
87 #define SLIP_ESC_END 0334
88 #define SLIP_ESC_ESC 0335
89 
90 /*---------------------------------------------------------------------------*/
91 static void *
92 get_in_addr(struct sockaddr *sa)
93 {
94  if(sa->sa_family == AF_INET) {
95  return &(((struct sockaddr_in *)sa)->sin_addr);
96  }
97  return &(((struct sockaddr_in6 *)sa)->sin6_addr);
98 }
99 /*---------------------------------------------------------------------------*/
100 static int
101 connect_to_server(const char *host, const char *port)
102 {
103  /* Setup TCP connection */
104  struct addrinfo hints, *servinfo, *p;
105  char s[INET6_ADDRSTRLEN];
106  int rv, fd;
107 
108  memset(&hints, 0, sizeof hints);
109  hints.ai_family = AF_UNSPEC;
110  hints.ai_socktype = SOCK_STREAM;
111 
112  if((rv = getaddrinfo(host, port, &hints, &servinfo)) != 0) {
113  err(1, "getaddrinfo: %s", gai_strerror(rv));
114  return -1;
115  }
116 
117  /* loop through all the results and connect to the first we can */
118  for(p = servinfo; p != NULL; p = p->ai_next) {
119  if((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
120  perror("client: socket");
121  continue;
122  }
123 
124  if(connect(fd, p->ai_addr, p->ai_addrlen) == -1) {
125  close(fd);
126  perror("client: connect");
127  continue;
128  }
129  break;
130  }
131 
132  if(p == NULL) {
133  err(1, "can't connect to ``%s:%s''", host, port);
134  return -1;
135  }
136 
137  fcntl(fd, F_SETFL, O_NONBLOCK);
138 
139  inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
140  s, sizeof(s));
141 
142  /* all done with this structure */
143  freeaddrinfo(servinfo);
144  return fd;
145 }
146 /*---------------------------------------------------------------------------*/
147 int
148 is_sensible_string(const unsigned char *s, int len)
149 {
150  int i;
151  for(i = 1; i < len; i++) {
152  if(s[i] == 0 || s[i] == '\r' || s[i] == '\n' || s[i] == '\t') {
153  continue;
154  } else if(s[i] < ' ' || '~' < s[i]) {
155  return 0;
156  }
157  }
158  return 1;
159 }
160 /*---------------------------------------------------------------------------*/
161 void
162 slip_packet_input(unsigned char *data, int len)
163 {
164  packetbuf_copyfrom(data, len);
165  if(slip_config_verbose > 0) {
166  printf("Packet input over SLIP: %d\n", len);
167  }
168  NETSTACK_MAC.input();
169 }
170 /*---------------------------------------------------------------------------*/
171 /*
172  * Read from serial, when we have a packet call slip_packet_input. No output
173  * buffering, input buffered by stdio.
174  */
175 void
176 serial_input(FILE *inslip)
177 {
178  static unsigned char inbuf[2048];
179  static int inbufptr = 0;
180  int ret, i;
181  unsigned char c;
182 
183 #ifdef linux
184  ret = fread(&c, 1, 1, inslip);
185  if(ret == -1 || ret == 0) {
186  err(1, "serial_input: read");
187  }
188  goto after_fread;
189 #endif
190 
191 read_more:
192  if(inbufptr >= sizeof(inbuf)) {
193  fprintf(stderr, "*** dropping large %d byte packet\n", inbufptr);
194  inbufptr = 0;
195  }
196  ret = fread(&c, 1, 1, inslip);
197 #ifdef linux
198 after_fread:
199 #endif
200  if(ret == -1) {
201  err(1, "serial_input: read");
202  }
203  if(ret == 0) {
204  clearerr(inslip);
205  return;
206  }
207  slip_received++;
208  switch(c) {
209  case SLIP_END:
210  if(inbufptr > 0) {
211  if(inbuf[0] == '!') {
212  command_context = CMD_CONTEXT_RADIO;
213  cmd_input(inbuf, inbufptr);
214  } else if(inbuf[0] == '?') {
215 #define DEBUG_LINE_MARKER '\r'
216  } else if(inbuf[0] == DEBUG_LINE_MARKER) {
217  fwrite(inbuf + 1, inbufptr - 1, 1, stdout);
218  } else if(is_sensible_string(inbuf, inbufptr)) {
219  if(slip_config_verbose == 1) { /* strings already echoed below for verbose>1 */
220  fwrite(inbuf, inbufptr, 1, stdout);
221  }
222  } else {
223  if(slip_config_verbose > 2) {
224  printf("Packet from SLIP of length %d - write TUN\n", inbufptr);
225  if(slip_config_verbose > 4) {
226 #if WIRESHARK_IMPORT_FORMAT
227  printf("0000");
228  for(i = 0; i < inbufptr; i++) {
229  printf(" %02x", inbuf[i]);
230  }
231 #else
232  printf(" ");
233  for(i = 0; i < inbufptr; i++) {
234  printf("%02x", inbuf[i]);
235  if((i & 3) == 3) {
236  printf(" ");
237  }
238  if((i & 15) == 15) {
239  printf("\n ");
240  }
241  }
242 #endif
243  printf("\n");
244  }
245  }
246  slip_packet_input(inbuf, inbufptr);
247  }
248  inbufptr = 0;
249  }
250  break;
251 
252  case SLIP_ESC:
253  if(fread(&c, 1, 1, inslip) != 1) {
254  clearerr(inslip);
255  /* Put ESC back and give up! */
256  ungetc(SLIP_ESC, inslip);
257  return;
258  }
259 
260  switch(c) {
261  case SLIP_ESC_END:
262  c = SLIP_END;
263  break;
264  case SLIP_ESC_ESC:
265  c = SLIP_ESC;
266  break;
267  }
268  /* FALLTHROUGH */
269  default:
270  inbuf[inbufptr++] = c;
271 
272  /* Echo lines as they are received for verbose=2,3,5+ */
273  /* Echo all printable characters for verbose==4 */
274  if(slip_config_verbose == 4) {
275  if(c == 0 || c == '\r' || c == '\n' || c == '\t' || (c >= ' ' && c <= '~')) {
276  fwrite(&c, 1, 1, stdout);
277  }
278  } else if(slip_config_verbose >= 2) {
279  if(c == '\n' && is_sensible_string(inbuf, inbufptr)) {
280  fwrite(inbuf, inbufptr, 1, stdout);
281  inbufptr = 0;
282  }
283  }
284  break;
285  }
286 
287  goto read_more;
288 }
289 unsigned char slip_buf[2048];
290 int slip_end, slip_begin, slip_packet_end, slip_packet_count;
291 static struct timer send_delay_timer;
292 /* delay between slip packets */
293 static clock_time_t send_delay = SEND_DELAY;
294 /*---------------------------------------------------------------------------*/
295 static void
296 slip_send(int fd, unsigned char c)
297 {
298  if(slip_end >= sizeof(slip_buf)) {
299  err(1, "slip_send overflow");
300  }
301  slip_buf[slip_end] = c;
302  slip_end++;
303  slip_sent++;
304  if(c == SLIP_END) {
305  /* Full packet received. */
306  slip_packet_count++;
307  if(slip_packet_end == 0) {
308  slip_packet_end = slip_end;
309  }
310  }
311 }
312 /*---------------------------------------------------------------------------*/
313 int
314 slip_empty()
315 {
316  return slip_packet_end == 0;
317 }
318 /*---------------------------------------------------------------------------*/
319 void
320 slip_flushbuf(int fd)
321 {
322  int n;
323 
324  if(slip_empty()) {
325  return;
326  }
327 
328  n = write(fd, slip_buf + slip_begin, slip_packet_end - slip_begin);
329 
330  if(n == -1 && errno != EAGAIN) {
331  err(1, "slip_flushbuf write failed");
332  } else if(n == -1) {
333  PROGRESS("Q"); /* Outqueue is full! */
334  } else {
335  slip_begin += n;
336  if(slip_begin == slip_packet_end) {
337  slip_packet_count--;
338  if(slip_end > slip_packet_end) {
339  memmove(slip_buf, slip_buf + slip_packet_end,
340  slip_end - slip_packet_end);
341  }
342  slip_end -= slip_packet_end;
343  slip_begin = slip_packet_end = 0;
344  if(slip_end > 0) {
345  /* Find end of next slip packet */
346  for(n = 1; n < slip_end; n++) {
347  if(slip_buf[n] == SLIP_END) {
348  slip_packet_end = n + 1;
349  break;
350  }
351  }
352  /* a delay between slip packets to avoid losing data */
353  if(send_delay > 0) {
354  timer_set(&send_delay_timer, send_delay);
355  }
356  }
357  }
358  }
359 }
360 /*---------------------------------------------------------------------------*/
361 static void
362 write_to_serial(int outfd, const uint8_t *inbuf, int len)
363 {
364  const uint8_t *p = inbuf;
365  int i;
366 
367  if(slip_config_verbose > 2) {
368 #ifdef __CYGWIN__
369  printf("Packet from WPCAP of length %d - write SLIP\n", len);
370 #else
371  printf("Packet from TUN of length %d - write SLIP\n", len);
372 #endif
373  if(slip_config_verbose > 4) {
374 #if WIRESHARK_IMPORT_FORMAT
375  printf("0000");
376  for(i = 0; i < len; i++) {
377  printf(" %02x", p[i]);
378  }
379 #else
380  printf(" ");
381  for(i = 0; i < len; i++) {
382  printf("%02x", p[i]);
383  if((i & 3) == 3) {
384  printf(" ");
385  }
386  if((i & 15) == 15) {
387  printf("\n ");
388  }
389  }
390 #endif
391  printf("\n");
392  }
393  }
394 
395  /* It would be ``nice'' to send a SLIP_END here but it's not
396  * really necessary.
397  */
398  /* slip_send(outfd, SLIP_END); */
399 
400  for(i = 0; i < len; i++) {
401  switch(p[i]) {
402  case SLIP_END:
403  slip_send(outfd, SLIP_ESC);
404  slip_send(outfd, SLIP_ESC_END);
405  break;
406  case SLIP_ESC:
407  slip_send(outfd, SLIP_ESC);
408  slip_send(outfd, SLIP_ESC_ESC);
409  break;
410  default:
411  slip_send(outfd, p[i]);
412  break;
413  }
414  }
415  slip_send(outfd, SLIP_END);
416  PROGRESS("t");
417 }
418 /*---------------------------------------------------------------------------*/
419 /* writes an 802.15.4 packet to slip-radio */
420 void
421 write_to_slip(const uint8_t *buf, int len)
422 {
423  if(slipfd > 0) {
424  write_to_serial(slipfd, buf, len);
425  }
426 }
427 /*---------------------------------------------------------------------------*/
428 static void
429 stty_telos(int fd)
430 {
431  struct termios tty;
432  speed_t speed = slip_config_b_rate;
433  int i;
434 
435  if(tcflush(fd, TCIOFLUSH) == -1) {
436  err(1, "tcflush");
437  }
438 
439  if(tcgetattr(fd, &tty) == -1) {
440  err(1, "tcgetattr");
441  }
442 
443  cfmakeraw(&tty);
444 
445  /* Nonblocking read. */
446  tty.c_cc[VTIME] = 0;
447  tty.c_cc[VMIN] = 0;
448  if(slip_config_flowcontrol) {
449  tty.c_cflag |= CRTSCTS;
450  } else {
451  tty.c_cflag &= ~CRTSCTS;
452  }
453  tty.c_cflag &= ~HUPCL;
454  tty.c_cflag &= ~CLOCAL;
455 
456  cfsetispeed(&tty, speed);
457  cfsetospeed(&tty, speed);
458 
459  if(tcsetattr(fd, TCSAFLUSH, &tty) == -1) {
460  err(1, "tcsetattr");
461  }
462 
463 #if 1
464  /* Nonblocking read and write. */
465  /* if(fcntl(fd, F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl"); */
466 
467  tty.c_cflag |= CLOCAL;
468  if(tcsetattr(fd, TCSAFLUSH, &tty) == -1) {
469  err(1, "tcsetattr");
470  }
471 
472  i = TIOCM_DTR;
473  if(ioctl(fd, TIOCMBIS, &i) == -1) {
474  err(1, "ioctl");
475  }
476 #endif
477 
478  usleep(10 * 1000); /* Wait for hardware 10ms. */
479 
480  /* Flush input and output buffers. */
481  if(tcflush(fd, TCIOFLUSH) == -1) {
482  err(1, "tcflush");
483  }
484 }
485 /*---------------------------------------------------------------------------*/
486 static int
487 set_fd(fd_set *rset, fd_set *wset)
488 {
489  /* Anything to flush? */
490  if(!slip_empty() && (send_delay == 0 || timer_expired(&send_delay_timer))) {
491  FD_SET(slipfd, wset);
492  }
493 
494  FD_SET(slipfd, rset); /* Read from slip ASAP! */
495  return 1;
496 }
497 /*---------------------------------------------------------------------------*/
498 static void
499 handle_fd(fd_set *rset, fd_set *wset)
500 {
501  if(FD_ISSET(slipfd, rset)) {
502  serial_input(inslip);
503  }
504 
505  if(FD_ISSET(slipfd, wset)) {
506  slip_flushbuf(slipfd);
507  }
508 }
509 /*---------------------------------------------------------------------------*/
510 static const struct select_callback slip_callback = { set_fd, handle_fd };
511 /*---------------------------------------------------------------------------*/
512 void
513 slip_init(void)
514 {
515  setvbuf(stdout, NULL, _IOLBF, 0); /* Line buffered output. */
516 
517  if(slip_config_host != NULL) {
518  if(slip_config_port == NULL) {
519  slip_config_port = "60001";
520  }
521  slipfd = connect_to_server(slip_config_host, slip_config_port);
522  if(slipfd == -1) {
523  err(1, "can't connect to ``%s:%s''", slip_config_host, slip_config_port);
524  }
525  } else if(slip_config_siodev != NULL) {
526  if(strcmp(slip_config_siodev, "null") == 0) {
527  /* Disable slip */
528  return;
529  }
530  slipfd = devopen(slip_config_siodev, O_RDWR | O_NONBLOCK);
531  if(slipfd == -1) {
532  err(1, "can't open siodev ``/dev/%s''", slip_config_siodev);
533  }
534  } else {
535  static const char *siodevs[] = {
536  "ttyUSB0", "cuaU0", "ucom0" /* linux, fbsd6, fbsd5 */
537  };
538  int i;
539  for(i = 0; i < 3; i++) {
540  slip_config_siodev = siodevs[i];
541  slipfd = devopen(slip_config_siodev, O_RDWR | O_NONBLOCK);
542  if(slipfd != -1) {
543  break;
544  }
545  }
546  if(slipfd == -1) {
547  err(1, "can't open siodev");
548  }
549  }
550 
551  select_set_callback(slipfd, &slip_callback);
552 
553  if(slip_config_host != NULL) {
554  fprintf(stderr, "********SLIP opened to ``%s:%s''\n", slip_config_host,
555  slip_config_port);
556  } else {
557  fprintf(stderr, "********SLIP started on ``/dev/%s''\n", slip_config_siodev);
558  stty_telos(slipfd);
559  }
560 
561  timer_set(&send_delay_timer, 0);
562  slip_send(slipfd, SLIP_END);
563  inslip = fdopen(slipfd, "r");
564  if(inslip == NULL) {
565  err(1, "slip_init: fdopen");
566  }
567 }
568 /*---------------------------------------------------------------------------*/
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
Definition: timer.c:64
Simple command handler
void slip_send(void)
Send an IP packet from the uIP buffer with SLIP.
Definition: slip.c:189
A timer.
Definition: timer.h:82
void(* input)(void)
Callback for getting notified of incoming packet.
Definition: mac.h:72
int timer_expired(struct timer *t)
Check if a timer has expired.
Definition: timer.c:123
int packetbuf_copyfrom(const void *from, uint16_t len)
Copy from external data into the packetbuf.
Definition: packetbuf.c:84
Sets up some commands for the border router
Header file for the Packet buffer (packetbuf) management
Include file for the Contiki low-layer network stack (NETSTACK)