/*****************************************************************************/ /* * net2kiss.c - convert a network interface to KISS data stream * * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.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., 675 Mass Ave, Cambridge, MA 02139, 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 /* --------------------------------------------------------------------- */ static int fdif, fdpty; static struct ifreq ifr; static char *progname; static int verbose = 0; /* --------------------------------------------------------------------- */ static void die(char *func) { fprintf(stderr, "%s: %s (%i)%s%s\n", progname, 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 int openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) { char line[] = "/dev/ptyXX"; const char *cp1, *cp2; int master, slave; struct group *gr = getgrnam("tty"); for (cp1 = "pqrstuvwxyzPQRST"; *cp1; cp1++) { line[8] = *cp1; for (cp2 = "0123456789abcdef"; *cp2; cp2++) { line[9] = *cp2; if ((master = open(line, O_RDWR, 0)) == -1) { if (errno == ENOENT) return (-1); /* out of ptys */ } else { line[5] = 't'; (void) chown(line, getuid(), gr ? gr->gr_gid : -1); (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP); #if 0 (void) revoke(line); #endif if ((slave = open(line, O_RDWR, 0)) != -1) { *amaster = master; *aslave = slave; if (name) strcpy(name, line); if (termp) (void) tcsetattr(slave, TCSAFLUSH, termp); if (winp) (void) ioctl(slave, TIOCSWINSZ, (char *)winp); return 0; } (void) close(master); line[5] = 'p'; } } } errno = ENOENT; /* out of ptys */ return (-1); } /* ---------------------------------------------------------------------- */ 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"); return; } /* --------------------------------------------------------------------- */ 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; int 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[32]; 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); } 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"); slavename[5] = 'p'; master_name = slavename; } else { if ((fdpty = open(name_pname, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) { fprintf(stderr, "%s: cannot open \"%s\"\n", progname, name_pname); exit(1); } master_name = name_pname; } if ((fdif = socket(PF_INET, SOCK_PACKET, proto)) < 0) die("socket"); strcpy(sa.sa_data, name_iface); sa.sa_family = AF_INET; if (bind(fdif, &sa, sizeof(struct sockaddr)) < 0) die("bind"); strcpy(ifr.ifr_name, name_iface); 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"); 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; /* * try to reopen master */ if (verbose) printf("reopening master tty: %s\n", master_name); close(fdpty); if ((fdpty = open(master_name, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) { fprintf(stderr, "%s: cannot reopen \"%s\"\n", progname, master_name); exit(1); } } restore_ifflags(0); exit(0); } /* --------------------------------------------------------------------- */