/* io.c All base I/O routines live here * * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc. * This software may be freely used, distributed, or modified, providing * this header is not removed. * * This is the only module that knows about base level UNIX/SunOS I/O * This is also the key dispatching module, so it knows about a lot more * than just I/O stuff. */ #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ax25ipd.h" static struct termio nterm; int ttyfd = -1; static int udpsock = -1; static int sock = -1; static struct sockaddr_in udpbind; static struct sockaddr_in to; static struct sockaddr_in from; static socklen_t fromlen; static time_t last_bc_time; int ttyfd_bpq = 0; /* * I/O modes for the io_error routine */ #define READ_MSG 0x00 #define SEND_MSG 0x01 #define IP_MODE 0x10 #define UDP_MODE 0x20 #define TTY_MODE 0x30 #ifndef FNDELAY #define FNDELAY O_NDELAY #endif /* * process an I/O error; return true if a retry is needed * * oops - the error flag; < 0 indicates a problem * buf - the data in question * bufsize - the size of the data buffer * dir - the direction; input or output * mode - the fd on which we got the error * where - line in the code where this function was called */ static int io_error( int oops, unsigned char *buf, int bufsize, int dir, int mode, int where) { /* if (oops >= 0) return 0; */ /* do we have an error ? */ /* dl9sau: nobody has set fd's to O_NONBLOCK. * thus EAGAIN (below) or EWOULDBLOCK are never be set. * Has someone removed this behaviour previously? * Anyway, in the current implementation, with blocking * read/writes, a read or write of 0 bytes means EOF, * for e.g. if the attached tty is closed. * We have to exit then. We've currentlsy no mechanism * for regulary reconnects. */ if (oops > 0) return 0; /* do we have an error ? */ if (oops == 0) { if (dir == READ_MSG && oops != TTY_MODE /* && != TCP_MODE, if we'd implement this */ ) return 0; fprintf(stderr, "Close event on mode 0x%2.2x (during %s). LINE %d. Terminating normaly.\n", mode, (dir == READ_MSG ? "READ" : "WRITE"), where); exit(1); } #ifdef EAGAIN if (errno == EAGAIN) { #ifdef notdef /* select() said that data is available, but recvfrom sais * EAGAIN - i really do not know what's the sense in this.. */ if (dir == READ_MSG && oops != TTY_MODE /* && != TCP_MODE, if we'd implement this */ ) return 0; perror("System 5 I/O error!"); fprintf(stderr, "A System 5 style I/O error was detected. This rogram requires BSD 4.2\n"); fprintf(stderr, "behaviour. This is probably a result of compile-time environment.\n"); fprintf(stderr, "Mode 0x%2.2x, LINE: %d. During %s\n", mode, where, (dir == READ_MSG ? "READ" : "WRITE")); exit(3); #else int ret = 0; if (dir == READ_MSG) { LOGL4("read / recv returned -1 EAGAIN\n"); ret = 0; } else if (dir == SEND_MSG) { LOGL4("write / send returned -1 EAGAIN, sleeping and retrying!\n"); usleep(100000); ret = 1; } return ret; #endif } #endif if (dir == READ_MSG) { if (errno == EINTR) return 0; /* never retry read */ if (errno == EWOULDBLOCK) { LOGL4("READ would block (?!), sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } if (mode == IP_MODE) { perror("reading from raw ip socket"); exit(2); } else if (mode == UDP_MODE) { perror("reading from udp socket"); exit(2); } else if (mode == TTY_MODE) { perror("reading from tty device"); exit(2); } else { perror("reading from unknown I/O"); exit(2); } } else if (dir == SEND_MSG) { if (errno == EINTR) return 1; /* always retry on writes */ if (mode == IP_MODE) { if (errno == EMSGSIZE) { /* msg too big, drop it */ perror("message dropped on raw ip socket"); fprintf (stderr, "message was %d bytes long.\n", bufsize); return 0; } if (errno == ENETDOWN || errno == ENETRESET || errno == ENETUNREACH || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ENONET || errno == EPERM) { /* host closed his axip receiver or dropped the line */ perror("error after sending on to axip partner. ignoring."); LOGL4("error after sending on to axip partner: %s; ignoring!\n", strerror(errno)); return 0; } if (errno == ENOBUFS) { /* congestion; sleep + retry */ LOGL4("send congestion on raw ip, sleeping and retrying!\n"); usleep(100000); return 1; } if (errno == EWOULDBLOCK) { LOGL4("send on raw ip would block, sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } perror("writing to raw ip socket"); exit(2); } else if (mode == UDP_MODE) { if (errno == EMSGSIZE) { /* msg too big, drop it */ perror("message dropped on udp socket"); fprintf(stderr, "message was %d bytes long.\n", bufsize); return 0; } if (errno == ENETDOWN || errno == ENETRESET || errno == ENETUNREACH || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ENONET || errno == EPERM) { /* host closed his axudp receiver or dropped the line */ perror("error after sending to axudp partner. ignoring."); LOGL4("error after sending to axudp partner: %s; ignoring!\n", strerror(errno)); return 0; } if (errno == ENOBUFS) { /* congestion; sleep + retry */ LOGL4("send congestion on udp, sleeping and retrying!\n"); usleep(100000); return 1; } if (errno == EWOULDBLOCK) { LOGL4("send on udp would block, sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } perror("writing to udp socket"); exit(2); } else if (mode == TTY_MODE) { if (errno == EWOULDBLOCK) { LOGL4("write to tty would block, sleeping and retrying!\n"); usleep(100000); /* sleep a bit */ return 1; /* and retry */ } perror("writing to tty device"); exit(2); } else { perror("writing to unknown I/O"); exit(2); } } else { perror("Unknown direction and I/O"); exit(2); } return 0; } /* * Initialize the io variables */ void io_init(void) { /* * Close the file descriptors if they are open. The idea is that we * will be able to support a re-initialization if sent a SIGHUP. */ if (ttyfd >= 0) { close(ttyfd); ttyfd = -1; } if (sock >= 0) { close(sock); sock = -1; } if (udpsock >= 0) { close(udpsock); udpsock = -1; } /* * The memset is not strictly required - it simply zeros out the * address structure. Since both to and from are static, they are * already clear. */ memset(&to, 0, sizeof(struct sockaddr)); to.sin_family = AF_INET; memset(&from, 0, sizeof(struct sockaddr)); from.sin_family = AF_INET; memset(&udpbind, 0, sizeof(struct sockaddr)); udpbind.sin_family = AF_INET; } /* * open and initialize the IO interfaces */ void io_open(void) { int baudrate; int i_am_unix98_pty_master = 0; /* unix98 ptmx support */ char *namepts = NULL; /* name of the unix98 pts slave, which * the client has to use */ if (ip_mode) { sock = socket(AF_INET, SOCK_RAW, IPPROTO_AX25); if (sock < 0) { perror("opening raw socket"); exit(1); } if (fcntl(sock, F_SETFL, FNDELAY) < 0) { perror("setting non-blocking I/O on raw socket"); exit(1); } } if (udp_mode) { udpsock = socket(AF_INET, SOCK_DGRAM, 0); if (udpsock < 0) { perror("opening udp socket"); exit(1); } if (fcntl(udpsock, F_SETFL, FNDELAY) < 0) { perror("setting non-blocking I/O on UDP socket"); exit(1); } /* * Ok, the udp socket is open. Now express our interest in receiving * data destined for a particular socket. */ udpbind.sin_addr.s_addr = INADDR_ANY; udpbind.sin_port = my_udp; if (bind(udpsock, (struct sockaddr *) &udpbind, sizeof udpbind) < 0) { perror("binding udp socket"); exit(1); } } if (!strcmp("/dev/ptmx", ttydevice)) i_am_unix98_pty_master = 1; ttyfd = ((ttyfd_bpq = (strchr(ttydevice, '/') ? 0 : 1)) ? open_ethertap(ttydevice) : open(ttydevice, O_RDWR, 0)); if (ttyfd < 0) { perror("opening tty device"); exit(1); } if (fcntl(ttyfd, F_SETFL, FNDELAY) < 0) { perror("setting non-blocking I/O on tty device"); exit(1); } if (i_am_unix98_pty_master) { /* get name of pts-device */ namepts = ptsname(ttyfd); if (namepts == NULL) { perror("Cannot get name of pts-device."); exit(1); } /* unlock pts-device */ if (unlockpt(ttyfd) == -1) { perror("Cannot unlock pts-device."); exit(1); } } if (ttyfd_bpq) { set_bpq_dev_call_and_up(ttydevice); goto behind_normal_tty; } if (ioctl(ttyfd, TCGETA, &nterm) < 0) { perror("fetching tty device parameters"); exit(1); } if (ttyspeed == 50) baudrate = B50; else if (ttyspeed == 50) baudrate = B50; else if (ttyspeed == 75) baudrate = B75; else if (ttyspeed == 110) baudrate = B110; else if (ttyspeed == 134) baudrate = B134; else if (ttyspeed == 150) baudrate = B150; else if (ttyspeed == 200) baudrate = B200; else if (ttyspeed == 300) baudrate = B300; else if (ttyspeed == 600) baudrate = B600; else if (ttyspeed == 1200) baudrate = B1200; else if (ttyspeed == 1800) baudrate = B1800; else if (ttyspeed == 2400) baudrate = B2400; else if (ttyspeed == 4800) baudrate = B4800; else if (ttyspeed == 9600) baudrate = B9600; #ifdef B19200 else if (ttyspeed == 19200) baudrate = B19200; #else #ifdef EXTA else if (ttyspeed == 19200) baudrate = EXTA; #endif /* EXTA */ #endif /* B19200 */ #ifdef B38400 else if (ttyspeed == 38400) baudrate = B38400; #else #ifdef EXTB else if (ttyspeed == 38400) baudrate = EXTB; #endif /* EXTB */ #endif /* B38400 */ #ifdef B57600 else if (ttyspeed == 57600) baudrate = B57600; #endif /* B57600 */ #ifdef B76800 /* SPARC-specific */ else if (ttyspeed == 76800) baudrate = B76800; #endif /* B76800 */ #ifdef B115200 else if (ttyspeed == 115200) baudrate = B115200; #endif /* B115200 */ #ifdef B153600 /* SPARC-specific */ else if (ttyspeed == 153600) baudrate = B153600; #endif /* B153600 */ #ifdef B230400 else if (ttyspeed == 230400) baudrate = B230400; #endif /* B230400 */ #ifdef B307200 /* SPARC-specific */ else if (ttyspeed == 307200) baudrate = B307200; #endif /* B307200 */ #ifdef B460800 else if (ttyspeed == 460800) baudrate = B460800; #endif /* B460800 */ #ifdef B500000 else if (ttyspeed == 500000) baudrate = B500000; #endif /* B500000 */ #ifdef B576000 else if (ttyspeed == 576000) baudrate = B576000; #endif /* B576000 */ #ifdef B614400 /* SPARC-specific */ else if (ttyspeed == 614400) baudrate = B614400; #endif /* B614400 */ #ifdef B921600 /* SPARC-specific */ else if (ttyspeed == 921600) baudrate = B921600; #endif /* B921600 */ #ifdef B1000000 else if (ttyspeed == 1000000) baudrate = B1000000; #endif /* B1000000 */ #ifdef B1152000 else if (ttyspeed == 1152000) baudrate = B1152000; #endif /* B1152000 */ #ifdef B1500000 else if (ttyspeed == 1500000) baudrate = B1500000; #endif /* B1500000 */ #ifdef B2000000 else if (ttyspeed == 2000000) baudrate = B2000000; #endif /* B2000000 */ #ifdef B2500000 else if (ttyspeed == 2500000) baudrate = B2500000; #endif /* B2500000 */ #ifdef B3000000 else if (ttyspeed == 3000000) baudrate = B3000000; #endif /* B3000000 */ #ifdef B3500000 else if (ttyspeed == 3500000) baudrate = B3500000; #endif /* B3500000 */ #ifdef B4000000 else if (ttyspeed == 4000000) baudrate = B4000000; #endif /* B4000000 */ else baudrate = B9600; nterm.c_iflag = 0; nterm.c_oflag = 0; nterm.c_cflag = baudrate | CS8 | CREAD | CLOCAL; nterm.c_lflag = 0; nterm.c_cc[VMIN] = 0; nterm.c_cc[VTIME] = 0; if (ioctl(ttyfd, TCSETA, &nterm) < 0) { perror("setting tty device parameters"); exit(1); } if (digi) send_params(); if (i_am_unix98_pty_master) { /* Users await the slave pty to be referenced in the last line */ printf("Awaiting client connects on\n%s\n", namepts); syslog(LOG_INFO, "Bound to master pty /dev/ptmx with slave pty %s\n", namepts); } behind_normal_tty: last_bc_time = 0; /* force immediate id */ } /* * Start up and run the I/O mechanisms. * run in a loop, using the select call to handle input. */ void io_start(void) { int n, nb, hdr_len; fd_set readfds; unsigned char buf[MAX_FRAME]; struct timeval wait; struct iphdr *ipptr; time_t now; for (;;) { if ((bc_interval > 0) && digi) { now = time(NULL); if (last_bc_time + bc_interval < now) { last_bc_time = now; LOGL4("iostart: BEACON\n"); do_beacon(); } } wait.tv_sec = 10; /* lets us keep the beacon going */ wait.tv_usec = 0; FD_ZERO(&readfds); FD_SET(ttyfd, &readfds); if (ip_mode) { FD_SET(sock, &readfds); } if (udp_mode) { FD_SET(udpsock, &readfds); } nb = select(FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &wait); if (nb < 0) { if (errno == EINTR) continue; /* Ignore */ perror("select"); exit(1); } if (nb == 0) { fflush(stdout); fflush(stderr); /* just so we go back to the top of the loop! */ continue; } if (FD_ISSET(ttyfd, &readfds)) { do { n = read(ttyfd, buf, MAX_FRAME); } while (io_error(n, buf, n, READ_MSG, TTY_MODE, __LINE__)); LOGL4("ttydata l=%d\n", n); if (n > 0) { if (!ttyfd_bpq) { assemble_kiss(buf, n); } else { /* no crc but MAC header on bpqether */ if (receive_bpq(buf, n) < 0) { goto out_ttyfd; } } } /* * If we are in "beacon after" mode, reset the "last_bc_time" each time * we hear something on the channel. */ if (!bc_every) last_bc_time = time(NULL); } out_ttyfd: if (udp_mode) { if (FD_ISSET(udpsock, &readfds)) { do { fromlen = sizeof from; n = recvfrom(udpsock, buf, MAX_FRAME, 0, (struct sockaddr *) &from, &fromlen); } while (io_error(n, buf, n, READ_MSG, UDP_MODE, __LINE__)); LOGL4("udpdata from=%s port=%d l=%d\n", inet_ntoa(from. sin_addr), ntohs(from. sin_port), n); stats.udp_in++; if (n > 0) from_ip(buf, n); } } /* if udp_mode */ if (ip_mode) { if (FD_ISSET(sock, &readfds)) { do { fromlen = sizeof from; n = recvfrom(sock, buf, MAX_FRAME, 0, (struct sockaddr *) &from, &fromlen); } while (io_error(n, buf, n, READ_MSG, IP_MODE, __LINE__)); ipptr = (struct iphdr *) buf; hdr_len = 4 * ipptr-> ihl; LOGL4("ipdata from=%s l=%d, hl=%d\n", inet_ntoa(from. sin_addr), n, hdr_len); stats.ip_in++; if (n > hdr_len) from_ip(buf + hdr_len, n - hdr_len); } } /* if ip_mode */ } /* for forever */ } /* Send an IP frame */ void send_ip(unsigned char *buf, int l, unsigned char *targetip) { int n; if (l <= 0) return; memcpy(&to.sin_addr, targetip, 4); memcpy(&to.sin_port, &targetip[4], 2); LOGL4("sendipdata to=%s %s %d l=%d\n", inet_ntoa(to. sin_addr), to.sin_port ? "udp" : "ip", ntohs(to.sin_port), l); if (to.sin_port) { if (udp_mode) { stats.udp_out++; do { n = sendto(udpsock, buf, l, 0, (struct sockaddr *) &to, sizeof to); } while (io_error(n, buf, l, SEND_MSG, UDP_MODE, __LINE__)); } } else { if (ip_mode) { stats.ip_out++; do { n = sendto(sock, buf, l, 0, (struct sockaddr *) &to, sizeof to); } while (io_error(n, buf, l, SEND_MSG, IP_MODE, __LINE__)); } } } /* Send a kiss frame */ void send_tty(unsigned char *buf, int l) { int n; unsigned char *p; int nc; if (l <= 0) return; LOGL4("sendttydata l=%d\tsent: ", l); stats.kiss_out++; p = buf; nc = l; n = 0; /* * we have to loop around here because each call to write may write a few * characters. So we simply increment the buffer each time around. If * we ever write no characters, we should get an error code, and io_error * will sleep for a fraction of a second. Note that we are keyed to * the BSD 4.2 behaviour... the Sys 5 non-blocking I/O may or may not work * in this loop. We may detect system 5 behaviour (this would result from * compile-time options) by having io_error barf when it detects an EAGAIN * error code. */ do { if ((n > 0) && (n < nc)) { /* did we put only write a bit? */ p += n; /* point to the new data */ nc -= n; /* drop the length */ } n = write(ttyfd, p, nc); if (n > 0) { if (n != nc) { LOGL4("%d ", n); /* no-one said loglevel 4 */ } else { LOGL4("%d\n", n); /* was efficient!!! */ } } } while (((n > 0) && (n < nc)) || (io_error(n, p, nc, SEND_MSG, TTY_MODE, __LINE__))); }