/*****************************************************************************/ /* * net2kiss.c - convert a network interface to KISS data stream * * Copyright (C) 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA * 02110-1301, USA. * * Please note that the GPL allows you to use the driver, NOT the radio. * In order to use the radio, you need a license from the communications * authority of your country. * * * History: * 0.1 18.09.96 Started */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* --------------------------------------------------------------------- */ static int fdif, fdpty; static struct ifreq ifr; static char *progname; static int verbose; static int i_am_unix98_pty_master; /* unix98 ptmx support */ static char *namepts; /* name of the unix98 pts slave, which * the client has to use */ /* --------------------------------------------------------------------- */ static void die(char *func) { fprintf(stderr, "%s: %s (%i)%s%s\n", progname, strerror(errno), errno, func ? " in " : "", func ? func : ""); syslog(LOG_WARNING, "%s (%i)%s%s\n", strerror(errno), errno, func ? " in " : "", func ? func : ""); exit(-1); } /* --------------------------------------------------------------------- */ static void display_packet(unsigned char *bp, unsigned int len) { unsigned char v1=1,cmd=0; unsigned char i,j; if (!bp || !len) return; if (len < 8) return; if (bp[1] & 1) { /* * FlexNet Header Compression */ v1 = 0; cmd = (bp[1] & 2) != 0; printf("fm ? to "); i = (bp[2] >> 2) & 0x3f; if (i) printf("%c",i+0x20); i = ((bp[2] << 4) | ((bp[3] >> 4) & 0xf)) & 0x3f; if (i) printf("%c",i+0x20); i = ((bp[3] << 2) | ((bp[4] >> 6) & 3)) & 0x3f; if (i) printf("%c",i+0x20); i = bp[4] & 0x3f; if (i) printf("%c",i+0x20); i = (bp[5] >> 2) & 0x3f; if (i) printf("%c",i+0x20); i = ((bp[5] << 4) | ((bp[6] >> 4) & 0xf)) & 0x3f; if (i) printf("%c",i+0x20); printf("-%u QSO Nr %u", bp[6] & 0xf, (bp[0] << 6) | (bp[1] >> 2)); bp += 7; len -= 7; } else { /* * normal header */ if (len < 15) return; if ((bp[6] & 0x80) != (bp[13] & 0x80)) { v1 = 0; cmd = (bp[6] & 0x80); } printf("fm "); for (i = 7; i < 13; i++) if ((bp[i] &0xfe) != 0x40) printf("%c",bp[i] >> 1); printf("-%u to ",(bp[13] >> 1) & 0xf); for (i = 0; i < 6; i++) if ((bp[i] &0xfe) != 0x40) printf("%c",bp[i] >> 1); printf("-%u",(bp[6] >> 1) & 0xf); bp += 14; len -= 14; if ((!(bp[-1] & 1)) && (len >= 7)) printf(" via "); while ((!(bp[-1] & 1)) && (len >= 7)) { for (i = 0; i < 6; i++) if ((bp[i] &0xfe) != 0x40) printf("%c",bp[i] >> 1); printf("-%u",(bp[6] >> 1) & 0xf); bp += 7; len -= 7; if ((!(bp[-1] & 1)) && (len >= 7)) printf(","); } } if (!len) return; i = *bp++; len--; j = v1 ? ((i & 0x10) ? '!' : ' ') : ((i & 0x10) ? (cmd ? '+' : '-') : (cmd ? '^' : 'v')); if (!(i & 1)) { /* * Info frame */ printf(" I%u%u%c",(i >> 5) & 7,(i >> 1) & 7,j); } else if (i & 2) { /* * U frame */ switch (i & (~0x10)) { case 0x03: printf(" UI%c",j); break; case 0x2f: printf(" SABM%c",j); break; case 0x43: printf(" DISC%c",j); break; case 0x0f: printf(" DM%c",j); break; case 0x63: printf(" UA%c",j); break; case 0x87: printf(" FRMR%c",j); break; default: printf(" unknown U (0x%x)%c",i & (~0x10),j); break; } } else { /* * supervisory */ switch (i & 0xf) { case 0x1: printf(" RR%u%c",(i >> 5) & 7,j); break; case 0x5: printf(" RNR%u%c",(i >> 5) & 7,j); break; case 0x9: printf(" REJ%u%c",(i >> 5) & 7,j); break; default: printf(" unknown S (0x%x)%u%c", i & 0xf, (i >> 5) & 7, j); break; } } if (!len) { printf("\n"); return; } printf(" pid=%02X\n", *bp++); len--; j = 0; while (len) { i = *bp++; if ((i >= 32) && (i < 128)) printf("%c",i); else if (i == 13) { if (j) printf("\n"); j = 0; } else printf("."); if (i >= 32) j = 1; len--; } if (j) printf("\n"); } /* ---------------------------------------------------------------------- */ static void restore_ifflags(int signum) { if (ioctl(fdif, SIOCSIFFLAGS, &ifr) < 0) die("ioctl SIOCSIFFLAGS"); close(fdif); close(fdpty); exit(0); } /* --------------------------------------------------------------------- */ #define KISS_FEND ((unsigned char)0300) #define KISS_FESC ((unsigned char)0333) #define KISS_TFEND ((unsigned char)0334) #define KISS_TFESC ((unsigned char)0335) #define KISS_CMD_DATA 0 #define KISS_CMD_TXDELAY 1 #define KISS_CMD_PPERSIST 2 #define KISS_CMD_SLOTTIME 3 #define KISS_CMD_TXTAIL 4 #define KISS_CMD_FULLDUP 5 #define KISS_HUNT 0 #define KISS_RX 1 #define KISS_ESCAPED 2 /* --------------------------------------------------------------------- */ static void kiss_overflow(void) { if (verbose) printf("KISS: packet overflow\n"); } static void kiss_bad_escape(void) { if (verbose) printf("KISS: bad escape sequence\n"); } static void display_kiss_packet(char *pfx, unsigned char *pkt, unsigned int pktlen) { if (!verbose) return; switch (*pkt) { case KISS_CMD_DATA: printf("%s: ", pfx); display_packet(pkt+1, pktlen-1); break; case KISS_CMD_TXDELAY: printf("%s: txdelay = %dms\n", pfx, (int)pkt[1] * 10); break; case KISS_CMD_PPERSIST: printf("%s: p persistence = %d\n", pfx, pkt[1]); break; case KISS_CMD_SLOTTIME: printf("%s: slottime = %dms\n", pfx, (int)pkt[1] * 10); break; case KISS_CMD_TXTAIL: printf("%s: txtail = %dms\n", pfx, (int)pkt[1] * 10); break; case KISS_CMD_FULLDUP: printf("%s: %sduplex\n", pfx, pkt[1] ? "full" : "half"); break; default: printf("%s: unknown frame type 0x%02x, length %d\n", pfx, *pkt, pktlen); } } static void kiss_packet(int fdif, char *addr, unsigned char *pkt, unsigned int pktlen) { struct sockaddr to; int i; if (pktlen < 2) return; display_kiss_packet("KISS", pkt, pktlen); strncpy(to.sa_data, addr, sizeof(to.sa_data)); i = sendto(fdif, pkt, pktlen, 0, &to, sizeof(to)); if (i >= 0) return; if (errno == EMSGSIZE) { if (verbose) printf("sendto: %s: packet (size %d) too " "long\n", addr, pktlen-1); return; } if (errno == EWOULDBLOCK) { if (verbose) printf("sendto: %s: busy\n", addr); return; } die("sendto"); } /* --------------------------------------------------------------------- */ static int doio(int fdif, int fdpty, char *ifaddr) { unsigned char ibuf[2048]; unsigned char *bp; unsigned char pktbuf[2048]; unsigned char *pktptr = pktbuf; unsigned char pktstate = KISS_HUNT; unsigned char obuf[16384]; unsigned int ob_wp = 0, ob_rp = 0, ob_wpx; int i; fd_set rmask, wmask; struct sockaddr from; socklen_t from_len; #define ADD_CHAR(c) \ obuf[ob_wpx] = c; \ ob_wpx = (ob_wpx + 1) % sizeof(obuf); \ if (ob_wpx == ob_rp) goto kissencerr; #define ADD_KISSCHAR(c) \ if (((c) & 0xff) == KISS_FEND) \ { ADD_CHAR(KISS_FESC); ADD_CHAR(KISS_TFEND); } \ else if (((c) & 0xff) == KISS_FESC) \ { ADD_CHAR(KISS_FESC); ADD_CHAR(KISS_TFESC); } \ else { ADD_CHAR(c); } for (;;) { FD_ZERO(&rmask); FD_ZERO(&wmask); FD_SET(fdif, &rmask); FD_SET(fdpty, &rmask); if (ob_rp != ob_wp) FD_SET(fdpty, &wmask); i = select((fdif > fdpty) ? fdif+1 : fdpty+1, &rmask, &wmask, NULL, NULL); if (i < 0) die("select"); if (FD_ISSET(fdpty, &wmask)) { if (ob_rp > ob_wp) i = write(fdpty, obuf+ob_rp, sizeof(obuf)-ob_rp); else i = write(fdpty, obuf+ob_rp, ob_wp - ob_rp); if (i < 0) die("write"); ob_rp = (ob_rp + i) % sizeof(obuf); } if (FD_ISSET(fdpty, &rmask)) { i = read(fdpty, bp = ibuf, sizeof(ibuf)); if (i < 0) { if (errno != EIO) die("read"); return 0; } for (; i > 0; i--, bp++) { switch (pktstate) { default: case KISS_HUNT: if (*bp != KISS_FEND) break; pktptr = pktbuf; pktstate = KISS_RX; break; case KISS_RX: if (*bp == KISS_FESC) { pktstate = KISS_ESCAPED; break; } if (*bp == KISS_FEND) { kiss_packet(fdif, ifaddr, pktbuf, pktptr - pktbuf); pktptr = pktbuf; break; } if (pktptr >= pktbuf+sizeof(pktbuf)) { kiss_overflow(); pktstate = KISS_HUNT; break; } *pktptr++ = *bp; break; case KISS_ESCAPED: if (pktptr >= pktbuf+sizeof(pktbuf)) { kiss_overflow(); pktstate = KISS_HUNT; break; } if (*bp == KISS_TFESC) *pktptr++ = KISS_FESC; else if (*bp == KISS_TFEND) *pktptr++ = KISS_FEND; else { kiss_bad_escape(); pktstate = KISS_HUNT; break; } pktstate = KISS_RX; break; } } } if (FD_ISSET(fdif, &rmask)) { from_len = sizeof(from); i = recvfrom(fdif, bp = ibuf, sizeof(ibuf), 0, &from, &from_len); if (i < 0) { if (errno == EWOULDBLOCK) continue; die("recvfrom"); } if (verbose) display_kiss_packet(from.sa_data, ibuf, i); ob_wpx = ob_wp; ADD_CHAR(KISS_FEND); for (; i > 0; i--, bp++) { ADD_KISSCHAR(*bp); } ADD_CHAR(KISS_FEND); ob_wp = ob_wpx; } continue; kissencerr: if (verbose) printf("KISS: Encoder out of memory\n"); } #undef ADD_CHAR #undef ADD_KISSCHAR } /* --------------------------------------------------------------------- */ int main(int argc, char *argv[]) { struct ifreq ifr_new; struct sockaddr sa; char *name_iface = "bc0"; char *name_pname = NULL; char slavename[PATH_MAX]; char *master_name; struct termios termios; int c; int errflg = 0; int symlnk = 0; int symlnkforce = 0; short if_newflags = 0; int proto = htons(ETH_P_AX25); progname = argv[0]; while ((c = getopt(argc, argv, "sfzvai:")) != EOF) { switch (c) { case 's': symlnk = 1; break; case 'f': symlnkforce = 1; break; case 'i': name_iface = optarg; break; case 'z': if_newflags |= IFF_PROMISC; break; case 'v': verbose++; break; case 'a': proto = htons(ETH_P_ALL); break; default: errflg++; break; } } if (argc > optind) name_pname = argv[optind]; else errflg++; if (errflg) { fprintf(stderr, "usage: %s [-s] [-f] [-i iface] " "[-z] [-v] ptyname\n", progname); exit(1); } openlog(progname, LOG_PID, LOG_DAEMON); if (symlnk) { int fdtty; if (openpty(&fdpty, &fdtty, slavename, NULL, NULL)) { fprintf(stderr, "%s: out of pseudoterminals\n", progname); exit(1); } close(fdtty); fcntl(fdpty, F_SETFL, fcntl(fdpty, F_GETFL, 0) | O_NONBLOCK); if (symlnkforce) unlink(name_pname); if (symlink(slavename, name_pname)) perror("symlink"); /* Users await the slave pty to be referenced in the 2nd line */ printf("Awaiting client connects on\n%s\n", slavename); slavename[5] = 'p'; master_name = slavename; } else { fdpty = open(name_pname, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fdpty < 0) { fprintf(stderr, "%s: cannot open \"%s\"\n", progname, name_pname); exit(1); } if (!strcmp("/dev/ptmx", name_pname)) i_am_unix98_pty_master = 1; master_name = name_pname; } fdif = socket(PF_INET, SOCK_PACKET, proto); if (fdif < 0) die("socket"); memset(&sa, 0, sizeof(struct sockaddr)); memcpy(sa.sa_data, name_iface, sizeof(sa.sa_data)); sa.sa_family = AF_INET; if (bind(fdif, &sa, sizeof(struct sockaddr)) < 0) die("bind"); memcpy(ifr.ifr_name, name_iface, IFNAMSIZ); if (ioctl(fdif, SIOCGIFFLAGS, &ifr) < 0) die("ioctl SIOCGIFFLAGS"); ifr_new = ifr; ifr_new.ifr_flags |= if_newflags; if (ioctl(fdif, SIOCSIFFLAGS, &ifr_new) < 0) die("ioctl SIOCSIFFLAGS"); if (i_am_unix98_pty_master) { /* get name of pts-device */ namepts = ptsname(fdpty); if (namepts == NULL) { fprintf(stderr, "%s: Cannot get name of pts-device.\n", progname); exit (1); } /* unlock pts-device */ if (unlockpt(fdpty) == -1) { fprintf(stderr, "%s: Cannot unlock pts-device %s\n", progname, namepts); exit (1); } /* Users await the slave pty to be referenced in the 2nd line */ printf("Awaiting client connects on\n%s\n", namepts); if (!verbose) { fflush(stdout); fflush(stderr); close(0); close(1); close(2); } } signal(SIGHUP, restore_ifflags); signal(SIGINT, restore_ifflags); signal(SIGTERM, restore_ifflags); signal(SIGQUIT, restore_ifflags); signal(SIGUSR1, restore_ifflags); signal(SIGUSR2, restore_ifflags); for (;;) { if (tcgetattr(fdpty, &termios)) die("tcgetattr"); termios.c_iflag = IGNBRK; termios.c_oflag = 0; termios.c_lflag = 0; termios.c_cflag &= ~(CSIZE|CSTOPB|PARENB|HUPCL|CRTSCTS); termios.c_cflag |= CS8|CREAD|CLOCAL; if (tcsetattr(fdpty, TCSANOW, &termios)) die("tsgetattr"); if (doio(fdif, fdpty, name_iface)) break; if (i_am_unix98_pty_master) { if (verbose) printf("%s: Trying to poll port ptmx (slave %s).\nWaiting 30s...\n", progname, namepts); syslog(LOG_WARNING, "Trying to poll port ptmx (slave %s). Waiting 30s...\n", namepts); sleep(30); } else { /* * try to reopen master */ if (verbose) printf("reopening master tty: %s\n", master_name); close(fdpty); fdpty = open(master_name, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fdpty < 0) { fprintf(stderr, "%s: cannot reopen \"%s\"\n", progname, master_name); exit(1); } } } restore_ifflags(0); exit(0); } /* --------------------------------------------------------------------- */