pax_global_header00006660000000000000000000000064144277606700014527gustar00rootroot0000000000000052 comment=d3e6d4ffaa02d8523220cf0ed2acd10594c86482 .gitignore000066400000000000000000000006351442776067000130670ustar00rootroot00000000000000*~ *.o .deps /INSTALL /aclocal.m4 /autom4te.cache /ax25-tools-*.shar.gz /ax25-tools-*.tar.Z /ax25-tools-*.tar.bz2 /ax25-tools-*.tar.gz /ax25-tools-*.tar.lz /ax25-tools-*.tar.lzma /ax25-tools-*.tar.xz /ax25-tools-*.zip /ax25-tools.spec /config.cache /config.h /config.h.in /config.log /config.status /configure /depcomp /install-sh /missing /stamp-h1 /scm-version.h /scm-version.h.tmp Makefile Makefile.in compile 6pack/000077500000000000000000000000001442776067000120775ustar00rootroot000000000000006pack/.gitignore000066400000000000000000000000421442776067000140630ustar00rootroot00000000000000.deps Makefile Makefile.in m6pack 6pack/Makefile.am000066400000000000000000000005351442776067000141360ustar00rootroot00000000000000 installconf: sbin_PROGRAMS = m6pack LDADD= $(AX25_LIB) dist_man_MANS = m6pack.8 AM_CPPFLAGS = -D_GNU_SOURCE \ -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" AX25_SYSCONFDIR=${sysconfdir}/ax25/ AX25_LOCALSTATEDIR=${localstatedir}/ax25/ install-exec-hook: (cd $(DESTDIR)$(sbindir)) 6pack/m6pack.8000066400000000000000000000033531442776067000133550ustar00rootroot00000000000000.TH M6PACK 8 "20 January 2011" Linux "Linux System Managers Manual" .SH NAME m6pack \- Attach multiples 6PACK interfaces .SH SYNOPSIS .B m6pack [-l] [-s speed] [-x n_ptmx] [-v] ttyinterface pty .. .SH DESCRIPTION .LP .B m6pack allows multiple 6PACK TNCs sharing the same serial port to be used with the Linux AX.25 kernel software. The AX.25 softare has no support for multiple TNCs sharing the same serial line. The different ports are addressed by encoding the port number in the control byte of every 6pack frame. .B m6pack watches a serial port, and routes 6pack frames to/from the pseudo ttys. The other side of the pseudo ttys are then attached with .B spattach as normal. .sp 1 Statistics about the operation of .B m6pack may be obtained by sending the SIGUSR1 signal to the running program. On reception of such a signal .B m6pack will print a set of statistics to the system log if logging has been enabled. .sp 1 Although mention is made of using pseudo ttys as the last arguments, these devices may be normal serial ports. However .B m6pack provides no way in which to set their speed, the speed must therefore be set by some other method. .SH OPTIONS .TP 10 .BI \-l Enables system logging, the default is off. .TP 10 .BI "\-s speed" Set the speed of the serial port. .TP 10 .BI \-v Display the version. .TP 10 .BI "\-x number" This option is for Unix98 PTYs. It allocates "number" ptys; their names are written to stdout. When -x is used, the pty arguments are optional. .SH "SEE ALSO" .BR spattach (8), .BR ifconfig (8), .BR kill (1). .SH AUTHORS I.aki Arenaza EB2EBU , based in the work by: .br Tomi Manninen OH2BNS .br Jonathan Naylor G4KLX .br Kevin Uhlir N0BEL 6pack/m6pack.c000066400000000000000000000404401442776067000134260ustar00rootroot00000000000000/* Hey Emacs! this is -*- linux-c -*- * from /usr/src/linux/Documentation/CodingStyle * * m6pack.c * * Fake out AX.25 code into supporting 6pack TNC rings by routing serial * port data to/from pseudo ttys. * * Author(s): * * Iñaki Arenaza (EB2EBU) * ********************************************************************* * * Quite a lot of stuff "stolen" from mkiss.c, written by * * Kevin Uhlir * Ron Curry * Jonathan Naylor * Tomi Manninen * * Copyright (C) 1999 Iñaki Arenaza * mkiss.c was GPLed, so I guess this one is GPLed too ;-) * ********************************************************************* * * REMARK: * * See document '6pack.ps' by Matthias Welwarsky (DG2FEF), translated * by Thomas Sailer (HB9JNX) found in ax25-doc-1.0.tgz for details about * 6pack protocol specifications. * ********************************************************************* */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef unsigned char __u8; typedef enum {data, command} frame_t; #define SIXP_MAX_ADDR ((__u8) 8) #define SIZE 4096 #define MAX_PTYS SIXP_MAX_ADDR /* Max number of TNCs in a 6PACK ring */ /* * Keep these off the stack. */ static __u8 ibuf[SIZE]; /* buffer for input operations */ static __u8 obuf[SIZE]; /* buffer for sixpack_tx() */ static int invalid_ports; static char *usage_string = "usage: m6pack [-l] [-s speed] [-x num_ptmx_devices] [-v] tyinterface pty ..\n"; static int dump_report = FALSE; static int logging = FALSE; static struct iface *pty[MAX_PTYS]; static struct iface *tty; static int numptys; struct iface { char *name; /* Interface name (/dev/???) */ int fd; /* File descriptor */ int pty_id; /* 6pack address asigned to pty */ __u8 seof; /* leading SEOF of this frame */ __u8 databuf[SIZE]; /* TX buffer (data frames) */ __u8 cmdbuf[1]; /* TX buffer (command frames) */ __u8 *optr; /* Next byte to transmit */ unsigned int sixp_cnt; /* 6pack-coded octets count */ unsigned int decod_cnt; /* 6pack-decoded octets cont */ unsigned int errors; /* 6PACK frame error count */ unsigned int rxpackets; /* RX frames count */ unsigned int txpackets; /* TX frames count */ unsigned long rxbytes; /* RX bytes count */ unsigned long txbytes; /* TX bytes count */ char namepts[PATH_MAX]; /* name of the unix98 pts slaves, which * the client has to use */ }; #define PTY_ID_TTY (-1) /* 6PACK "TNC Address" commands are special in that the address they * carry is not the address of the destination TNC but the initial * address for the firt TNC in the ring (when sent PC->TNC Ring), or * the last TNC address+1 (when received TNC ring -> PC). So we can't * use the address in the command to select a pty to pass the * command, and we have to keep track of what ptys have sent "TNC * Address" commands to sent them back the response (until we modify * the kernel 6pack driver to support multiple TNC's (i.e., network * devices) on one serial port). We deliver the responses with a FIFO * policy. */ static int cmd_addr_fifo[SIXP_MAX_ADDR]; static int head; /* We retrieve from head position */ static int tail; /* We insert at tail position */ /* Masks to tell apart 6pack command and data frames */ #define SIXP_IS_DATA(x) ((((__u8)(x)) & (__u8)0xC0) == (__u8)0) #define SIXP_IS_CMD(x) !(SIXP_IS_DATA(x)) /* Masks to split commands in opcode and TNC address */ #define SIXP_ADDR_MASK ((__u8) (SIXP_MAX_ADDR-1)) #define SIXP_CMD_MASK ((__u8) ~SIXP_ADDR_MASK) #define SIXP_CMD(x) (((__u8)x) & SIXP_CMD_MASK) #define SIXP_ADDR(x) (((__u8)x) & SIXP_ADDR_MASK) /* Macro to build a 6pack TNC command from opcode and TNC address */ #define SIXP_MAKE_CMD(opcode,addr) (((__u8)opcode) | ((__u8)addr)) /* 6pack protocol commands (relevant bits only) */ #define SIXP_CMD_SEOF ((__u8) 0x40) /* start/end of 6pack frame */ #define SIXP_CMD_TX_ORUN ((__u8) 0x48) /* transmit overrun */ #define SIXP_CMD_RX_ORUN ((__u8) 0x50) /* receive overrun */ #define SIXP_CMD_RX_BUF_OVL ((__u8) 0x58) /* receive buffer overflow */ #define SIXP_CMD_LED ((__u8) 0x60) /* set LED status */ #define SIXP_CMD_TX_1 ((__u8) 0xA0) /* TX counter + 1 */ #define SIXP_CMD_RX_1 ((__u8) 0x90) /* RX counter + 1 */ #define SIXP_CMD_DCD ((__u8) 0x88) /* DCD state */ #define SIXP_CMD_CAL ((__u8) 0xE0) /* send calibration pattern */ #define SIXP_CMD_ADDR ((__u8) 0xE8) /* set TNC address */ /* Valid checksum of a 6pack frame */ #define SIXP_CHKSUM ((__u8) 0xFF) static int sixpack_rx(struct iface *ifp, __u8 c, __u8 *tnc_addr, frame_t *type) { int i, len; __u8 checksum; /* Is it a data octect? */ if (SIXP_IS_DATA(c)) { /* Decode the 6PACK octect and store the resultant * bits in the output buffer. */ switch (ifp->sixp_cnt % 4) { case 0: *ifp->optr = (c & 0x3F); break; case 1: *ifp->optr++ |= (c & 0x30) << 2; *ifp->optr = (c & 0x0F); break; case 2: *ifp->optr++ |= (c & 0x3C) << 2; *ifp->optr = (c & 0x03); break; default: *ifp->optr++ |= (c & 0x3F) << 2; } ifp->sixp_cnt++; return 0; } /* Nope, it's a command octect. See which kind of command. * Anything but a SEOF command is a one-octect command, so * process it immediately and return. */ if (SIXP_CMD(c) != SIXP_CMD_SEOF) { if (SIXP_CMD(c) == SIXP_CMD_ADDR) { /* 6PACK "TNC Address" commans are * "special". The address field must be * rewritten. */ if (PTY_ID_TTY == ifp->pty_id) { /* TNC Ring -> pty * Dequeue a TNC Address request. */ *ifp->cmdbuf = SIXP_MAKE_CMD(SIXP_CMD(c),1); *tnc_addr = cmd_addr_fifo[head]; head = (head + 1) % SIXP_MAX_ADDR; } else { /* pty -> TNC Ring * Enqueue a TNC Address request. */ *ifp->cmdbuf = SIXP_MAKE_CMD(SIXP_CMD(c), 0); *tnc_addr = 0; cmd_addr_fifo[tail] = ifp->pty_id; tail = (tail + 1) % SIXP_MAX_ADDR; } } else { /* We need a separate buffer here for 6PACK commands * as the protocol allows the transmission of command * frames in the middle of data frames. Don't touch * anything else! A data frame may be "en route". */ *ifp->cmdbuf = SIXP_CMD(c); *tnc_addr = SIXP_ADDR(c); } /* Update statistics and return. */ ifp->rxpackets++; ifp->rxbytes++; *type = command; return 1; } /* We're dealing with a SEOF command. */ len = ifp->optr - ifp->databuf; if (len > 0) { if (len < 3) { /* Data frames have at least 3 octets: * TxDelay, Datum, CheckSum. * Signal error and reset state. */ goto error_reset_state; } /* Now that we've decoded 6PACK octects, * check the checksum of the frame. */ checksum = 0; for (i = 0; i < len; i++) { checksum += ifp->databuf[i]; } /* Address of this frame must be taken from the * leading SEOF. */ *tnc_addr = SIXP_ADDR(ifp->seof); checksum += *tnc_addr; if (checksum != SIXP_CHKSUM) { /* Signal error and reset state. */ goto error_reset_state; } /* Now remove checksum from the frame (this makes * sixpack_tx easier). */ len--;/* Minus Checksum */ /* Finally update statistics and return all * needed parameters */ ifp->rxpackets++; ifp->rxbytes += len; *type = data; } ifp->optr = ifp->databuf; ifp->seof = c; ifp->sixp_cnt = 0; ifp->decod_cnt = 0; return len; error_reset_state: ifp->errors++; ifp->optr = ifp->databuf; ifp->sixp_cnt = 0; ifp->decod_cnt = 0; return 0; } static void sixpack_tx(int fd, __u8 tnc_addr, __u8 *ibuf, int len, frame_t type) { __u8 *ptr = obuf; __u8 checksum; int count, written; if (type == command) { if (SIXP_CMD(*ibuf) == SIXP_CMD_ADDR) { /* 6PACK "TNC Address" commans are * "special". We must send them untouched here. */ *ptr++ = *ibuf; } else { /* Commands, except SEOF, are 1 byte long. */ *ptr++ = SIXP_MAKE_CMD(SIXP_CMD(*ibuf), tnc_addr); } } else { *ptr++ = SIXP_MAKE_CMD(SIXP_CMD_SEOF, tnc_addr); checksum = 0; for (count = 0; count < len; count++) { switch (count % 3) { case 0: *ptr++ = (ibuf[count] & 0x3F); *ptr = (ibuf[count] & 0xC0) >> 2; break; case 1: *ptr++ |= (ibuf[count] & 0x0F); *ptr = (ibuf[count] & 0xF0) >> 2; break; default: *ptr++ |= (ibuf[count] & 0x03); *ptr++ = (ibuf[count] & 0xFC) >> 2; break; } checksum += ibuf[count]; } checksum += tnc_addr; checksum = 0xFF - checksum; switch (count % 3) { case 0: *ptr++ = (checksum & 0x3F); *ptr++ = (checksum & 0xC0) >> 2; break; case 1: *ptr++ |= (checksum & 0x0F); *ptr++ = (checksum & 0xF0) >> 2; break; default: *ptr++ |= (checksum & 0x03); *ptr++ = (checksum & 0xFC) >> 2; break; } /* Trailing SEOF doesn't carry TNC address */ *ptr++ = SIXP_MAKE_CMD(SIXP_CMD_SEOF, 0); } count = ptr - obuf; written = 0; ptr = obuf; while (count > 0) { written = write (fd, ptr, count); count -= written; ptr += written; } } static void sigterm_handler(int sig) { int i; if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } tty_unlock(tty->name); close(tty->fd); free(tty); for (i = 0; i < numptys; i++) { tty_unlock(pty[i]->name); close(pty[i]->fd); free(pty[i]); } exit(0); } static void sigusr1_handler(int sig) { signal(SIGUSR1, sigusr1_handler); dump_report = TRUE; } static void report(struct iface *tty, struct iface **pty, int numptys) { int i; long t; time(&t); syslog(LOG_INFO, "version %s.", FULL_VER); syslog(LOG_INFO, "Status report at %s", ctime(&t)); syslog(LOG_INFO, "ttyinterface is %s (fd=%d)", tty->name, tty->fd); for (i = 0; i < numptys; i++) syslog(LOG_INFO, "pty%d is %s (fd=%d)", i, pty[i]->name, pty[i]->fd); syslog(LOG_INFO, "Invalid ports: %d", invalid_ports); syslog(LOG_INFO, "Interface TX frames TX bytes RX frames RX " "bytes Errors"); syslog(LOG_INFO, "%-11s %-9u %-9lu %-9u %-9lu %u", tty->name, tty->txpackets, tty->txbytes, tty->rxpackets, tty->rxbytes, tty->errors); for (i = 0; i < numptys; i++) { syslog(LOG_INFO, "%-11s %-9u %-9lu %-9u %-9lu %u", pty[i]->name, pty[i]->txpackets, pty[i]->txbytes, pty[i]->rxpackets, pty[i]->rxbytes, pty[i]->errors); } } int main(int argc, char *argv[]) { __u8 *icp, tnc_addr; int topfd; fd_set readfd; int retval, i, size, len; int speed = -1; int ptmxdevices = 0; char *npts; int wrote_info = 0; frame_t type; head = tail = 0; while ((size = getopt(argc, argv, "ls:vx:")) != -1) { switch (size) { case 'l': logging = TRUE; break; case 's': speed = atoi(optarg); break; case 'x': ptmxdevices = atoi(optarg); if (ptmxdevices < 1 || ptmxdevices > MAX_PTYS) { fprintf(stderr, "m6pack: too %s devices\n", ptmxdevices < 1 ? "few" : "many"); return 1; } break; case 'v': printf("m6pack: %s\n", FULL_VER); return 1; case ':': case '?': fprintf(stderr, "%s", usage_string); return 1; } } if ((argc - optind) < 2 && ptmxdevices == 0) { fprintf(stderr, "%s", usage_string); return 1; } if ((argc - optind) < 1 && ptmxdevices > 0) { fprintf(stderr, "%s", usage_string); return 1; } numptys = argc - optind - 1; if (numptys + ptmxdevices > MAX_PTYS) { fprintf(stderr, "m6pack: max %d pty interfaces allowed.\n", MAX_PTYS); return 1; } /* * Check for lock files before opening any TTYs */ if (tty_is_locked(argv[optind])) { fprintf(stderr, "m6pack: tty %s is locked by another process\n", argv[optind]); return 1; } for (i = 0; i < numptys; i++) { if (!strcmp("/dev/ptmx", argv[optind + i + 1])) continue; if (tty_is_locked(argv[optind + i + 1])) { fprintf(stderr, "m6pack: pty %s is locked by another process\n", argv[optind + i + 1]); return 1; } } /* * Open and configure the tty interface. Open() is * non-blocking so it won't block regardless of the modem * status lines. */ tty = calloc(1, sizeof(struct iface)); if (tty == NULL) { perror("m6pack: malloc"); return 1; } tty->fd = open(argv[optind], O_RDWR | O_NDELAY); if (tty->fd == -1) { perror("m6pack: open"); return 1; } tty->name = argv[optind]; if (speed != -1) tty_speed(tty->fd, speed); tty_raw(tty->fd, FALSE); tty->pty_id = PTY_ID_TTY; tty->optr = tty->databuf; topfd = tty->fd; tty->namepts[0] = '\0'; /* * Make it block again... */ fcntl(tty->fd, F_SETFL, 0); /* * Open and configure the pty interfaces */ for (i = 0; i < numptys+ptmxdevices; i++) { static char name_ptmx[] = "/dev/ptmx"; char *pty_name = (i < numptys ? argv[optind+i+1] : name_ptmx); pty[i] = calloc(1, sizeof(struct iface)); if (pty[i] == NULL) { perror("m6pack: malloc"); return 1; } pty[i]->fd = open(pty_name, O_RDWR); if (pty[i]->fd == -1) { perror("m6pack: open"); return 1; } pty[i]->name = pty_name; tty_raw(pty[i]->fd, FALSE); pty[i]->pty_id = i; pty[i]->optr = pty[i]->databuf; topfd = (pty[i]->fd > topfd) ? pty[i]->fd : topfd; pty[i]->namepts[0] = '\0'; if (!strcmp(pty[i]->name, "/dev/ptmx")) { /* get name of pts-device */ npts = ptsname(pty[i]->fd); if (npts == NULL) { fprintf(stderr, "m6pack: Cannot get name of pts-device.\n"); return 1; } strncpy(pty[i]->namepts, npts, PATH_MAX-1); pty[i]->namepts[PATH_MAX-1] = '\0'; /* unlock pts-device */ if (unlockpt(pty[i]->fd) == -1) { fprintf(stderr, "m6pack: Cannot unlock pts-device %s\n", pty[i]->namepts); return 1; } if (wrote_info == 0) printf("\nAwaiting client connects on:\n"); else printf(" "); printf("%s", pty[i]->namepts); wrote_info = 1; } } if (wrote_info > 0) printf("\n"); numptys=numptys+ptmxdevices; /* * Now all the ports are open, lock them. */ tty_lock(tty->name); for (i = 0; i < numptys; i++) { if (pty[i]->namepts[0] == '\0') tty_lock(pty[i]->name); } if (logging) { openlog("m6pack", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting"); } if (wrote_info > 0) { fflush(stdout); fflush(stderr); close(0); close(1); close(2); } signal(SIGHUP, SIG_IGN); signal(SIGUSR1, sigusr1_handler); signal(SIGTERM, sigterm_handler); if (!daemon_start(FALSE)) { fprintf(stderr, "m6pack: cannot become a daemon\n"); return 1; } /* * Loop until an error occurs on a read. */ while (TRUE) { FD_ZERO(&readfd); FD_SET(tty->fd, &readfd); for (i = 0; i < numptys; i++) FD_SET(pty[i]->fd, &readfd); errno = 0; retval = select(topfd + 1, &readfd, NULL, NULL, NULL); if (retval == -1) { if (dump_report) { if (logging) report(tty, pty, numptys); dump_report = FALSE; continue; } else { perror("m6pack: select"); continue; } } /* * A character has arrived on the ttyinterface. */ if (FD_ISSET(tty->fd, &readfd)) { if ((size = read(tty->fd, ibuf, SIZE)) < 0 && errno != EINTR) { if (logging) syslog(LOG_ERR, "tty->fd: %m"); break; } for (icp = ibuf; size > 0; size--, icp++) { len = sixpack_rx(tty, *icp, &tnc_addr, &type); if (len != 0) { if (tnc_addr <= numptys) { sixpack_tx(pty[tnc_addr]->fd, 0, (type == data) ? tty->databuf : tty->cmdbuf, len, type); pty[tnc_addr]->txpackets++; pty[tnc_addr]->txbytes += len; } else invalid_ports++; } } } for (i = 0; i < numptys; i++) { /* * A character has arrived on pty[i]. */ if (FD_ISSET(pty[i]->fd, &readfd)) { if ((size = read(pty[i]->fd, ibuf, SIZE)) < 0 && errno != EINTR) { if (logging) syslog(LOG_ERR, "pty[%d]->fd: %m\n",i); goto end; } for (icp = ibuf; size > 0; size--, icp++) { len = sixpack_rx(pty[i], *icp, &tnc_addr, &type); if (len != 0) { sixpack_tx(tty->fd, i, (type == data) ? pty[i]->databuf : pty[i]->cmdbuf, len, type); tty->txpackets++; tty->txbytes += len; } } } } } end: if (logging) closelog(); tty_unlock(tty->name); close(tty->fd); free(tty); for (i = 0; i < numptys; i++) { tty_unlock(pty[i]->name); close(pty[i]->fd); free(pty[i]); } return 1; } AUTHORS000066400000000000000000000027011442776067000121430ustar00rootroot00000000000000axspawn Joerg Reuter DL1BKE axwrapper Tomi Manninen OH2BNS ax25ipd Rob Mayfield VK5XXX ax25rtd Klaus Kudielka bpqparms Joerg Reuter DL1BKE call Alexander Tietzel DG6XA mkiss Tomi Manninen OH2BNS kissnetd Frederic Rible F1OAT listen Heikki Hannikainen OH7LZB net2kiss Thomas Sailer HB9JNX node Tomi Manninen OH2BNS nodesave Tomi Manninen OH2BNS nrsdrv Dave Brown N2RJT piconfig John Paul Morrison VE7JPM pms David Brown N2RJT rxecho Tomi Manninen OH2BNS setcrystal Thomas Sailer HB9JNX sethdlc Thomas Sailer HB9JNX smdiag Thomas Sailer HB9JNX smmixer Thomas Sailer HB9JNX ttylinkd Craig Small VK2XLZ dmascc_cfg Klaus Kudielka xfhdlcchpar Thomas Sailer HB9JNX xfhdlcst Thomas Sailer HB9JNX xfsmdiag Thomas Sailer HB9JNX xfsmmixer Thomas Sailer HB9JNX yamcfg Jean-Paul Roubelat F6FBB All others Jonathon Naylor G4KLX COPYING000066400000000000000000000432541442776067000121360ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ChangeLog000066400000000000000000000103371442776067000126510ustar00rootroot00000000000000ax25-tools (0.0.10) * Version number 0.0.10 was used for several years in CVS. To avoid confusion we decieded to not make a 0.0.9 tarball release. * Fixed numerous build warnings and errors with modern distributions and compilers and other functional errors. * Support for UNIX98 pseudo terminals. * Support UNIX authentication for axspawn logins. * Axspawn now support Hufman compression. * An IP address is no longer mandatory for nrattach, kissattach. * kissparms now allows to send a raw value to the driver and to set the CRC mode by command line option. * axspawn now records the time at microsecond accuracy in utmp. * removed old kernel patches for yum support for kernels 2.0.36 and 2.2.1. In the unexpected case somebody should still need them the patches can be found in older releases and the source archive. * Add support for building rpm packages. * Add Tomi Manninen OH2BNS's axwrapper program. * Fix build issues with modern autoconf, automake and GCC. * For the tarball release Regenerate generates files with most recent autoconf and automake. * axparms --assoc now also accepts a numeric user id. * Some programs need FLTK-libraries (otherwise they're skipped on compile). * Fixed regression in axgetput (bget/bput crc was calculated wrong) * Simplification of CRC calculation. * Lots of stylistic cleanups to the code trying to follow the coding style of the kernel. * Updated to recent autoconf and automake version to keep the code buildable on modern distributions. * Improvements to git usage. * Fix possible buffer overflows. * Updates and corrections to man pages. * Code cleanups all across the tree. * Move ax25-tools-docs package back into ax25-tools and ax25-tools-x packages. -- Ralf Baechle DL5RB Sat, 6 Jun 2015 14:29:07 +0200 -- Thomas Osterried Fri, 12 Feb 2016 14:31:33 +0100 ax25-tools (0.0.9) * Fix for mkiss pid problem * Removed inline statesment in hdrvcomm.c * Added remaining missing sys/time.h * Netromd now uses PF_SOCKET * Changed sys_errlist[] to strerror() -- Craig Small NO RELEASE YET ax25-tools (0.0.8) * kissattach now tells you where it thinks the config file is * rip98r metric patch form Jean-Paul * Fix for FTLK_LIB for configure.in from Hans * Works better with newer GCCs (thanks again to Hans) * Fixed sockaddr/osockaddr problem in ttylink * Changed bcopy and bzero to memcpy and memset * netromd was printing raw callsigns, now use ax25_ntoa() * synced dmascc_cfg to Klauss' latest -- Craig Small ax25-tools (0.0.7) * Fixed automake/autoconf stuff to sort out FLTK tools. -- Craig Small Thu, 26 Oct 2000 14:19:32 +1100 ax25-tools (0.0.6) * Added the new fltk smdiag and friends and put the old non-X based ones back in. * Changed all references of rip98.conf to rip98d.conf * Attempted to hunt down and change all my email addresses * Another Tomi's patch for nrparms and user_call -- Craig Small Tue Jan 4 15:13:15 EST 2000 ax25-tools (0.0.5) * Added Tomi's patch to correctly detect broken headers -- Craig Small Mon, 23 Aug 1999 12:41:12 +1000 ax25-tools (0.0.4) * Changed kissattach so that it demands an IP address * The hdlc stuff now has -i in it * mheardd.c patch for old glibc headers * kissattach.c basename changed to kiss_bname due to conflict * configure checks for linux/dmascc.h * kiss/Makefile.am uses ln -sf instead of ln -s --Craig Small Tue, 10 Aug 1999 11:44:19 +1000, ax25-tools (0.0.3) * Fixed configure.in so it doesn't check for libax25io * Added dmascc configuration program * Added spattach * Added yamcfg * Removed nrports, axports and rsports, they are in the library * Had another crack at getting config locations portable -- Craig Small 30 June 1999 ax25-tools (0.0.2) * First public release -- Craig Small 21 April 1999 ax25-tools (0.0.1) * Initial version from Terry Dawson's code, split from the old ax25-utils package. Uses new ntoa aton calls. -- Craig Small Makefile.am000066400000000000000000000014431442776067000131310ustar00rootroot00000000000000 installconf: @for app in $(SUBDIRS); do $(MAKE) -C $$app installconf; done SUBDIRS = ax25 hdlcutil kiss 6pack netrom rose tcpip user_call yamdrv dmascc EXTRA_DIST = pathnames.h scm-version.h.in ax25-tools.spec AM_CPPFLAGS = -D_GNU_SOURCE \ -DAX25_SYSCONFDIR=\""$(sysconfdir)/ax25/"\" \ -DAX25_LOCALSTATEDIR=\""$(localstatedir)/ax25/"\" AX25_SYSCONFDIR=@sysconfdir@/ax25/ AX25_LOCALSTATEDIR=@localstatedir@/ax25/ BUILT_SOURCES = scm-version.h dist_noinst_SCRIPTS = setlocalversion .PHONY: scm-version.h scm-version.h: sed -e 's/@RELEASE_VER@/@VERSION@/' \ -e 's/@SCM_VER@/'$$(./setlocalversion)'/' \ scm-version.h.in > scm-version.h.tmp && \ (cmp -s scm-version.h.tmp scm-version.h 2>/dev/zero || \ mv scm-version.h.tmp scm-version.h) rm -f scm-version.h.tmp NEWS000066400000000000000000000000431442776067000115670ustar00rootroot00000000000000This is some news about ax25-tools README000066400000000000000000000005611442776067000117550ustar00rootroot00000000000000AX25 Tools ========== Note that kissattach has changed its command line format slight. While the old format will work, the -i option is now, err, not optional. This was done to stop the million or so emails we get on linux-hams email list saying why do people get axports unconfigured even after a kissattach command. - Craig Small README.compile000066400000000000000000000004251442776067000134030ustar00rootroot00000000000000After checkout: autoreconf --install --force ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man Compile: make Onle once (if you did not have configuration files in /etc; if you have, they will be overwritten!): make installconf ax25-tools.spec.in000066400000000000000000000136071442776067000143000ustar00rootroot00000000000000Name: ax25-tools Version: @BASEVERSION@.@EXTRAVERSION@ Release: 1%{?dist} Summary: Non-GUI tools used to configure an AX.25 enabled computer Obsoletes: ax25-tools-docs < 0.0.10.rc5 License: GPLv2+ URL: http://www.linux-ax25.org/ Source0: http://www.linux-ax25.org/pub/%{name}/%{name}-@VERSION@.tar.gz BuildRoot: %{_tmppath}/%{name}-@VERSION@-%{release}-root-%(%{__id_u} -n) BuildRequires: gcc gcc-c++ BuildRequires: libax25-devel BuildRequires: fltk-devel %description ax25-tools is a collection of tools that are used to configure an ax.25 enabled computer. They will configure interfaces and assign callsigns to ports as well as Net/ROM and ROSE configuration. This package only contains the command line programs; the GUI programs are contained in ax25-tools-x package. * m6pack - handle multiple 6pack TNCs on a single interface * ax25d - general purpose AX.25, NET/ROM and Rose daemon * axctl - configure/Kill running AX.25 connections * axparms - configure AX.25 interfaces * axspawn - allow automatic login to a Linux system * axwrapper - run non-ax.25-aware programs from ax25d * beacon - transmit periodic messages on an AX.25 port * bpqparms - configure BPQ ethernet devices * mheardd - display AX.25 calls recently heard * rxecho - transparently route AX.25 packets between ports * mheard - collect information about packet activity * dmascc_cfg - configure dmascc devices * sethdlc - get/set Linux HDLC packet radio modem driver port information * smmixer - get/set Linux soundcard packet radio modem driver mixer * kissattach - Attach a KISS or 6PACK interface * kissnetd - create a virtual network * kissparms - configure KISS TNCs * mkiss - attach multiple KISS interfaces * net2kiss - convert a network AX.25 driver to a KISS stream on a pty * netromd - send and receive NET/ROM routing messages * nodesave - saves NET/ROM routing information * nrattach - start a NET/ROM interface * nrparms - configure a NET/ROM interface * nrsdrv - KISS to NET/ROM serial converter * rsattach - start a ROSE interface * rsdwnlnk - user exit from the ROSE network * rsmemsiz - monitor the ROSE subsystem * rsusers.sh - monitor AX.25, NET/ROM and ROSE users * rsparms - configure a ROSE interface * rsuplnk - User entry into the ROSE network * rip98d - RIP98 routing daemon * ttylinkd - TTYlink daemon for AX.25, NET/ROM, ROSE and IP * ax25_call - Make an AX.25 connection * netrom_call - Make a NET/ROM connection * rose_call - Make a ROSE connection * tcp_call - Make a TCP connection * yamcfg - configure a YAM interface %package x Summary: Tools used to configure an AX.25 enabled computer Requires: %{name} = %{version}-%{release} Obsoletes: ax25-tools-docs < 0.0.10.rc5 %description x ax25-tools is a collection of tools that are used to configure an ax.25 enabled computer. This package contains the GUI programs to configure Baycom modem and sound modem. * smdiag - Linux soundcard packet radio modem driver diagnostics utility * xfhdlcchpar - kernel HDLC radio modem driver channel parameter utility * xfhdlcst - kernel HDLC radio modem driver status display utility * xfsmdiag - kernel soundcard radio modem driver diagnostics utility * xfsmmixer - kernel soundcard radio modem driver mixer utility %prep %setup -q -n %{name}-@VERSION@ %build %configure make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT dist_doc_DATA= install make DESTDIR=$RPM_BUILD_ROOT installconf %clean rm -rf $RPM_BUILD_ROOT %files %doc AUTHORS ChangeLog COPYING README %doc yamdrv/README.yamdrv user_call/README.user_call tcpip/ttylinkd.README dmascc/README.dmascc %doc ax25/axgetput/TIPS.txt ax25/axgetput/README.axgetput %{_bindir}/axgetput %{_bindir}/bget %{_bindir}/bput %{_bindir}/mheard %{_bindir}/sethdlc %{_bindir}/smmixer %{_sbindir}/ax25_call %{_sbindir}/ax25d %{_sbindir}/axctl %{_sbindir}/axparms %{_sbindir}/axspawn %{_sbindir}/axwrapper %{_sbindir}/beacon %{_sbindir}/bpqparms %{_sbindir}/dmascc_cfg %{_sbindir}/kissattach %{_sbindir}/kissnetd %{_sbindir}/kissparms %{_sbindir}/m6pack %{_sbindir}/mcs2h %{_sbindir}/mheardd %{_sbindir}/mkiss %{_sbindir}/net2kiss %{_sbindir}/netrom_call %{_sbindir}/netromd %{_sbindir}/nodesave %{_sbindir}/nrattach %{_sbindir}/nrparms %{_sbindir}/nrsdrv %{_sbindir}/rip98d %{_sbindir}/rose_call %{_sbindir}/rsattach %{_sbindir}/rsdwnlnk %{_sbindir}/rsmemsiz %{_sbindir}/rsparms %{_sbindir}/rsuplnk %{_sbindir}/rsusers.sh %{_sbindir}/rxecho %{_sbindir}/spattach %{_sbindir}/tcp_call %{_sbindir}/ttylinkd %{_sbindir}/yamcfg %config(noreplace) %{_sysconfdir}/ax25/ax25.profile %config(noreplace) %{_sysconfdir}/ax25/ax25d.conf %config(noreplace) %{_sysconfdir}/ax25/axspawn.conf %config(noreplace) %{_sysconfdir}/ax25/nrbroadcast %config(noreplace) %{_sysconfdir}/ax25/rip98d.conf %config(noreplace) %{_sysconfdir}/ax25/rxecho.conf %config(noreplace) %{_sysconfdir}/ax25/ttylinkd.conf %dir %{_localstatedir}/ax25 %dir %{_localstatedir}/ax25/mheard %config(noreplace) %{_localstatedir}/ax25/mheard/mheard.dat %{_mandir}/man?/* %exclude %{_mandir}/man8/smdiag.8* %files x %doc COPYING %{_bindir}/smdiag %{_sbindir}/xfhdlcchpar %{_sbindir}/xfhdlcst %{_sbindir}/xfsmdiag %{_sbindir}/xfsmmixer %{_mandir}/man8/smdiag.8* %changelog * Tue Apr 2 2019 Ralf Baechle - Move doc fils back into the main packages, that seems to be more standard these days. Suggested by Richard Shaw. * Tue Jun 4 2013 Ralf Baechle - Move doc files to separate docs package. - Reset Release to 1. * Sat Jun 25 2011 Ralf Baechle - Add BuildRequires to libax25-devel, libXext-devel and libX11-devel. - Move smdiag, xfhdlcchpar, xfhdlcst, xfsmdiag and xfsmmixer into a separate package. - Bump Release to 4. * Fri Jun 24 2011 Ralf Baechle - Add BuildRequires to libXi-devel and fltk-devel - Bump Release to 2. * Sat Jun 27 2009 Ralf Baechle - Initial version ax25/000077500000000000000000000000001442776067000116525ustar00rootroot00000000000000ax25/.gitignore000066400000000000000000000001671442776067000136460ustar00rootroot00000000000000*.o .deps Makefile Makefile.in ax25d /ax25d.conf axctl axparms axspawn axwrapper beacon bpqparms mheard mheardd rxecho ax25/Makefile.am000066400000000000000000000034611442776067000137120ustar00rootroot00000000000000SUBDIRS = axgetput etcfiles = ax25.profile ax25d.conf axspawn.conf rxecho.conf etcsrcfiles = ax25.profile ax25d.conf.in axspawn.conf rxecho.conf noinst_DATA = $(etcfiles) varfiles = mheard.dat subst_path = sed -e 's|[@]SBINDIR[@]|$(sbindir)|' \ -e 's|[@]SYSCONFDIR[@]|$(sysconfdir)|' ax25d.conf: ax25d.conf.in $(subst_path) < $(srcdir)/ax25d.conf.in > ax25d.conf.tmp mv ax25d.conf.tmp ax25d.conf installconf: $(mkinstalldirs) $(DESTDIR)$(AX25_SYSCONFDIR) @list='$(etcfiles)'; for p in $$list; do \ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p"; \ $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p; \ done $(mkinstalldirs) $(DESTDIR)$(AX25_LOCALSTATEDIR)/mheard @list='$(varfiles)'; for p in $$list; do \ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_LOCALSTATEDIR)/mheard/$$p"; \ $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_LOCALSTATEDIR)/mheard/$$p; \ done sbin_PROGRAMS = ax25d axctl axparms axspawn axwrapper beacon bpqparms mheardd \ rxecho bin_PROGRAMS = mheard LDADD = $(AX25_LIB) axspawn_LDADD = $(AX25_LIB) $(UTIL_LIB) AM_CPPFLAGS = -D_GNU_SOURCE \ -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" AX25_SYSCONFDIR=$(sysconfdir)/ax25/ AX25_LOCALSTATEDIR=$(localstatedir)/ax25/ dist_man_MANS = ax25.4 ax25d.conf.5 axspawn.conf.5 rxecho.conf.5 \ ax25d.8 axctl.8 axparms.8 axspawn.8 axwrapper.8 beacon.8 \ bpqparms.8 mheard.1 mheardd.8 rxecho.8 CLEANFILES = ax25d.conf EXTRA_DIST = $(etcsrcfiles) $(varfiles) ax25d_SOURCES = ax25d.c axctl_SOURCES = axctl.c axparms_SOURCES = axparms.c axspawn_SOURCES = axspawn.c axspawn.h access.c access.h md5.c md5.h axwrapper_SOURCES = axwrapper.c beacon_SOURCES = beacon.c bpqparms_SOURCES = bpqparms.c mheard_SOURCES = mheard.c mheardd_SOURCES = mheardd.c rxecho_SOURCES = rxecho.c ax25/access.c000066400000000000000000000240341442776067000132620ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "access.h" #include "md5.h" #include "axspawn.h" #define CONV_RAND_MAX 0x7fffffff #define SYSTEMPW 0 #define USERPW 1 static long seed = 1L; /*--------------------------------------------------------------------------*/ static int conv_rand(void) { seed = (1103515245L * seed + 12345) & CONV_RAND_MAX; return (int) (seed & 077777); } /*--------------------------------------------------------------------------*/ static void conv_randomize(void) { seed = (time(NULL) & CONV_RAND_MAX); } /*--------------------------------------------------------------------------*/ static int conv_random(int num, int base) { return ((long) (conv_rand() * time(NULL)) & CONV_RAND_MAX) % num + base; } /*--------------------------------------------------------------------------*/ static void char_to_hex(char *c, char *h, int n) { int i; static char *hextable = "0123456789abcdef"; for (i = 0; i < n; i++) { *h++ = hextable[(*c>>4)&0xf]; *h++ = hextable[*c++ &0xf]; } *h = '\0'; } /*--------------------------------------------------------------------------*/ static char *generate_rand_pw(int len) { static char pass[PASSSIZE+1]; int i, j; pass[0] = 0; if (seed == 1L) conv_randomize(); if (len < 1) return pass; if (len > PASSSIZE) len = PASSSIZE; for (i = 0; i < len; i++) { j = conv_random(10+26*2, 0); if (j < 10) { pass[i] = j + '0'; continue; } j -= 10; if (j < 26) { pass[i] = j + 'A'; continue; } j -= 26; pass[i] = j + 'a'; } pass[len] = 0; return pass; } /*--------------------------------------------------------------------------*/ static void calc_md5_pw (const char *MD5prompt, const char *MD5pw, char *MD5result) { MD5_CTX context; short i, n, len; char buffer[1024]; strncpy(buffer, MD5prompt, 10); buffer[10] = 0; strcat(buffer, MD5pw); MD5Init(&context); len = strlen(buffer); for (i= 0; i < len; i += 16) { n = (len - i) > 16 ? 16 : (len - i); MD5Update(&context, (unsigned char *) buffer+i, n); } MD5Final(&context); MD5result[0] = '\0'; for (i = 0; i < 16; i++) { MD5result[i] = context.digest[i]; } } /*--------------------------------------------------------------------------*/ void ask_pw_sys(char *prompt, char *pass_want, char *pw) { char buffer[2048]; int five_digits[5]; int pwlen; int i, j; pass_want[0]= 0; if (!pw || !*pw) return; pwlen = strlen(pw); if (seed == 1L) conv_randomize(); for (i = 0; i < 5; i++) { int k; again: j = conv_random(pwlen, 0); /* store generated request-numbers */ five_digits[i] = j+1; /* pos0 refers as 1 */ /* same number again? */ for (k = 0; k < i; k++) { if (five_digits[k] == five_digits[i]) goto again; } /* store expected string in cp->passwd */ pass_want[i] = pw[j]; } /* and terminate the string */ pass_want[i] = 0; sprintf(buffer, "\r%s> %d %d %d %d %d\r", prompt, five_digits[0], five_digits[1], five_digits[2], five_digits[3], five_digits[4]); write_ax25(buffer, strlen(buffer), 1); } /*--------------------------------------------------------------------------*/ void ask_pw_md5(char *prompt, char *pass_want, char *pw) { #define SALT_LEN 10 char buffer[2048]; char cipher[16]; char key[256]; char *challenge; pass_want[0]= 0; if (!pw || !*pw) return; if (seed == 1L) conv_randomize(); strncpy(key, pw, sizeof(key)); key[sizeof(key)-1] = 0; /* compute random salt */ challenge = generate_rand_pw(SALT_LEN); /* ask for proper response to this challenge: */ sprintf(buffer, "\r%s> [%s]\r", prompt, challenge); write_ax25(buffer, strlen(buffer), 1); /* compute md5 challenge */ calc_md5_pw(challenge, key, cipher); /* store expected answer */ char_to_hex(cipher, pass_want, 16); } /*--------------------------------------------------------------------------*/ static void write_example_passwd(char *pwfile, char pwlocation, struct passwd *pw) { FILE * f; int i; i = open(pwfile, O_CREAT | O_WRONLY | O_TRUNC, (S_IRUSR | S_IWUSR | (pwlocation == SYSTEMPW ? (S_IRGRP /* | S_IWGRP */) : 0))); if (i == -1) return; fchown(i, (pwlocation == SYSTEMPW ? 0 : pw->pw_uid), (pwlocation == SYSTEMPW ? 0 : pw->pw_gid)); close(i); f = fopen(pwfile, "w"); if (!f) return; fprintf(f, "# %s Password file for axspawn\n", (pwlocation == SYSTEMPW ? "System" : "User")); if (pwlocation == SYSTEMPW) { fprintf(f, "# disable user self-administered passwords in $HOME/.%s\n", PWFILE); fprintf(f, "# with the line \"systempasswordonly\"\n"); fprintf(f, "# systempasswordonly\n"); } fprintf(f, "# Examples (sys and md5 passwords may differ):\n"); fprintf(f, "# md5 standard (secure) - length: >= %d and <= %d characters\n", MINPWLEN_MD5, PASSSIZE); fprintf(f, "# %smd5:%s\n", (pwlocation == SYSTEMPW ? "username:" : ""), generate_rand_pw(MINPWLEN_MD5)); fprintf(f, "# sys/baycom standard (not very secure) - length: >= %d and <= %d characters\n", MINPWLEN_SYS, PASSSIZE); fprintf(f, "# %ssys:%s\n", (pwlocation == SYSTEMPW ? "username:" : ""), generate_rand_pw(MINPWLEN_SYS)); fprintf(f, "# unix standard (plaintext): no password is read here. Your password is looked\n"); fprintf(f, "# up during login in the system password table /etc/passwd or /etc/shadow\n"); fprintf(f, "# unix\n"); fclose(f); } /*--------------------------------------------------------------------------*/ char *read_pwd (struct passwd *pw, int *pwtype) { FILE * f = NULL; struct stat statbuf; char pwfile[PATH_MAX + 1]; int len; char pwlocation; char buf[2048]; int only_systempw = 0; char *pass = NULL; char *p_buf; char *p; for (pwlocation = 0; pwlocation < 2; pwlocation++) { if (pwlocation == SYSTEMPW) { sprintf(pwfile, "/%s/%s", AX25_SYSCONFDIR, PWFILE); if (stat(pwfile, &statbuf)) { write_example_passwd(pwfile, pwlocation, pw); continue; } if (!S_ISREG(statbuf.st_mode) || (statbuf.st_mode & (S_IROTH | S_IWOTH))) continue; f = fopen(pwfile, "r"); if (!f) continue; } else { if (only_systempw) goto end; snprintf(pwfile, sizeof(pwfile), "%s/.%s", pw->pw_dir, PWFILE); pwfile[sizeof(pwfile)-1] = 0; if (stat(pwfile, &statbuf)) { sprintf(buf, "Notice: No .%s file found in your homedirectory (for more secure\r", PWFILE); write_ax25(buf, strlen(buf), 1); sprintf(buf, " password authentication than plaintext). Generating example file,\r"); write_ax25(buf, strlen(buf), 1); sprintf(buf, " with unique passwords (which may be changed).\r"); write_ax25(buf, strlen(buf), 1); sprintf(buf, " Please edit ~/.%s, and enable your preferred authentication type;\r", PWFILE); write_ax25(buf, strlen(buf), 1); sprintf(buf, " MD5 is recommended.\r"); write_ax25(buf, strlen(buf), 1); write_example_passwd(pwfile, pwlocation, pw); goto end; } if (!S_ISREG(statbuf.st_mode)) { sprintf(buf, "Error: password file .%s should be a regular file. Skipping..\r", PWFILE); write_ax25(buf, strlen(buf), 1); goto end; } if (statbuf.st_uid != 0 && statbuf.st_uid != pw->pw_uid) { sprintf(buf, "Error: your password file .%s is not owned by you. Skipping..\r", PWFILE); write_ax25(buf, strlen(buf), 1); goto end; } if ((statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))) { sprintf(buf, "WARNING: your password file .%s has wrong permissions.\r", PWFILE); write_ax25(buf, strlen(buf), 1); sprintf(buf, " Please change it with\r"); write_ax25(buf, strlen(buf), 1); sprintf(buf, " chmod 600 .%s\r", PWFILE); write_ax25(buf, strlen(buf), 1); sprintf(buf, " and don't forget to change your password stored in .%s\r", PWFILE); write_ax25(buf, strlen(buf), 1); sprintf(buf, " because it may be compromised.\r"); write_ax25(buf, strlen(buf), 1); /* go on.. if user takes no action, he always gets this message */ } f = fopen(pwfile, "r"); if (!f) goto end; } for (;;) { if (!fgets(buf, sizeof(buf), f)) { fclose(f); if (pwlocation == SYSTEMPW) break; /* perhaps this is too irritating for the user when the message occurs always. Thus only write the notice, if cleartext fallback is disabled by administrative configuration. */ if (!((*pwtype) & PW_CLEARTEXT)) { sprintf(buf, "Failed to find a suitable password in %s\r", pwfile); write_ax25(buf, strlen(buf), 1); } goto end; } p = strchr(buf, '\n'); if (p) *p = 0; if (!*buf || isspace(*buf & 0xff)) continue; if (*buf == '#') continue; if (pwlocation == SYSTEMPW) { if (!Strcasecmp(buf, "systempasswordonly")) { only_systempw = 1; continue; } p = strchr(buf, ':'); if (!p) continue; *p++ = 0; if (strcmp(pw->pw_name, buf)) continue; p_buf = p; } else { p_buf = buf; } if (!Strcasecmp(p_buf, "unix")) { pass = p_buf; } else { pass = strchr(p_buf, ':'); if (!pass) continue; *pass++ = 0; } while (*pass && isspace(*pass & 0xff)) pass++; for (p = pass; *p && !isspace(*p & 0xff); p++) ; *p = 0; if ( (*pwtype & PW_MD5) && !Strcasecmp(p_buf, "md5") ) { fclose(f); *pwtype = PW_MD5; goto found; } else if ( (*pwtype & PW_SYS) && (!Strcasecmp(p_buf, "sys") || !strcmp(p_buf, "baycom")) ) { fclose(f); *pwtype = PW_SYS; goto found; } else if ( (*pwtype & PW_UNIX) && (!Strcasecmp(p_buf, "unix") ) ) { fclose(f); *pwtype = PW_UNIX; return NULL; } } } found: if (!pass || !*pwtype) goto end; len = strlen(pass); if ((*pwtype == PW_SYS && len < MINPWLEN_SYS) || (*pwtype == PW_MD5 && len < MINPWLEN_MD5)) { sprintf(buf, "Password in password file too short\r"); write_ax25(buf, strlen(buf), 1); goto end; } if (strlen(pass) > PASSSIZE) pass[PASSSIZE] = 0; return strdup(pass); end: *pwtype = (((*pwtype) & PW_CLEARTEXT) ? PW_CLEARTEXT : 0); /* ^ may allow cleartext? - then cleartext, else deny */ return NULL; } ax25/access.h000066400000000000000000000007201442776067000132630ustar00rootroot00000000000000#ifndef ACCESS_H #define ACCESS_H #define PASSSIZE 80 /* for md5 passwords, at least 32 characters */ #define MINPWLEN_SYS 20 #define MINPWLEN_MD5 8 #define PWFILE "bcpasswd" /* PWxxx: set bits */ #define PW_CLEARTEXT 1 #define PW_SYS 2 #define PW_MD5 4 #define PW_UNIX 8 void ask_pw_sys(char *prompt, char *pass_want, char *pw); void ask_pw_md5(char *prompt, char *pass_want, char *pw); char *read_pwd (struct passwd *pw, int *pwtype); #endif ax25/ax25.4000066400000000000000000000063431442776067000125240ustar00rootroot00000000000000.TH AX25 4 "3 August 2017" Linux "Linux Programmer's Manual" .SH NAME AF_AX25 \- AX.25 amateur packet radio protocol family .SH DESCRIPTION .LP .B AX.25 is a protocol used extensively by radio amateurs. The Linux AX.25 protocol family permits access to these protocols via the standard networking .B socket metaphor. .LP The AX.25 protocol layer supports both connected mode and datagram (UI) frame modes. IP traffic may be stacked on top of AX.25 frames for IP transmission over the AX.25 medium. .LP The primary mode of operation is connected mode which is the mode used for a socket of type .B SOCK_SEQPACKET (stream sockets are not available in AX.25). This requires that the user ensures output data is suitably packetised, and that input data is read a packet at a time into a buffer of suitable size. The Linux AX.25 protocol layer can operate in standard AX.25 mode with three bit sequence numbers or in PE1CHL extended AX.25 mode which uses seven bit sequence numbers. The protocol passed to the socket is used for all outgoing frames. Passing 0 causes the normal AX.25 Text PID to be used. .LP .B SOCK_DGRAM gives access to AX.25 UI frames. For access to special frames (of any form) SOCK_RAW can be used. .LP AX.25 addresses consist of 6 .B ASCII characters and a number called the SSID. These are encoded into a sockaddr_ax25 structure which is provided to the relevant system calls. When digipeaters are included a callsign path can be much more complex. When this is the case a struct full_sockaddr_ax25 should be passed to the system calls. .LP AX.25 has some unusual properties. Notably in a multi-user system an AX.25 address is often associated with a user, and some users may not have such an association. a set of ioctl calls are provided to manage an association table, and in addition the superuser may use an arbitrary callsign by binding to the callsign desired and specifying the port to use as a first digipeated hop. .LP AX.25 supports the following socket options for .BR SOL_AX25 . .B AX25_T1 is the T1 timer in 1/10ths of a second, AX25_T2 is the T2 timer in 1/10ths of a second, .B AX25_T3 is the T3 timer. The window is settable with .BR AX25_WINDOW . .BR AX25_N2 , the retry counter is also configurable. There is no 'infinite retry' option supported however. The method of backoff for retries is configurable via the socket option .BR AX25_BACKOFF , a value of true indicates the use of exponential backoff and false simple linear backoff. The mode of a connection made be altered to be either standard AX.25 or extended AX.25 via .BR AX25_EXTSEQ . It is possible to have the complete AX.25 header returned to the application by setting .B AX25_HDRINCL to true, programs must be aware of the internal structure of AX.25 frames to use this option. Note that if AX.25 fragmentation is encountered, only the control information of the first frame is returned along with the defragmented data. .SH "SEE ALSO" .BR call (1), .BR socket (2), .BR setsockopt (2), .BR getsockopt (2), .BR axctl (8), .BR axports (5), .BR axparms (8), .BR packet (7), .BR kissattach (8). .LP .SH BUGS .LP Too numerous to list in full currently. .TP 3 \(bu Minor protocol violations exist. .SH AUTHOR .nf Alan Cox GW4PTS .br Jonathan Naylor G4KLX .fi ax25/ax25.profile000066400000000000000000000000461442776067000140130ustar00rootroot00000000000000#echo "/char ibmpc ibmpc" >.conversrc ax25/ax25d.8000066400000000000000000000024031442776067000126650ustar00rootroot00000000000000.TH AX25D 8 "25 May 2015" Linux "Linux System Managers Manual" .SH NAME ax25d \- General purpose AX.25, NET/ROM and Rose daemon .SH SYNOPSIS .B ax25d [-v] [-c altconffile] [-l] .SH DESCRIPTION .LP .B Ax25d is a general purpose server daemon that listens on a number of AX.25, NET/ROM and Rose ports and offers different services depending upon port, callsign and other parameters. .B Ax25d is driven by a complex configuration file, a full description of which may be found in another manual page. .sp 1 .B AX25d has the facility to log information about incoming connections to the system log file. By default no logging is done. When .B ax25d is running, and a change in the configuration file is made, .B ax25d can be forced to re-read its configuration file by sending it a SIGHUP. .SH OPTIONS .TP 15 .BI "\-c altconffile" Specifies an alternate configuration file name. .TP 15 .BI \-l Specifies that messages should be logged into the system log file. By default no messages are logged. .TP 15 .BI \-v Display the version. .SH FILES .LP /etc/ax25/ax25d.conf .SH "SEE ALSO" .BR kill (1), .BR ax25 (4), .BR ax25wrapper (8), .BR netrom (4), .BR rose (4), .BR ax25d.conf (5). .SH AUTHOR Darryl Miles G7LED .br Jonathan Naylor G4KLX ax25/ax25d.c000066400000000000000000001066451442776067000127550ustar00rootroot00000000000000/* * This is my version of axl.c, written for the LBBS code to make it * compatible with the kernel AX25 driver. It appears to work, with * my setup, so it'll probably not work else where :-). * * This was inspired by the example code written by Alan Cox (GW4PTS) * axl.c in AX25USER.TGZ from sunacm.swan.ac.uk. * * * Copyright (C) 1995, 1996 by Darryl L. Miles, G7LED. * Copyright (C) 1996 by Jonathan Naylor G4KLX * * 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. * * * * Just a quickie Feb 1995. * It *was* just a quickie (at the time) but you'd know how these things * just... Apr 1995. * * If your AX25/NETROM system is relying on this code for * securetty/firewalling then please be aware this has been coded * with the intent on striving on through system/(mis)configuration * errors in the hope that at worst it will run with a degraded * service. Rather than leave your system providing no service at * all, if opinions require the old behavior back when let me know * and I'll #ifdef it in. * * * History: * * 1.0 Feb 1995 Basic AX25 listening daemon, Multi-port, Call * matching, etc... * * 1.1 Feb 1995 Moved entry scanning before fork(). * Added setgroups() to plug security hole. * Minor fixes + Improved handling. * * 1.2 Apr 1995 NETROM support added from developing AX25 * 028b/029. * Added 'defaults' port setting. * Added FLAG_NODIGIS. * * 1.3 Jul 1995 Make it a little more intelligent about what to * do with errors. * Added exec and argv[0] as two different fields, * much like inetd uses. * * 1.4 Aug 1995 Confirmed support for AX25 030 (1.3.20 + hacks), * it appears to work. * It will now bootup even if initial config errors * occur when setting up and binding (e.g. port(s) * down), it will skip the port(s) with a problem * and listen out on those which are left standing. * * 1.5 Aug 1995 Updated old (buggy) libax25.a function copies in axl. * Causing all sorts of problems. * * 1.6 Aug 1995 Reset the 'defaults' entry's when we start parsing * a new interface. * * 1.7 Dec 1995 Added BROKEN_NETROM_KERNEL define for setsockopt. * * 1.8 Jan 1996 Added support for AX25_BIND_ANY_DEVICE, specify just * [CALL-X VIA *]. * Better param parsing, T1 and T2 now using the real * time in seconds as params, and not kernel units. * Connection loggin added, either via it's own logfile * or syslog. * Modified 'defaults' to 'parameters'. * * 1.9 Jun 1996 Reworked config file parsing to use port names instead * of callsigns. Reformated source code. * * Under alpha: * BPQ like clever mode called for.... also a mode that * requires a packet to kick application open. * A flag/mode which will cause a call to initgroups() * based on uid. * Callsign validation check. * Logging of errors. * Handling of AX25.IP mode VC connections - dl9sau * recommended settings in ax25d.conf: * parameters_extAX25 VC-wait-login VC-disc-on-linkfailure-msg VC-log-connections or * parameters_extAX25 VC-reject-login VC-send-failure-msg VC-log-connections * * * TODO: * The timing of the 'accept()' might be changed, defered to the * child, then that child fork() itself, to stop race conditions * around 'accept()'. * Add a config file to allow/disallow connections/services at * different times of the day to restrict access say. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" /* Maximum number of command line arguments for the application we run */ #define MAX_ARGS 32 #define FLAG_VALIDCALL 0x01 /* NOTUSED */ #define FLAG_NOLOGGING 0x02 /* Don't log this connection */ #define FLAG_CHKNRN 0x04 /* Check NetRom Neighbour - NOTUSED */ #define FLAG_NODIGIS 0x08 /* Disallow digipeated uplinks */ #define FLAG_LOCKOUT 0x10 /* Disallow connection */ struct axlist { /* Have used same struct for quickness */ struct axlist *next; /* Port list */ char *port; /* Port call, only set across the port list */ int fd; /* The listening socket fd */ int af_type; /* AF_AX25, AF_NETROM or AF_ROSE port */ struct axlist *ents; /* Exec line entries */ char *call; /* Call in listing entries */ char *node; /* Node call in listing entries */ uid_t uid; /* UID to run program as */ gid_t gid; /* GID to run program as */ char *exec; /* Real exec */ char *shell; /* Command line. With possible escapes. */ unsigned int window; /* Set window to... */ unsigned long t1; /* Set T1 to... (Retrans timer) */ unsigned long t2; /* Set T2 to... (Ack delay) */ unsigned long t3; /* Set T3 to... (Idle Poll timer) */ unsigned long idle; /* Set T4 to... (Link Drop timer) */ unsigned long n2; /* Set N2 to... (Retries) */ unsigned long flags; /* FLAG_ values ORed... */ int checkVC; /* check for AX25.IP mode VC users? */ int VCdiscOnLinkfailureMsg; /* action when mode VC user and checkVC == 1 */ int VCsendFailureMsg; /* action when mode VC user and on checkVC > 1 */ int VCloginEnable; /* trigger login when mode VC user and checkVC == 1 and one line is read */ int LoggingVC; /* extra log stuff */ }; static struct axlist *AXL; static char *ConfigFile = CONF_AX25D_FILE; static char User[10]; /* Room for 'GB9ZZZ-15\0' */ static char Node[11]; /* Room for 'GB9ZZZ-15\0' (NETROM) and 10 bytes ROSE '6505551234\0' */ static char myAX25Name[10]; /* Room for 'GB9ZZZ-15\0' */ static char *Port; static int Logging = FALSE; static int ReadConfig(void); static fd_set fdread; static int maxfd = -1; /*--------------------------------------------------------------------------*/ static void update_maxfd(void) { struct axlist *paxl; FD_ZERO(&fdread); for (maxfd = -1, paxl = AXL; paxl != NULL && paxl->fd >= 0; paxl = paxl->next) { FD_SET(paxl->fd, &fdread); if (paxl->fd > maxfd) maxfd = paxl->fd; } } /*--------------------------------------------------------------------------*/ static void err_config(void) { if (AXL == NULL) { if (Logging) syslog(LOG_ERR, "config file reload error, exiting"); exit(1); } else { if (Logging) syslog(LOG_INFO, "config file reload error, continuing with original"); } } /*--------------------------------------------------------------------------*/ static void _ReadConfig(int dummy) { ReadConfig(); } /*--------------------------------------------------------------------------*/ static void reload_timer(int sec) { struct itimerval itv; itv.it_value.tv_sec = sec; itv.it_value.tv_usec = 0; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itv, NULL); signal(SIGALRM, _ReadConfig); } /*--------------------------------------------------------------------------*/ static void SignalHUP(int code) { ReadConfig(); } static void SignalTERM(int code) { if (Logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } exit(0); } static void WorkoutArgs(int af_type, char *shell, int *argc, char **argv) { char buffer[1024]; /* Maximum arg size */ char *sp, *cp; int cnt = 0; int args = 0; for (cp = shell; *cp != '\0'; cp++) { if (isspace(*cp) && cnt != 0) { buffer[cnt] = '\0'; argv[args++] = strdup(buffer); cnt = 0; if (args == MAX_ARGS - 1) { argv[args] = NULL; *argc = args; return; } continue; } else if (isspace(*cp)) /* && !cnt */ continue; if (*cp == '%') { cp++; switch (*cp) { case 'd': /* portname */ for (sp = Port; *sp != '\0' && *sp != '-'; sp++) buffer[cnt++] = *sp; break; case 'U': /* username in UPPER */ for (sp = User; *sp != '\0' && *sp != '-'; sp++) buffer[cnt++] = toupper(*sp); break; case 'u': /* USERNAME IN lower */ for (sp = User; *sp != '\0' && *sp != '-'; sp++) buffer[cnt++] = tolower(*sp); break; case 'S': /* username in UPPER (with SSID) */ for (sp = User; *sp != '\0'; sp++) buffer[cnt++] = toupper(*sp); break; case 's': /* USERNAME IN lower (with SSID) */ for (sp = User; *sp != '\0'; sp++) buffer[cnt++] = tolower(*sp); break; case 'P': /* nodename in UPPER */ if (af_type == AF_NETROM) { for (sp = Node; *sp != '\0' && *sp != '-'; sp++) buffer[cnt++] = toupper(*sp); } else { buffer[cnt++] = '%'; } break; case 'p': /* NODENAME IN lower */ if (af_type == AF_NETROM) { for (sp = Node; *sp != '\0' && *sp != '-'; sp++) buffer[cnt++] = tolower(*sp); } else { buffer[cnt++] = '%'; } break; case 'R': /* nodename in UPPER (with SSID) */ if (af_type == AF_NETROM) { for (sp = Node; *sp != '\0'; sp++) buffer[cnt++] = toupper(*sp); } else { buffer[cnt++] = '%'; } break; case 'r': /* NODENAME IN lower (with SSID) */ if (af_type == AF_NETROM) { for (sp = Node; *sp != '\0'; sp++) buffer[cnt++] = tolower(*sp); } else { buffer[cnt++] = '%'; } break; case '\0': case '%': default: buffer[cnt++] = '%'; break; } } else { buffer[cnt++] = *cp; } } if (cnt != 0) { buffer[cnt] = '\0'; argv[args++] = strdup(buffer); } argv[args] = NULL; *argc = args; } static void SetupOptions(int new, struct axlist *axl) { switch (axl->af_type) { case AF_AX25: if (axl->window != 0) setsockopt(new, SOL_AX25, AX25_WINDOW, &axl->window, sizeof(axl->window)); if (axl->t1 != 0) setsockopt(new, SOL_AX25, AX25_T1, &axl->t1, sizeof(axl->t1)); if (axl->n2 != 0) setsockopt(new, SOL_AX25, AX25_N2, &axl->n2, sizeof(axl->n2)); if (axl->t2 != 0) setsockopt(new, SOL_AX25, AX25_T2, &axl->t2, sizeof(axl->t2)); if (axl->t3 != 0) setsockopt(new, SOL_AX25, AX25_T3, &axl->t3, sizeof(axl->t3)); if (axl->idle != 0) setsockopt(new, SOL_AX25, AX25_IDLE, &axl->idle, sizeof(axl->idle)); break; case AF_NETROM: if (axl->t1 != 0) setsockopt(new, SOL_NETROM, NETROM_T1, &axl->t1, sizeof(axl->t1)); if (axl->n2 != 0) setsockopt(new, SOL_NETROM, NETROM_N2, &axl->n2, sizeof(axl->n2)); if (axl->t2 != 0) setsockopt(new, SOL_NETROM, NETROM_T2, &axl->t2, sizeof(axl->t2)); break; case AF_ROSE: if (axl->idle != 0) setsockopt(new, SOL_ROSE, ROSE_IDLE, &axl->idle, sizeof(axl->idle)); break; } } /**************************** CONFIGURATION STUFF ***************************/ static unsigned long ParseFlags(const char *kp, int line) { unsigned long flags = 0UL; for (; *kp != '\0'; kp++) { switch (*kp) { case 'v': case 'V': flags |= FLAG_VALIDCALL; break; case 'q': case 'Q': flags |= FLAG_NOLOGGING; break; case 'n': case 'N': flags |= FLAG_CHKNRN; break; case 'd': case 'D': flags |= FLAG_NODIGIS; break; case 'l': case 'L': flags |= FLAG_LOCKOUT; break; case '0': case '*': case '-': /* Be compatible and allow markers */ break; default: fprintf(stderr, "ax25d: config file line %d bad flag option '%c'.\n", line, *kp); break; } } return flags; } static struct axlist *ClearList(struct axlist *list) { struct axlist *tp1, *tp2, *tmp; for (tp1 = list; tp1 != NULL; ) { for (tp2 = tp1->ents; tp2 != NULL; ) { if (tp2->port != NULL) free(tp2->port); if (tp2->call != NULL) free(tp2->call); if (tp2->node != NULL) free(tp2->node); if (tp2->exec != NULL) free(tp2->exec); if (tp2->shell != NULL) free(tp2->shell); tmp = tp2->ents; free(tp2); tp2 = tmp; } if (tp1->port != NULL) free(tp1->port); if (tp1->call != NULL) free(tp1->call); if (tp1->node != NULL) free(tp1->node); if (tp1->exec != NULL) free(tp1->exec); if (tp1->shell != NULL) free(tp1->shell); close(tp1->fd); tmp = tp1->next; free(tp1); tp1 = tmp; } return NULL; } static int ReadConfig(void) { struct axlist axl_defaults; struct axlist *axl_build = NULL; struct axlist *axl_port = NULL; struct axlist *axl_ent, *axltmp; union { struct full_sockaddr_ax25 ax25; struct sockaddr_rose rose; } sockaddr; struct passwd *pwd; FILE *fp; char buffer[2048]; char *s, *port, *call, *node, *addr = NULL; unsigned long val; socklen_t addrlen; int af_type = 0; /* Keep GCC happy */ int line = 0; int hunt = TRUE, error = FALSE; int parameters = 0; signal(SIGALRM, SIG_IGN); memset(&axl_defaults, 0, sizeof(axl_defaults)); fp = fopen(ConfigFile, "r"); if (fp == NULL) return -1; while (fgets(buffer, sizeof(buffer), fp) != NULL) { line++; s = strchr(buffer, '\n'); if (s != NULL) *s = '\0'; s = strchr(buffer, '\r'); if (s != NULL) *s = '\0'; if (buffer[0] == '#') continue; switch (buffer[0]) { case '[': /* AX25 port call */ af_type = AF_AX25; hunt = TRUE; error = FALSE; break; case '<': /* NETROM iface call */ af_type = AF_NETROM; hunt = TRUE; error = FALSE; break; case '{': /* ROSE iface call */ af_type = AF_ROSE; hunt = TRUE; error = FALSE; break; default: if (hunt && !error) goto BadLine; break; } if (hunt) { /* We've found a Iface entry */ /* Reset 'defaults' entry on the interface */ memset(&axl_defaults, 0, sizeof(axl_defaults)); switch (af_type) { case AF_AX25: s = strchr(buffer, ']'); if (s == NULL) goto BadLine; *s = '\0'; s = strtok(buffer + 1, " \t"); if (s == NULL) goto BadLine; port = s; call = NULL; s = strtok(NULL, " \t"); if (s != NULL) { if (strcasecmp(s, "VIA") == 0 || strcasecmp(s, "V") == 0) { s = strtok(NULL, " \t"); if (s == NULL) goto BadLine; } call = port; port = s; s = strchr(call, '*'); if (s != NULL) { *s = '\0'; } } if (strcmp(port, "*") == 0 && call == NULL) { fprintf(stderr, "ax25d: invalid port name\n"); continue; } if (strcmp(port, "*") != 0) { addr = ax25_config_get_addr(port); if (addr == NULL) { fprintf(stderr, "ax25d: invalid AX.25 port '%s'\n", port); continue; } } if (call == NULL) { sockaddr.ax25.fsa_ax25.sax25_family = AF_AX25; sockaddr.ax25.fsa_ax25.sax25_ndigis = 0; ax25_aton_entry(addr, sockaddr.ax25.fsa_ax25.sax25_call.ax25_call); } else { sockaddr.ax25.fsa_ax25.sax25_family = AF_AX25; sockaddr.ax25.fsa_ax25.sax25_ndigis = 1; ax25_aton_entry(call, sockaddr.ax25.fsa_ax25.sax25_call.ax25_call); if (strcmp(port, "*") != 0) ax25_aton_entry(addr, sockaddr.ax25.fsa_digipeater[0].ax25_call); else sockaddr.ax25.fsa_digipeater[0] = null_ax25_address; } addrlen = sizeof(struct full_sockaddr_ax25); break; case AF_NETROM: s = strchr(buffer, '>'); if (s == NULL) goto BadLine; *s = '\0'; port = buffer + 1; addr = nr_config_get_addr(port); if (addr == NULL) { fprintf(stderr, "ax25d: invalid NET/ROM port '%s'\n", port); continue; } sockaddr.ax25.fsa_ax25.sax25_family = AF_NETROM; sockaddr.ax25.fsa_ax25.sax25_ndigis = 0; ax25_aton_entry(addr, sockaddr.ax25.fsa_ax25.sax25_call.ax25_call); addrlen = sizeof(struct full_sockaddr_ax25); break; case AF_ROSE: s = strchr(buffer, '}'); if (s == NULL) goto BadLine; *s = '\0'; s = strtok(buffer + 1, " \t"); if (s == NULL) goto BadLine; call = s; s = strtok(NULL, " \t"); if (s == NULL) goto BadLine; if (strcasecmp(s, "VIA") == 0 || strcasecmp(s, "V") == 0) { s = strtok(NULL, " \t"); if (s == NULL) goto BadLine; } port = s; addr = rs_config_get_addr(port); if (addr == NULL) { fprintf(stderr, "ax25d: invalid Rose port '%s'\n", port); continue; } if (strcmp(call, "*") == 0) { sockaddr.rose.srose_family = AF_ROSE; sockaddr.rose.srose_ndigis = 0; rose_aton(addr, sockaddr.rose.srose_addr.rose_addr); sockaddr.rose.srose_call = null_ax25_address; } else { sockaddr.rose.srose_family = AF_ROSE; sockaddr.rose.srose_ndigis = 0; rose_aton(addr, sockaddr.rose.srose_addr.rose_addr); ax25_aton_entry(call, sockaddr.rose.srose_call.ax25_call); } addrlen = sizeof(struct sockaddr_rose); break; default: fprintf(stderr, "ax25d: unknown af_type=%d\n", af_type); exit(1); } axl_port = calloc(1, sizeof(*axl_port)); if (axl_port == NULL) { fprintf(stderr, "ax25d: out of memory\n"); goto Error; } axl_port->port = strdup(port); axl_port->af_type = af_type; axl_port->fd = socket(axl_port->af_type, SOCK_SEQPACKET, 0); if (axl_port->fd < 0) { fprintf(stderr, "ax25d: socket: %s\n", strerror(errno)); free(axl_port->port); free(axl_port); error = TRUE; reload_timer(60); continue; } if (bind(axl_port->fd, (struct sockaddr *)&sockaddr, addrlen) < 0) { fprintf(stderr, "ax25d: bind: %s on port %s\n", strerror(errno), axl_port->port); close(axl_port->fd); free(axl_port->port); free(axl_port); error = TRUE; reload_timer(60); continue; } if (listen(axl_port->fd, SOMAXCONN) < 0) { fprintf(stderr, "ax25d: listen: %s\n", strerror(errno)); close(axl_port->fd); free(axl_port->port); free(axl_port); error = TRUE; reload_timer(60); continue; } /* Add it to the head of the list we are building */ if (axl_build == NULL) { axl_build = axl_port; } else { for (axltmp = axl_build; axltmp->next != NULL; axltmp = axltmp->next); axltmp->next = axl_port; } hunt = FALSE; /* Next lines will be entries */ } else { /* This is an entry */ axl_ent = calloc(1, sizeof(*axl_ent)); if (axl_ent == NULL) { fprintf(stderr, "ax25d: out of memory\n"); goto Error; } axl_ent->af_type = axl_port->af_type; /* Inherit this */ call = strtok(buffer, " \t"); if (call == NULL) { free(axl_ent); continue; } strupr(call); if (axl_ent->af_type == AF_NETROM) { s = strchr(call, '@'); if (s != NULL) { node = s + 1; *s = '\0'; if (*node == '\0') { free(axl_ent); continue; } axl_ent->node = strdup(node); if (*call == '\0') call = "default"; /* @NODE means default@NODE */ } } if (axl_ent->af_type == AF_AX25) { if ((strcasecmp("parameters_extAX25", call) == 0)) { axl_defaults.checkVC = axl_defaults.VCdiscOnLinkfailureMsg = axl_defaults.VCsendFailureMsg = axl_defaults.VCloginEnable = axl_defaults.LoggingVC = 0; while ((s = strtok(NULL, " \t")) != NULL) { if (strncmp(s, "VC", 2)) goto ignore; if (!strncmp(s, "VC", 2)) { if (!strcasecmp(s, "VC-reject-login")) axl_defaults.checkVC += 2; else if (!strcasecmp(s, "VC-wait-login")) axl_defaults.checkVC += 1; else if (!strcasecmp(s, "VC-disc-on-linkfailure-msg")) axl_defaults.VCdiscOnLinkfailureMsg = 1; else if (!strcasecmp(s, "VC-send-failure-msg")) axl_defaults.VCsendFailureMsg = 1; else if (!strcasecmp(s, "VC-login-ok")) axl_defaults.VCloginEnable = 1; else if (!strcasecmp(s, "VC-log-connections")) axl_defaults.LoggingVC += 1; else if (!strcasecmp(s, "VC-debug")) axl_defaults.LoggingVC += 2; else goto ignore; } continue; ignore: fprintf(stderr, "ax25d: bad config entry on line %d: %s", line, s); fprintf(stderr, " valid parametrs for parameters_extAX25 are:\n[VC-reject-login ||\nVC-wait-login [VC-login-ok] [VC-disc-on-linkfailure-msg]||\nVC-send-failure-msg || VC-log-connections]\n"); } if (axl_defaults.checkVC) { if (axl_defaults.checkVC > 2) { fprintf(stderr, "warning: line %d: VC-reject-login and VC-wait-login are exclusive. using VC-reject-login", line); axl_defaults.checkVC = 2; } } continue; } if (axl_defaults.checkVC != 0) { axl_ent->checkVC = axl_defaults.checkVC; axl_ent->VCdiscOnLinkfailureMsg = axl_defaults.VCdiscOnLinkfailureMsg; axl_ent->VCsendFailureMsg = axl_defaults.VCsendFailureMsg; axl_ent->VCloginEnable = axl_defaults.VCloginEnable; axl_ent->LoggingVC = axl_defaults.LoggingVC; } } parameters = FALSE; if (strcasecmp("parameters", call) == 0) parameters = TRUE; else if (strcasecmp("default", call) != 0) axl_ent->call = strdup(call); /* Window */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; if (!parameters) { if (strcmp(s, "*") != 0) axl_ent->window = atoi(s); else axl_ent->window = axl_defaults.window; } else { if (strcmp(s, "*") != 0) axl_defaults.window = atoi(s); } /* T1 */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; if (!parameters) { if (strcmp(s, "*") != 0) { val = (unsigned long)(atof(s) / 0.1); if (val == 0 || val > 65535) axl_ent->t1 = axl_defaults.t1; else axl_ent->t1 = val; } else { axl_ent->t1 = axl_defaults.t1; } } else { if (strcmp(s, "*") != 0) { val = (unsigned long)(atof(s) / 0.1); if (val > 0 && val < 65535) axl_defaults.t1 = val; } } /* T2 */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; if (!parameters) { if (strcmp(s, "*") != 0) { val = (unsigned long)(atof(s) / 0.1); if (val == 0 || val > 65535) axl_ent->t2 = axl_defaults.t2; else axl_ent->t2 = val; } else { axl_ent->t2 = axl_defaults.t2; } } else { if (strcmp(s, "*") != 0) { val = (unsigned long)(atof(s) / 0.1); if (val > 0 && val < 65535) axl_defaults.t2 = val; } } /* T3 */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; if (!parameters) { if (strcmp(s, "*") != 0) axl_ent->t3 = atoi(s); else axl_ent->t3 = axl_defaults.t3; } else { if (strcmp(s, "*") != 0) axl_defaults.t3 = atoi(s); } /* Idle */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; if (!parameters) { if (strcmp(s, "*") != 0) axl_ent->idle = atoi(s); else axl_ent->idle = axl_defaults.idle; } else { if (strcmp(s, "*") != 0) axl_defaults.idle = atoi(s); } /* N2 */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; if (!parameters) { if (strcmp(s, "*") != 0) axl_ent->n2 = atoi(s); else axl_ent->n2 = axl_defaults.n2; } else { if (strcmp(s, "*") != 0) axl_defaults.n2 = atoi(s); } if (!parameters) { /* Flags */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; axl_ent->flags = ParseFlags(s, line); if (!(axl_ent->flags & FLAG_LOCKOUT)) { /* Get username */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; pwd = getpwnam(s); if (pwd == NULL) { fprintf(stderr, "ax25d: UID for user '%s' is unknown, ignoring entry\n", s); goto BadUID; } axl_ent->uid = pwd->pw_uid; axl_ent->gid = pwd->pw_gid; /* Get exec file */ s = strtok(NULL, " \t"); if (s == NULL) goto BadArgsFree; axl_ent->exec = strdup(s); /* Get command line */ s = strtok(NULL, ""); if (s == NULL) goto BadArgsFree2; axl_ent->shell = strdup(s); } axl_ent->next = NULL; if (axl_port->ents == NULL) { axl_port->ents = axl_ent; } else { for (axltmp = axl_port->ents; axltmp->ents != NULL; axltmp = axltmp->ents) ; axltmp->ents = axl_ent; } } } continue; BadLine: fprintf(stderr, "ax25d: bad config entry on line %d\n", line); continue; BadUID: if (axl_ent->call != NULL) free(axl_ent->call); free(axl_ent); continue; BadArgsFree2: if (axl_ent->exec != NULL) free(axl_ent->exec); BadArgsFree: if (axl_ent->call != NULL) free(axl_ent->call); free(axl_ent); /* BadArgs: */ fprintf(stderr, "ax25d: bad config entry on line %d, not enough fields.\n", line); continue; } fclose(fp); AXL = ClearList(AXL); AXL = axl_build; /* Assign our built list to AXL */ if (Logging) syslog(LOG_INFO, "new config file loaded successfully"); update_maxfd(); return 0; Error: axl_build = ClearList(axl_build); err_config(); return -1; } static char *stripssid(const char *call) { static char newcall[10]; char *s; strcpy(newcall, call); s = strchr(newcall, '-'); if (s != NULL) *s = '\0'; return newcall; } int main(int argc, char *argv[]) { struct axlist *axltmp, *paxl, *raxl; union { struct full_sockaddr_ax25 ax25; struct sockaddr_rose rose; } sockaddr; struct sigaction act, oact; socklen_t addrlen; int cnt; char buf[1024]; char *p; char *mesg; while ((cnt = getopt(argc, argv, "c:lv")) != EOF) { switch (cnt) { case 'c': ConfigFile = optarg; break; case 'l': Logging = TRUE; break; case 'v': printf("ax25d: %s\n", FULL_VER); return 1; default: fprintf(stderr, "Usage: ax25d [-v] [-c altfile] [-l]\n"); return 1; } } if (ax25_config_load_ports() == 0) { fprintf(stderr, "ax25d: no AX.25 port data configured\n"); return 1; } nr_config_load_ports(); rs_config_load_ports(); if (!daemon_start(TRUE)) { fprintf(stderr, "ax25d: cannot become a daemon\n"); return 1; } if (Logging) { openlog("ax25d", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting"); } act.sa_handler = SignalHUP; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGHUP, &act, &oact); act.sa_handler = SignalTERM; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGTERM, &act, &oact); ReadConfig(); for (;;) { update_maxfd(); if (maxfd < 0) { sleep(10); continue; } if (select(maxfd + 1, &fdread, NULL, NULL, NULL) <= 0) continue; for (paxl = AXL; paxl != NULL; paxl = paxl->next) { if (paxl->fd > 0 && FD_ISSET(paxl->fd, &fdread)) { pid_t pid; gid_t grps[2]; char *argv[MAX_ARGS]; int argc; int new; int i; /* * Setting up a non-blocking accept() so is does not hang up * - I am not sure at this time why I didn't/don't assign * the socket non-blocking to start with. */ /* * We really need to setup the netrom window option here so * that it's negotiated correctly on accepting the connection. */ /* * It would be very useful if recvmsg/sendmsg were supported * then we can move the call checking up here. */ i = TRUE; ioctl(paxl->fd, FIONBIO, &i); addrlen = sizeof(struct full_sockaddr_ax25); new = accept(paxl->fd, (struct sockaddr *)&sockaddr, &addrlen); i = FALSE; ioctl(paxl->fd, FIONBIO, &i); if (new < 0) { if (errno == EWOULDBLOCK) continue; /* It's gone ??? */ if (Logging) syslog(LOG_ERR, "accept error %m, closing socket on port %s", paxl->port); close(paxl->fd); paxl->fd = -1; reload_timer(10); continue; } switch (paxl->af_type) { case AF_AX25: strcpy(User, ax25_ntoa(&sockaddr.ax25.fsa_ax25.sax25_call)); strcpy(Node, ""); break; case AF_NETROM: strcpy(User, ax25_ntoa(&sockaddr.ax25.fsa_ax25.sax25_call)); strcpy(Node, ax25_ntoa(&sockaddr.ax25.fsa_digipeater[0])); break; case AF_ROSE: strcpy(User, ax25_ntoa(&sockaddr.rose.srose_call)); strcpy(Node, rose_ntoa(&sockaddr.rose.srose_addr)); break; } for (raxl = paxl->ents; raxl != NULL; raxl = raxl->ents) { if (paxl->af_type == AF_NETROM && raxl->node != NULL && Node[0] != '\0') { if (strchr(raxl->node, '-') == NULL) { if (strcasecmp(raxl->node, stripssid(Node)) != 0) continue; /* Found no match (for any SSID) */ } else { if (strcasecmp(raxl->node, Node) != 0) continue; /* Found no match */ } } if (raxl->call == NULL) /* default */ break; if (strchr(raxl->call, '-') == NULL) { if (strcasecmp(raxl->call, stripssid(User)) == 0) break; /* Found a match (for any SSID) */ } else { if (strcasecmp(raxl->call, User) == 0) break; /* Found a match */ } } addrlen = sizeof(struct full_sockaddr_ax25); getsockname(new, (struct sockaddr *)&sockaddr, &addrlen); switch (paxl->af_type) { case AF_AX25: Port = ax25_config_get_port(&sockaddr.ax25.fsa_digipeater[0]); break; case AF_NETROM: Port = nr_config_get_port(&sockaddr.ax25.fsa_ax25.sax25_call); break; case AF_ROSE: Port = rs_config_get_port(&sockaddr.rose.srose_addr); break; default: Port = "???"; break; } if (raxl == NULL) { /* No default */ if (Logging && !(paxl->flags & FLAG_NOLOGGING)) { switch (paxl->af_type) { case AF_AX25: syslog(LOG_INFO, "AX.25 %s (%s) rejected - no default", User, Port); break; case AF_NETROM: syslog(LOG_INFO, "NET/ROM %s@%s (%s) rejected - no default", User, Node, Port); break; case AF_ROSE: syslog(LOG_INFO, "Rose %s@%s (%s) rejected - no default", User, Node, Port); break; } } close(new); continue; } if (raxl->flags & FLAG_LOCKOUT) { /* Not allowed to connect */ if (Logging && !(paxl->flags & FLAG_NOLOGGING)) { switch (raxl->af_type) { case AF_AX25: syslog(LOG_INFO, "AX.25 %s (%s) rejected - port locked", User, Port); break; case AF_NETROM: syslog(LOG_INFO, "NET/ROM %s@%s (%s) rejected - port locked", User, Node, Port); break; case AF_ROSE: syslog(LOG_INFO, "Rose %s@%s (%s) rejected - port locked", User, Node, Port); break; } } close(new); continue; } if (raxl->af_type == AF_AX25 && (raxl->flags & FLAG_NODIGIS) && sockaddr.ax25.fsa_ax25.sax25_ndigis != 0) { /* Not allowed to uplink via digi's */ if (Logging && !(paxl->flags & FLAG_NOLOGGING)) syslog(LOG_INFO, "AX.25 %s (%s) rejected - digipeaters", User, Port); close(new); continue; } pid = fork(); switch (pid) { case -1: if (Logging) syslog(LOG_ERR, "fork error %m"); /* * I don't think AX25 at the moment will hold the * connection open, if the above does not make it * through first time. */ close(new); break; /* Oh well... */ case 0: if (raxl->af_type == AF_AX25 && raxl->checkVC) { /* to which of my own addresses has the user connected? */ getsockname(new, (struct sockaddr *)&sockaddr, &addrlen); strcpy(myAX25Name, ax25_ntoa(&sockaddr.ax25.fsa_ax25.sax25_call)); sprintf(buf, "/usr/sbin/ax25rtctl -l ip | grep -i ' %s ' | /bin/grep 'v ' >/dev/null", User); /* he's not a VC user? */ if (system(buf) != 0) goto login; if (raxl->checkVC > 1) { /* setproctitle("ax25d [%d]: rejecting, User); */ } else { /* setproctitle("ax25d [%d]: %s, User, (VCloginEnable ? "waiting" : "discarding")); */ } /* is a VC user. checkVC > 0? then reject now, or fork and wait for pid=textdata.. */ if (raxl->VCsendFailureMsg) { sprintf(buf, "*** %s: NO MODE VC with %s\r", myAX25Name, myAX25Name); write(new, buf, strlen(buf)); if (raxl->checkVC > 1) sleep(1); } if (raxl->checkVC > 1) { if (Logging && (!(paxl->flags & FLAG_NOLOGGING) || raxl->LoggingVC)) syslog((raxl->LoggingVC ? LOG_NOTICE : LOG_INFO), "AX.25 %s (%s:%s) rejected - AX25.IP-VC entry found", User, Port, myAX25Name); goto close_link; } /* checkVC == 1: wait for data */ if (Logging && (!(paxl->flags & FLAG_NOLOGGING) || raxl->LoggingVC)) { syslog((raxl->LoggingVC ? LOG_NOTICE : LOG_INFO), "AX.25 %s (%s:%s) AX25.IP-VC host connected. %s", User, Port, myAX25Name, (raxl->VCloginEnable ? "waiting for first PID=text packet for triggered login" : "discarding PID=text packets")); } *buf = 0; mesg = NULL; while ((i = read(new, buf, (sizeof(buf)-1))) > 0) { buf[i] = 0; if (Logging && raxl->LoggingVC > 1) { /* debug */ syslog((LOG_DEBUG), "DEBUG: AX.25 %s (%s:%s) AX25.IP-VC host said: >%s<", User, Port, myAX25Name, buf); } /* skip leading mess */ for (p = buf; *p && (isspace(*p & 0xff) || *p == '\r'); p++) ; if (raxl->VCdiscOnLinkfailureMsg && !strncmp(p, "***", 3)) { /* format received line for debug purposes */ /* 1. skip "***" */ for (p += 3; *p && isspace(*p & 0xff); p++ ) ; mesg = p; /* 2. get rid off EOL delimiters */ while (*p) { if (*p == '\r' || *p == '\n') { *p = 0; break; } p++; } /* end read loop */ break; } /* per default, we discard all messages, * because there's no useful combination * using VC and pidText togegther */ if (raxl->VCloginEnable) goto login; } if (Logging && (!(paxl->flags & FLAG_NOLOGGING) || raxl->LoggingVC)) { syslog((raxl->LoggingVC ? LOG_NOTICE : LOG_INFO), "AX.25 %s (%s:%s) AX25.IP-VC host disconnected%s%s", User, Port, myAX25Name, (mesg ? ": " : "."), (mesg ? mesg : "")); } /* point of no return */ close_link: /* close link */ /* setproctitle("ax25d [%s]: disconnecting", User); */ close(new); exit(0); } login: /* setproctitle("ax25d [%s]: login", User); */ SetupOptions(new, raxl); WorkoutArgs(raxl->af_type, raxl->shell, &argc, argv); if (Logging && !(paxl->flags & FLAG_NOLOGGING)) { switch (paxl->af_type) { case AF_AX25: syslog(LOG_INFO, "AX.25 %s (%s) %s", User, Port, argv[0]); break; case AF_NETROM: syslog(LOG_INFO, "NET/ROM %s@%s (%s) %s", User, Node, Port, argv[0]); break; case AF_ROSE: syslog(LOG_INFO, "Rose %s@%s (%s) %s", User, Node, Port, argv[0]); break; } } dup2(new, STDIN_FILENO); dup2(new, STDOUT_FILENO); close(new); /* * Might be more efficient if we just went down AXL, * we cleaned up our parents left overs on bootup. */ for (axltmp = AXL; axltmp != NULL; axltmp = axltmp->next) close(axltmp->fd); if (Logging) closelog(); /* Make root secure, before we exec() */ /* Strip any supplementary gid's */ if (setgroups(0, grps) == -1) exit(1); if (setgid(raxl->gid) == -1) exit(1); if (setuid(raxl->uid) == -1) exit(1); execve(raxl->exec, argv, NULL); exit(1); default: close(new); break; } } } } /* NOT REACHED */ return 0; } ax25/ax25d.conf.5000066400000000000000000000223501442776067000136110ustar00rootroot00000000000000.TH AX25D.CONF 5 "25 May 2015" Linux "Linux Programmer's Manual" .SH NAME ax25d.conf \- ax25d configuration file. .SH DESCRIPTION .LP .B Ax25d.conf controls the functioning of .B ax25d. Its purpose is to specify on which ports to listen on, which applications are available, and to whom they are available to. The configuration file is common to both AX.25, NET/ROM and Rose and their is similarity between the two parts of the file. .sp 1 The general layout for an entry for a given port is as follows: .sp 1 .RS interface control .br callsign entry 1 .br . .br . .br callsign entry n .RE .sp 1 The .B "interface control" line determines which port and callsigns apply to the following .B "callsign entry" lines, until the next .B "interface control" is read. There are four different variants of the .B "interface control" line: .sp 1 .RS 1. [AX.25 Port Name] .br 2. [Callsign VIA AX.25 Port Name] .br 3. .br 4. {Callsign VIA Rose Port Name} .RE .sp 1 Version 1 allows the following .B "callsign entry" lines to listen on the AX.25 port specified by the AX.25 port name using the default callsign of that AX.25 port. .sp 1 Version 2 allows the following .B "callsign entry" lines to listen on the AX.25 port specified by the AX.25 port name using the callsign specified instead of the default callsign of that AX.25 port. Specifying a * for the AX.25 port name allows the following .B "callsign entries" to be valid for all the operating AX.25 ports using the callsign specified. VIA can be abbreviated to just V. If the callsign has an asterisk appended to it then the system will be listening on the port with the callsign, but as a pseudo-digipeater instead of being the normal destination callsign. .sp 1 Version 3 allows the following .B "callsign entry" lines to listen on the NET/ROM port specified by the NET/ROM port name using the default callsign of that NET/ROM port. .sp 1 Version 4 allows the following .B "callsign entry" lines to listen on the Rose port using the specified Rose port name using the callsign specified as the service access point (SAP). A * may be specified for a callsign to allow matching to any incoming Call Requests with any SAP. .sp 1 The .B "callsign entry" lines have a similar layout for both AX.25, NET/ROM and Rose, the layout is: .sp 1 .RS peer window t1 t2 t3 idle n2 mode uid exec args... .RE .sp 1 All values must be entered for all entries even when they are not used (ie window for NET/ROM, just enter a * instead), The meanings of each of the fields is given below. All timings apart from the idle value are given in seconds, the idle values is given in minutes. .RS .TP 10 .B peer This specifies the callsign of the remote end of the connection that should have the following parameters and executable set up for them. The syntax of the peer argument is explained below. .TP 10 .B window This sets the the value of the window size, if a value of * is entered in this field then the default value for the port is taken from the \(lqparameters\(rq entry (see below) or lacking such an entry, the kernel default value is used. This entry is used by AX.25 but not by NET/ROM or Rose. .TP 10 .B t1 This sets the the value of the T1 timer, if a value of * is entered in this field then the default value for the port is taken from the \(lqparameters\(rq entry (see below) or lacking such an entry, the kernel default value is used. This entry is used by both AX.25 and NET/ROM but not by Rose. .TP 10 .B t2 This sets the the value of the T2 timer, if a value of * is entered in this field then the default value for the port is taken from the \(lqparameters\(rq entry (see below) or lacking such an entry, the kernel default value is used. This entry is used by both AX.25 and NET/ROM but not by Rose. .TP 10 .B t3 This sets the the value of the T3 timer, if a value of * is entered in this field then the default value for the port is taken from the \(lqparameters\(rq entry (see below) or lacking such an entry, the kernel default value is used. This entry is used by AX.25 but not by NET/ROM or Rose. .TP 10 .B idle This sets the the value of the idle timer, if a value of * is entered in this field then the default value for the port is taken from the \(lqparameters\(rq entry (see below) or lacking such an entry, the kernel default value is used. .TP 10 .B n2 This sets the the value of the N2 counter, if a value of * is entered in this field then the default value for the port is taken from the \(lqparameters\(rq entry (see below) or lacking such an entry, the kernel default value is used. This entry is used by both AX.25 and NET/ROM but not by Rose. .TP 10 .B mode This is a set of flags that control the various properties associated with the incoming connection. The flags are single letters, may be in either upper or lower case, and there may not be any spaces between them. If no flags are to be specified either a 0, - or a * must be entered instead. The valid mode flag letters are: .RS .TP 5 .B D Do not allow connections that have passed via any digipeaters. AX.25 only. .TP 5 .B L Do not allow this station to connect, they are Locked out. .TP 5 .B N Check that the NET/ROM neighbour is allowed, currently unused. .TP 5 .B Q Do not make an entry into the log file for this connection. .TP 5 .B V Validate the callsign of the incoming connection, currently unused. .RE .TP 10 .B uid This is the userid that the following command should run under when executing. .TP 10 .B exec This is the executable that should be executed when an incoming connection matches the criteria of both the .B "interface control" and the .B "callsign entry". .TP 10 .B args... These are the optional arguments that are passed to the executable. All of the arguments are passed literally apart from the following: .RS .TP 5 .B %d The name of the port that the connection is on. .TP 5 .B %U The username (callsign) of the remote station in upper case without the SSID. .TP 5 .B %u The username (callsign) of the remote station in lower case without the SSID. .TP 5 .B %S The username (callsign) of the remote station in upper case with the SSID. .TP 5 .B %s The username (callsign) of the remote station in lower case with the SSID. .TP 5 .B %P The nodename of the remote station in upper case without the SSID. This is only valid under NET/ROM and Rose, under AX.25 a % is substituted instead. .TP 5 .B %p The nodename of the remote station in lower case without the SSID. This is only valid under NET/ROM and Rose, under AX.25 a % is substituted instead. .TP 5 .B %R The nodename of the remote station in upper case with the SSID. This is only valid under NET/ROM and Rose, under AX.25 a % is substituted instead. .TP 5 .B %r The nodename of the remote station in lower case with the SSID. This is only valid under NET/ROM and Rose, under AX.25 a % is substituted instead. .TP 5 .B %% A %. .RE .RE .sp 1 The .B peer argument is dependent upon whether AX.25, NET/ROM or Rose is being used. There are five formats of this argument: .sp 1 .RS 1. default .br 2. parameters .br 3. callsign .br 4. callsign@node .br 5. @node .RE .sp 1 The first version is used by AX.25, NET/ROM and Rose to specify that all callsigns on a given port are to be matched. The default line is usually the last of the .B "callsign entry" lines, so that more specific entries may have the chance to be matched first. .sp 1 The second version is not a .B "callsign entry" that is used by any incoming connections. It is a means to specify default values for parameters such as Window, T1, T2, T3, Idle and N2. It is used for both AX.25, NET/ROM and Rose. .sp 1 The third version is used by both AX.25, NET/ROM and Rose to specify the callsign of the remote station to match the .B "callsign entry" line. If no SSID is specified then the callsign will be matched with any that has the same callsign and any SSID. Specifying an SSID causes the callsign to be matched exactly. In the case of NET/ROM and Rose this entry does not specify which node the originating callsign comes from. .sp 1 The fourth version is used by NET/ROM and Rose to specify the callsign of the remote station and the remote node to match the .B "callsign entry" line. If no SSID is specified in the callsign section then the callsign will be matched with any that has the same callsign and any SSID. Specifying an SSID causes the callsign to be matched exactly. .sp 1 The fifth version is used by NET/ROM and Rose to specify only the address of the remote node to match the .B "callsign entry" line. This entry will mean that all remote users at the given node will match the entry. .sp 1 Comments may be embedded in the configuration file by placing a # in the first column. .sp 1 ax25d can now honour AX.25 TCP/IP mode-VC connections in a special way. Therefore, a new port specific option "parameters_extAX25" is available, with the following options, which are separated by space. .TP 5 parameters_extAX25 VC-debug .br VC-reject-login|VC-wait-login|VC-login-ok .br VC-disc-on-linkfailure-msg, VC-send-failure-msg, VC-log-connections .br Recommended settings in ax25d.conf: .br parameters_extAX25 VC-wait-login VC-disc-on-linkfailure-msg VC-log-connections .br or .br parameters_extAX25 VC-reject-login VC-send-failure-msg VC-log-connections .br .SH FILES .LP /etc/ax25/ax25d.conf .SH "SEE ALSO" .BR ax25 (4), .BR ax25wrapper (8), .BR netrom (4), .BR rose (4), .BR axports (5), .BR nrports (5), .BR rsports (5), .BR ax25d (8). ax25/ax25d.conf.in000066400000000000000000000022061442776067000140510ustar00rootroot00000000000000# @SYSCONFDIR@/ax25/ax25d.conf # # ax25d Configuration File. # # AX.25 Ports begin with a '['. # # Please note that the programs node and finger are not part of ax25-tools. # [OH2BNS VIA 1] NOCALL * * * * * * L default * * * * * * - root @SBINDIR@/ttylinkd ttylinkd #parameters_extAX25 VC-wait-login VC-disc-on-linkfailure-msg VC-log-connections # # [OH2BNS-2 VIA 1] NOCALL * * * * * * L default * * * * * * - root @SBINDIR@/node node # # [OH2BNS VIA 2] NOCALL * * * * * * L default * * * * * * - root @SBINDIR@/ttylinkd ttylinkd # [OH2BNS-2 VIA 2] NOCALL * * * * * * L default * * * * * * - root @SBINDIR@/node node # [OH2BNS-3 VIA 2] NOCALL * * * * * * L default * * * * * * - root @SBINDIR@/axwrapper axwrapper /usr/bin/finger finger # #[OH2BNS-9] #NOCALL * * * * * * L #default * * * * * * - root @SBINDIR@/node node # # NET/ROM Ports begin with a '<'. # # #NOCALL * * * * * * L #default * * * * * * - root @SBINDIR@/ttylinkd ttylinkd # NOCALL * * * * * * L default * * * * * * - root @SBINDIR@/node node # # #NOCALL * * * * * * L #default * * * * * * - root @SBINDIR@/node node # ax25/axctl.8000066400000000000000000000034351442776067000130630ustar00rootroot00000000000000.TH AXCTL 8 "16 April 2007" Linux "Linux System Managers Manual" .SH NAME axctl \- Configure/Kill running AX.25 connections. .SH SYNOPSIS .B axctl [-v] port dest src window|t1|t2|t3|n2|idle|paclen|kill [parm] .SH DESCRIPTION .LP The .B axctl command is designed to be a multi-function command that allows miscellaneous commands to be issued to the Linux AX.25 protocol layer for existing AX.25 connections. The connection is uniquely identified via the combination of port, destination callsign and source callsign, with that information the kernel is able to change the parameters, or abort the connection. .LP Many of the options are similar to those found in .B axparms and perform the same function. Only one parameter may be changed on each invocation of .B axctl. .SH OPTIONS .TP 20 .BI \-v Displays the version number. .TP 20 .BI "window window" Sets the window size for the AX.25 connection. .TP 20 .BI "t1 t1\-timeout" Sets the initial T1 timeout value for the AX.25 connection, the value is given in seconds. .TP 20 .BI "t2 t2\-timeout" Sets the T2 timeout value for the AX.25 connection, the value is given in seconds. .TP 20 .BI "t3 t3\-timeout" Sets the T3 timeout value for the AX.25 connected, the value is given in seconds. .TP 20 .BI "n2 n2\-count" Sets the maximum number of tries for the AX.25 connection. .TP 20 .BI "idle idle-timeout" Sets the value for the idle timer for the AX.25 connection, the value is in minutes. .TP 20 .BI "paclen paclength" Sets the maximum packet length that may be transmitted on the AX.25 connection. .TP 20 .BI "kill" Will abort an existing AX.25 connection. .SH FILES .LP /etc/ax25/axports .SH "SEE ALSO" .BR call (1), .BR getsockopt (2), .BR setsockopt (2), .BR ax25 (4), .BR axparms (8), .BR axports (5). .SH AUTHORS .nf Joerg Reuter DL1BKE .fi ax25/axctl.c000066400000000000000000000044621442776067000131370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { struct ax25_ctl_struct ax25_ctl; char *addr; int s; if (argc == 2 && strncmp(argv[1], "-v", 2) == 0) { printf("axctl: %s\n", FULL_VER); return 0; } if (argc < 5) { fprintf(stderr, "Usage: axctl [-v] port dest src t1|t2|t3|n2|paclen|idle|window|maxq|kill [parm]\n"); return 1; } if (ax25_config_load_ports() == 0) { fprintf(stderr, "axctl: no AX.25 port data configured\n"); return 1; } addr = ax25_config_get_addr(argv[1]); if (addr == NULL) { fprintf(stderr, "axctl: invalid port name - %s\n", argv[1]); return 1; } if (ax25_aton_entry(addr, (char *)&ax25_ctl.port_addr) == -1) return 1; if (ax25_aton_entry(argv[2], (char *)&ax25_ctl.dest_addr) == -1) return 1; if (ax25_aton_entry(argv[3], (char *)&ax25_ctl.source_addr) == -1) return 1; s = socket(AF_AX25, SOCK_SEQPACKET, 0); if (s < 0) { perror("axctl: socket"); return 1; } if (strcmp(argv[4], "kill") == 0 || strcmp(argv[4], "-kill") == 0) { ax25_ctl.cmd = AX25_KILL; ax25_ctl.arg = 0; } else { if (argc < 6) { fprintf(stderr,"axctl: parameter missing\n"); return 1; } ax25_ctl.arg = atoi(argv[5]); if (strcmp(argv[4], "t1") == 0 || strcmp(argv[4], "-t1") == 0) ax25_ctl.cmd = AX25_T1; else if (strcmp(argv[4], "t2") == 0 || strcmp(argv[4], "-t2") == 0) ax25_ctl.cmd = AX25_T2; else if (strcmp(argv[4], "t3") == 0 || strcmp(argv[4], "-t3") == 0) ax25_ctl.cmd = AX25_T3; else if (strcmp(argv[4], "idle") == 0 || strcmp(argv[4], "-idle") == 0) ax25_ctl.cmd = AX25_IDLE; else if (strcmp(argv[4], "n2") == 0 || strcmp(argv[4], "-n2") == 0) ax25_ctl.cmd = AX25_N2; else if (strcmp(argv[4], "window") == 0 || strcmp(argv[4], "-window") == 0) ax25_ctl.cmd = AX25_WINDOW; else if (strcmp(argv[4], "paclen") == 0 || strcmp(argv[4], "-paclen") == 0) ax25_ctl.cmd = AX25_PACLEN; } ax25_ctl.digi_count = 0; if (ioctl(s, SIOCAX25CTLCON, &ax25_ctl) != 0) { perror("axctl: SIOCAX25CTLCON"); return 1; } return 0; } ax25/axgetput/000077500000000000000000000000001442776067000135135ustar00rootroot00000000000000ax25/axgetput/.gitignore000066400000000000000000000000441442776067000155010ustar00rootroot00000000000000.deps Makefile Makefile.in axgetput ax25/axgetput/Makefile.am000066400000000000000000000007321442776067000155510ustar00rootroot00000000000000 installconf: etcfiles = varfiles = sbin_PROGRAMS = bin_PROGRAMS = axgetput dist_man_MANS = axgetput.1 dist_doc_DATA = README.axgetput TIPS.txt TODO axgetput_SOURCES = axgetput.c axgetput.h util.h proto_bin.h util.c proto_bin.c includes.h install-exec-hook: (cd $(DESTDIR)$(bindir) && ln -sf axgetput bget && ln -sf axgetput bput) install-data-hook: (cd $(DESTDIR)$(mandir)/man1 && ln -sf axgetput.1 bget.1 && ln -sf axgetput.1 bput.1) AM_CPPFLAGS = -D_GNU_SOURCE ax25/axgetput/README.axgetput000066400000000000000000000010121442776067000162250ustar00rootroot00000000000000This shell utility is for up/downloading files via your ax25 unix login session which is managed by axspawn. you need a //BIN compatible axspawn, which has the functionality of making the stream 8bit compatible (normally the EOL-conversion <-> prevents this). see: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/patches/linux-ax25/ The location of these sources is http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ Since 2006-12-10, axgetput is part of the ax25-tools (s. linux-ax25.org). ax25/axgetput/TIPS.txt000066400000000000000000000001531442776067000150320ustar00rootroot00000000000000- compressed download: gzip -c foo.txt | bget foo.txt.gz tar cjf - ~/foo ~/bar/ | bget my_data.tar.bz2 ax25/axgetput/TODO000066400000000000000000000000421442776067000141770ustar00rootroot00000000000000 - yapp support - didadit support ax25/axgetput/axgetput.1000066400000000000000000000040511442776067000154360ustar00rootroot00000000000000.TH AXGETPUT 1 "11 March 2007" Linux "Linux Programmer's Manual" .SH NAME axgetput \- upload or download files via AX.25 / axspawn Linux login session. .SH SYNOPSIS .B axgetput [-b \fIblocksize\fR] [-i] [-v] [-h] [?] \fIfilename\fR .br .B axgetput [-b \fIblocksize\fR] [-i] -s [-v] [-h] [?] [\fIfilename\fR] .LP .SH DESCRIPTION .TP \fB-b blocksize\fR Set the blocksize (frame length) of transmitted data. Defaults to 256 bytes which matches typical AX.25 network confurations best. .TP \fB-i\fR Compute CRC checksum only. .TP \fB.B -s\fR Indicates input from a stream. This option is available only if STDIN is a pipe. The filename argument may be omitted if -s is given. The length of the file need not to be known. .TP \fB\-v\fR print version and exit. .TP \fB-h, ?\fR print usage and exit. .LP .B axgetput is the actual name of the program. You execute for e.g. .B bget or .B bput while axgetput is the common program to which bget and bput are linked to. axgetput autodetermines which operation mode the user desires. axgetput sets the pty to be 8bit clean (thus enables binary mode for the file transfer). It requires that the user is logged in through axspawn(8). .B I. #BIN Protocol .B bget is used for downloading a file on this system from the unix login shell via his ax25 session to his packet-radio terminal program. The download "protocol" is the "#BIN" standard known from packet radio mailboxes. .B bput does it the other way around: with this comand, the user may upload a file to this computer. The #BIN protocol provides a CRC consistency check after the file is transferred. The transfer of the file modification time is part of the #BIN protocol. .LP .B II. YAPP Protocol .B yget or .B yput is reserved for the yapp protocol, which is not supported in this version. .LP .B II. DIDADIT Protocol .B rget or .B rput is reserved for the didadit protocol, which is not supported in this version. .LP Anyone like to implement yapp and didadit protcols? .LP .SH AUTHOR Thomas Osterried DL9SAU Ralf Baechle DL5RB ax25/axgetput/axgetput.c000066400000000000000000000230541442776067000155240ustar00rootroot00000000000000/* * This is axgetput * * This shell utility is for up/downloading files via your ax25 unix login * session which is managed by axspawn. * you need a //BIN compatible axspawn, which has the functionality of making * the stream 8bit compatible (normally the EOL-conversion <-> * prevents this). see: * http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/patches/linux-ax25/ * * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "includes.h" #include "axgetput.h" #include "util.h" #include "proto_bin.h" int fdin; int fdout = 1; int fderr = 2; int fdout_is_pipe; int fdin_is_pipe; char myname[PATH_MAX+1]; char filename[PATH_MAX+1]; char err_msg[2048]; int is_stream; int mode; int do_crc_only; char c_eol = '\n'; unsigned int BLOCKSIZ = BLOCKSIZ_DEFAULT; char *send_on_signal; static struct termios prev_termios; static int prev_termios_stored; static mode_t mode_tty; #ifndef MYNAME #define MYNAME "axgetput" #endif /*---------------------------------------------------------------------------*/ static void set_tty_flags(void) { struct termios termios; struct stat statbuf; if (fdin_is_pipe) return; /* mesg no */ if (!fstat(fdin, &statbuf)) { /* save old mode */ mode_tty = statbuf.st_mode; fchmod(fdin, 0600); } /* make tty 8bit clean */ if (tcgetattr(fdin, &prev_termios) != -1) prev_termios_stored = 1; memset(&termios, 0, sizeof(termios)); termios.c_iflag = IGNBRK | IGNPAR; termios.c_oflag = 0; termios.c_cflag = CBAUD | CS8 | CREAD | CLOCAL; termios.c_cflag = ~(CSTOPB|PARENB|PARODD|HUPCL); termios.c_lflag = 0; termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; termios.c_cc[VSTART] = -1; termios.c_cc[VSTOP] = -1; tcsetattr(fdin, TCSANOW, &termios); } /*---------------------------------------------------------------------------*/ static void restore_tty_flags(void) { if (fdin_is_pipe) return; if (prev_termios_stored) tcsetattr(fdin, TCSANOW, &prev_termios); if (mode_tty) fchmod(fdin, mode_tty); } /*---------------------------------------------------------------------------*/ static void eol_convention(int state_should) { /* need patched axspawn */ #define BIN_ON "//BIN ON\r" #define BIN_OFF "//BIN OFF\r" static int state_is; /* already in correct state? */ if ((state_is && state_should) || (!state_is && !state_should)) return; sleep(1); if (state_should) { write(fderr, BIN_ON, strlen(BIN_ON)); c_eol = '\r'; } else { write(fderr, BIN_OFF, strlen(BIN_OFF)); c_eol = '\n'; } state_is = state_should; sleep(1); } /*---------------------------------------------------------------------------*/ static void restore_defaults(void) { eol_convention(0); restore_tty_flags(); } /*---------------------------------------------------------------------------*/ static void signal_handler(int sig) { if (send_on_signal) secure_write(fdout, send_on_signal, strlen(send_on_signal)); restore_defaults(); if (*err_msg) { fputs(err_msg, stderr); } fprintf(stderr, "Died by signal %d.\n", sig); exit(sig); } /*---------------------------------------------------------------------------*/ static void do_version(void) { fprintf(stderr, MYNAME " " FULL_VER "\n"); fprintf(stderr, " (c) 2002 Thomas Osterried \n"); fprintf(stderr, " License: GPL. See http://www.fsf.org/\n"); fprintf(stderr, " Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/\n"); } /*---------------------------------------------------------------------------*/ static void usage(int all) { fprintf(stderr, "usage: %s ", myname); if (mode % 2) { fprintf(stderr, "[-ivh] [filename]\n"); } else { fprintf(stderr, "[-isvh] [-b ] [filename]\n"); } fprintf(stderr, " -h prints detailed help\n"); fprintf(stderr, " -i computes checksum only\n"); fprintf(stderr, " -v prints version and exits\n"); if (!all) return; if (mode % 2) { fprintf(stderr, " filename is usually got from the remote side by the protocol\n"); fprintf(stderr, " but can be forced if you like to ignore this.\n"); fprintf(stderr, " filename should be omitted if output is sent to a pipe\n."); } else { fprintf(stderr, " -b value is the blocksize (framelen) of the transmitted data\n"); fprintf(stderr, " default %d, which is a useful choice for ampr ax25.\n", BLOCKSIZ_DEFAULT); fprintf(stderr, " -s indicates a stream with unknown size.\n"); fprintf(stderr, " otherwise, the data will be read to memory until EOF.\n"); fprintf(stderr, " -s is only available if stdin is a pipe\n"); fprintf(stderr, " if filename specified in filter, the given name will be suggested instead.\n"); fprintf(stderr, " filename may be omitted if used as filter.\n"); } fputc('\n', stderr); fprintf(stderr, "Tips: - compressed download:\n"); fprintf(stderr, " gzip -c foo.txt | bget foo.txt.gz\n"); fprintf(stderr, " tar cjf - ~/foo ~/bar/ | bget my_data.tar.bz2\n"); fputc('\n', stderr); fprintf(stderr, "Other protocols:\n"); fprintf(stderr, " bget / bput: receive / send with #BIN# protocol\n"); fprintf(stderr, " yget / yput: receive / send with yapp\n"); fprintf(stderr, " rget / rput: receive / send with didadit\n"); fprintf(stderr, "These are (sym)links to one program: " MYNAME "\n"); } /*---------------------------------------------------------------------------*/ /* not implemented */ static int yput(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; } static int yget(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; } static int rget(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; } static int rput(void) { strcpy(err_msg, "yapp: not implementet yet\n"); return 1; } /*---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int len; int ret = 0; int c; char *p; /* determine what to so in the way how we are called */ if ((p = strrchr(argv[0], '/'))) p++; /* skip '/' */ else p = argv[0]; len = strlen(p); if (len > sizeof(myname)-1) len = sizeof(myname)-1; strncpy(myname, p, len); myname[len] = 0; strlwc(myname); fdin_is_pipe = (isatty(fdin) ? 0 : 1); fdout_is_pipe = (isatty(fdout) ? 0 : 1); if (fdin_is_pipe && fdout_is_pipe) { fprintf(stderr, "error: cannot work in between two pipes\n"); exit(1); } *filename = 0; *err_msg = 0; if (!strcmp(myname, "bput")) mode = RECV_BIN; else if (!strcmp(myname, "bget")) mode = SEND_BIN; else if (!strcmp(myname, "yput")) mode = RECV_YAPP; else if (!strcmp(myname, "yget")) mode = SEND_YAPP; else if (!strcmp(myname, "rput")) mode = RECV_DIDADIT; else if (!strcmp(myname, "rget")) mode = SEND_DIDADIT; if (mode % 2) { if (fdin_is_pipe) { fprintf(stderr, "error: error: stdin must be a tty\n"); exit(1); } } else { if (fdout_is_pipe) { fprintf(stderr, "error: stdout must be a tty\n"); exit(1); } } signal(SIGHUP, signal_handler); signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); /* for difference betreen "bput -f file" and "bget file" */ #define get_filename(f) { \ if (!strcmp(f, "-")) { \ if (mode % 2) \ fdin_is_pipe = 1; \ else \ fdout_is_pipe = 1; \ } else { \ strncpy(filename, f, sizeof(filename)-1); \ filename[sizeof(filename)-1] = 0; \ if (mode % 2) { \ if (fdin_is_pipe) \ fdin_is_pipe = 0; \ } else { \ if (fdout_is_pipe) \ fdout_is_pipe = 0; \ } \ } \ } while ((c = getopt(argc, argv, (mode % 2) ? "ivh?" : "b:isvh?")) != EOF) { switch (c) { case 'b': if (((BLOCKSIZ = (unsigned ) atoi(optarg)) < BLOCKSIZ_MIN) || BLOCKSIZ > BLOCKSIZ_MAX) { fprintf(stderr, "error: invalid blocksize: %d\n", BLOCKSIZ); fprintf(stderr, " blocksize must be in the range %d <= x <= %d. a value\n", BLOCKSIZ_MIN, BLOCKSIZ_MAX); fprintf(stderr, " of %d (default) is suggested, because it fits in an ax25 frame.\n", BLOCKSIZ_DEFAULT); exit(1); } break; case 'i': do_crc_only = 1; break; case 's': is_stream = 1; break; case 'v': do_version(); exit(0); break; case 'h': case '?': usage((c == 'h')); exit(0); break; } } if (mode == 0) { usage(1); exit(0); } if (optind < argc) { get_filename(argv[optind]); optind++; } if (optind < argc) { usage(0); exit(1); } if (is_stream && !fdin_is_pipe) { fprintf(stderr, "error: -s is only available in a pipe\n"); exit(1); } if (do_crc_only) goto skiped_crc_only_tag_1; if (mode % 2) { if (fdin_is_pipe) { fprintf(stderr, "error: error: stdin must be a tty.\n"); exit(1); } if (fdout_is_pipe && *filename) { fprintf(stderr, "error: filename in a pipe does not make sense.\n"); exit(1); } } else { if (fdout_is_pipe) { fprintf(stderr, "error: stdout must be a tty.\n"); exit(1); } if (!fdin_is_pipe && !*filename) { fprintf(stderr, "error: no file to send.\n"); exit(1); } } signal(SIGQUIT, signal_handler); set_tty_flags(); eol_convention(1); skiped_crc_only_tag_1: switch (mode) { case RECV_BIN: if (do_crc_only) ret = bget(); else ret = bput(); break; case SEND_BIN: ret = bget(); break; case RECV_YAPP: ret = yput(); break; case SEND_YAPP: ret = yget(); break; case RECV_DIDADIT: ret = rput(); break; case SEND_DIDADIT: ret = rget(); break; } restore_defaults(); if (*err_msg) { fputs(err_msg, stderr); } exit(ret); return 0; } ax25/axgetput/axgetput.h000066400000000000000000000022071442776067000155260ustar00rootroot00000000000000/* * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ */ #ifndef AXGETPUT_H #define AXGETPUT_H extern int fdin; extern int fdout; extern int fderr; extern int fdin_is_pipe; extern int fdout_is_pipe; extern char myname[PATH_MAX+1]; extern char filename[PATH_MAX+1]; extern char err_msg[2048]; extern int is_stream; extern int mode; extern int do_crc_only; extern char c_eol; extern char *send_on_signal; /* modes */ #define RECV_BIN 1 /* #BIN# protocol: receive */ #define SEND_BIN 2 /* #BIN# protocol: send */ #define RECV_YAPP 3 /* yapp protocol: receive */ #define SEND_YAPP 4 /* yapp protocol: send */ #define RECV_DIDADIT 5 /* didadit protocol: receive */ #define SEND_DIDADIT 6 /* didadit protocol: send */ /* block sizes */ extern unsigned int BLOCKSIZ; #define BLOCKSIZ_MIN 1 /* not suggested */ #define BLOCKSIZ_DEFAULT 256 /* useful, because it fits in an ax25 frame */ #define BLOCKSIZ_MAX 1024 /* max. our buffer relies on it */ #endif /* AXGETPUT_H */ ax25/axgetput/includes.h000066400000000000000000000010561442776067000154740ustar00rootroot00000000000000#ifndef INCLUDES_H #define INCLUDES_H /* * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* INCLUDES_H */ ax25/axgetput/proto_bin.c000066400000000000000000000273371442776067000156660ustar00rootroot00000000000000/* * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ */ #include "includes.h" #include "proto_bin.h" #include "axgetput.h" #include "util.h" static int crctab[256]; static int crcbit[8] = { 0x9188, 0x48c4, 0x2462, 0x1231, 0x8108, 0x4084, 0x2042, 0x1021 }; /*---------------------------------------------------------------------------*/ static int init_crc(void) { int i,j; for (i = 0; i < 256; i++) { crctab[i] = 0; for (j = 0; j < 8; j++) { if ((i << j) & 0x80) crctab[i] = crctab[i] ^ crcbit[j]; } } return 0; } /*---------------------------------------------------------------------------*/ static int do_crc(char b, unsigned int crc) { return (crctab[(crc >> 8)] ^ ((crc << 8) | (b & 0xff))) & 0xffff; } static unsigned int calc_crc(char *buf, int n, unsigned crc) { while (--n >= 0) crc = do_crc(*buf++, crc); return crc; } /*---------------------------------------------------------------------------*/ static long parse_sfbin_date_to_unix(const char *s) { unsigned long x; sscanf(s, "%lX", &x); return date_dos2unix(((x << 16) >> 16), (x >> 16)); } /*---------------------------------------------------------------------------*/ static char * unix_to_sfbin_date_string(long gmt) { static char buf[9]; unsigned short s_time, s_date; date_unix2dos(((gmt == -1) ? time(NULL) : gmt), &s_time, &s_date); sprintf(buf, "%X", ((s_date << 16) + s_time)); return buf; } /*---------------------------------------------------------------------------*/ int bput(void) { struct stat statbuf; char buf[1024]; /* < signed int */ char filename_given[PATH_MAX]; unsigned long len_read_expected = 0L; unsigned long len_read_left; time_t file_time = 0L; unsigned int msg_crc = 0; unsigned int crc = 0; char *term_line = NULL; int last_line_had_CR = 0; int len_termline = 0; int len = 0; int fddata = fdout; int is_eof; int i; char *p; char *p_buf; #define save_close(x) { \ if (!fdout_is_pipe) \ close(x); \ } for (;;) { len = my_read(fdin, buf, sizeof(buf), &is_eof, "\r\n"); if (is_eof || len < 1) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); return 1; } if (buf[len-1] == '\n') { #if 0 p = "warning: received. not 8bit clean?\r"; secure_write(fdout, p, strlen(p)); #endif sprintf(err_msg, "bad EOL: \n"); return 1; } if (IS_BIN_ABORT(buf, len)) { sprintf(err_msg, "Aborted by user request\n"); return 1; } if (buf[len-1] == '\r' && len > 5 && !memcmp(buf, "#BIN#", 5)) { break; } if (len == sizeof(buf)) { sprintf(err_msg, "line to long\n"); return 1; } } buf[len-1] = 0; /* without trailing \r. and: string termination */ send_on_signal = bin_send_no_on_sig; /* parse #BIN arguments */ *filename_given = 0; p_buf = buf; for (i = 0; (p = strsep(&p_buf, "#")); i++) { switch (i) { case 0: case 1: break; case 2: if (*p) len_read_expected = (unsigned long ) atol(p); break; default: if (*p == '|') { msg_crc = (unsigned int ) atoi(p+1); } else if (*p == '$') { file_time = parse_sfbin_date_to_unix(p+1); } else { strncpy(filename_given, p, sizeof(filename_given)-1); filename_given[sizeof(filename_given)-1] = 0; } } } if (!fdout_is_pipe) { /* normal mode: store in given filename */ if (!*filename) { p = get_fixed_filename(filename_given, len_read_expected, msg_crc, 1); strncpy(filename, p, sizeof(filename)-1); filename[sizeof(filename)-1] = 0; } if (!stat(filename, &statbuf)) { /* file exist */ if (unlink(filename)) { sprintf(err_msg, "error: cannot unlink %s (%s)\n", filename, strerror(errno)); goto abort; } } fddata = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0640); if (fddata < 0) { sprintf(err_msg, "error: cannot open %s (%s)\n", filename, strerror(errno)); write(fderr, "\r#NO#\r", 6); return 1; } } if (!len_read_expected) { term_line = "***END\r"; len_termline = strlen(term_line); } /* say helo */ send_on_signal = bin_send_abort_on_sig; write(fderr, "\r#OK#\r", 6); len_read_left = len_read_expected; /* #bin# chechsum initialization */ init_crc(); for (;;) { len = my_read(fdin, buf, ((term_line || len_read_left > sizeof(buf)) ? sizeof(buf) : len_read_left), &is_eof, "\r"); if (len < 1) { save_close(fddata); sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); goto abort; } if (!len) { save_close(fddata); if (!term_line) { sprintf(err_msg, "error: unexpected end of file during read: %s\n", strerror(errno)); return 1; } return 0; } if (msg_crc) crc = calc_crc(buf, len, crc); if (buf[len-1] == '\r') { if (last_line_had_CR) { if (IS_BIN_ABORT(buf, len)) { /* "\r#ABORT#\r" was sent */ if (!fdout_is_pipe) { close(fddata); /* clean up */ unlink(filename); } return 1; } if (term_line && len == len_termline && !memcmp(buf, term_line, len_termline)) { /* successfully read until termination string */ break; } } last_line_had_CR = 1; } else { last_line_had_CR = 0; } if (!term_line) len_read_left -= len; if (secure_write(fddata, buf, len) == -1) { save_close(fddata); sprintf(err_msg, "error: write failed (%s)\n", strerror(errno)); goto abort; } /* nothing left? */ if (!term_line && len_read_left == 0L) break; if (is_eof) { if (!term_line && len_read_left) { save_close(fddata); goto abort; } break; } } if (crc != msg_crc) { sprintf(err_msg, "Invalid crc: computed %d, expected %d.\n", crc, msg_crc); /* don't unlink */ save_close(fddata); return 1; } if (!fdout_is_pipe) { close(fddata); if (file_time != 0L) { struct utimbuf utb; utb.modtime = file_time; utb.actime = time(NULL); utime(filename, &utb); } } send_on_signal = NULL; return 0; abort: sleep(1); write(fderr, "\r#ABORT#\r", 9); return 1; #undef save_close } /*---------------------------------------------------------------------------*/ int bget(void) { struct strlist { struct strlist *next; size_t len; char data[1]; /* actually a the address of char * pointer */ }; struct strlist *stored_file = NULL; struct strlist *sl_tail = NULL; struct strlist *sl; struct timeval timeout; struct stat statbuf; unsigned int crc = 0; char buf[1024]; fd_set readfds; int fddata = fdin; int len; unsigned long file_size = 0; unsigned long len_remains; int is_eof; time_t file_time = 0L; #define save_close(x) { \ if (!fdin_is_pipe) \ close(x); \ } #define store_line(s, len) { \ if (!(sl = (struct strlist *) malloc(sizeof(struct strlist *) + sizeof(size_t) + len))) \ return 1; \ sl->next = NULL; \ sl->len = len; \ memcpy(sl->data, s, len); \ if (!stored_file) { \ stored_file = sl; \ } else { \ sl_tail->next = sl; \ } \ sl_tail = sl; \ } if (BLOCKSIZ < 1 || BLOCKSIZ > sizeof(buf)) BLOCKSIZ = BLOCKSIZ_DEFAULT; init_crc(); if (!fdin_is_pipe && *filename) { fddata = open(filename, O_RDONLY); if (fddata == -1) { sprintf(err_msg, "error: cannot open %s (%s)\n", filename, strerror(errno)); return 1; } if (!fstat(fddata, &statbuf)) file_time = statbuf.st_mtime; else file_time = time(NULL); /* compute crc */ while ((len = read(fddata, buf, BLOCKSIZ)) > 0) { crc = calc_crc(buf, len, crc); file_size += len; } if (len < 0) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); close(fddata); return 1; } /* rewind */ if (lseek(fddata, 0L, SEEK_SET) != 0L) { sprintf(err_msg, "error: file io failed on lseek() (%s)\n", strerror(errno)); close(fddata); return 1; } sprintf(buf, "\r#BIN#%ld#|%d#$%s#%s\r", file_size, crc, unix_to_sfbin_date_string(file_time), get_fixed_filename(filename, file_size, crc, 1)); } else { file_time = time(NULL); if (!is_stream || do_crc_only) { sprintf(err_msg, "error: not enough memory\n"); while ((len = read(fddata, buf, sizeof(buf))) > 0) { crc = calc_crc(buf, len, crc); file_size += len; if (!do_crc_only) store_line(buf, len); } if (len < 0) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); close(fddata); return 1; } *err_msg = 0; sprintf(buf, "\r#BIN#%ld#|%d#$%s#%s\r", file_size, crc, unix_to_sfbin_date_string(file_time), get_fixed_filename(filename, file_size, crc, 1)); } else { sprintf(buf, "\r#BIN###$%s#%s\r", unix_to_sfbin_date_string(file_time), get_fixed_filename(filename, 0, 0, 1)); } /* * hack: check for #ABORT# from fdout (fd 1), because fddata (fd 0) is * our pipe we read the data from, which we actually tx. * believe me, it does work. */ fdin = fdout; } if (do_crc_only) { printf("File information for %s:\n", get_fixed_filename(filename, file_size, crc, 1)); printf(" size %ld bytes, crc %d, date %s (%ld)\n", file_size, crc, unix_to_sfbin_date_string(file_time), file_time); return 0; } send_on_signal = bin_send_abort_on_sig; if (secure_write(fdout, buf, strlen(buf)) == -1) { sprintf(err_msg, "error: write failed (%s)\n", strerror(errno)); save_close(fddata); return 1; } /* wait for answer */ for (;;) { /* * make sure we do not read from a pipe. fdout is also * assigned to the tty */ len = my_read(fdout, buf, sizeof(buf), &is_eof, "\r\n"); if (is_eof || len < 1) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); save_close(fddata); return 1; } if (buf[len-1] == '\n') { #if 0 char *p = "warning: received. not 8bit clean?\r"; secure_write(fdout, p, strlen(p)); #endif sprintf(err_msg, "bad EOL: \n"); goto abort; } else if (buf[len-1] != '\r') { sprintf(err_msg, "line to long\n"); continue; } if (IS_BIN_OK(buf, len)) break; if (IS_BIN_NO(buf, len)) { save_close(fddata); return 0; } if (IS_BIN_ABORT(buf, len)) { sprintf(err_msg, "Aborted by user request\n"); save_close(fddata); return 1; } } len_remains = file_size; timeout.tv_sec = 0; timeout.tv_usec = 0; for (;;) { char *p_buf; /* check for user \r#ABORT#\r on tty stream */ FD_ZERO(&readfds); FD_SET(fdin, &readfds); if (select(fdin+1, &readfds, NULL, NULL, &timeout) && FD_ISSET(fdin, &readfds)) { len = read(fdin, buf, sizeof(buf)); if (len < 0) { sprintf(err_msg, "read from tty failed (%s)\n", strerror(errno)); save_close(fddata); goto abort; } if (IS_BIN_ABORT(buf, len)) { sprintf(err_msg, "Aborted by user request\n"); save_close(fddata); return 1; } } /* read data */ if (!fdin_is_pipe || is_stream) { p_buf = buf; len = my_read(fddata, buf, ((len_remains > BLOCKSIZ || is_stream) ? BLOCKSIZ : len_remains), &is_eof, NULL); if (len < 1) { save_close(fddata); if (len < 0) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); goto abort; } break; } len_remains -= len; } else { p_buf = stored_file->data; len = stored_file->len; } /* write to client */ if (secure_write(fdout, p_buf, len) == -1) { sprintf(err_msg, "error: write failed (%s)\n", strerror(errno)); save_close(fddata); goto abort; } if (fdin_is_pipe && !is_stream) { sl = stored_file; stored_file = stored_file->next; if (!stored_file) is_eof = 1; free(sl); } if (!fdin_is_pipe && !len_remains) { if (read(fddata, buf, 1) == 1) { sprintf(err_msg, "Warning: file has grown in the meantime\n"); } is_eof = 1; break; } /* * need this because my_read may returned lenth != 0 (data to be written) * but also has detected EOF. */ if (is_eof) break; } sleep(10); return 0; abort: sleep(1); write(fderr, "\r#ABORT#\r", 9); return 1; #undef save_close } ax25/axgetput/proto_bin.h000066400000000000000000000017141442776067000156620ustar00rootroot00000000000000/* * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ */ #ifndef PROTO_BIN_H #define PROTO_BIN_H extern int bget(void); extern int bput(void); static const char bin_s_on_ok[] = "#OK#\r"; static const int bin_len_ok = sizeof(bin_s_on_ok)-1; static const char bin_s_on_no[] = "#NO#\r"; static const int bin_len_no = sizeof(bin_s_on_no)-1; static const char bin_s_on_abort[] = "#ABORT#\r"; static const int bin_len_abort = sizeof(bin_s_on_abort)-1; #define bin_send_no_on_sig "\r#NO#\r" #define bin_send_abort_on_sig "\r#ABORT#\r" #define IS_BIN_NO(s, len) \ len == bin_len_no && !memcmp(s, bin_s_on_no, bin_len_no) #define IS_BIN_OK(s, len) \ len == bin_len_ok && !memcmp(s, bin_s_on_ok, bin_len_ok) #define IS_BIN_ABORT(s, len) \ len == bin_len_abort && !memcmp(s, bin_s_on_abort, bin_len_abort) #endif /* PROTO_BIN_H */ ax25/axgetput/util.c000066400000000000000000000115021442776067000146330ustar00rootroot00000000000000/* * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ */ #include "includes.h" #include "axgetput.h" #include "util.h" /* Use private function because some platforms are broken, eg 386BSD */ static int Xtolower(int c) { return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; } /*---------------------------------------------------------------------------*/ char *strlwc(char *s) { char *p; for (p = s; (*p = Xtolower(*p)); p++) ; return s; } /*---------------------------------------------------------------------------*/ char *strtrim(char *s) { char *p; for (p = s; *p; p++) ; while (--p >= s && isspace(*p & 0xff)) ; p[1] = 0; return s; } /*---------------------------------------------------------------------------*/ int my_read(int fd, char *s, int len_max, int *eof, char *p_break) { struct timeval timeout; fd_set errfds; int len_got; *eof = 0; for (len_got = 0; len_got < len_max; ) { int len; char *s_curr = s + len_got; len = read(fd, s_curr, (p_break ? 1 : len_max)); if (len < 1) { if (len == -1 && errno == EAGAIN) { sleep(10); continue; } *eof = 1; /* * len = 0: normal eof. if we're looking for a string, return -1 since * we have'nt found */ return len == 0 && p_break ? -1 : (len_got ? len_got : len); } len_got += len; /* read once? or char in p_break found? */ if (!p_break || strchr(p_break, *s_curr)) break; } timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&errfds); FD_SET(fdout, &errfds); if (select(fd+1, NULL, NULL, &errfds, &timeout) && FD_ISSET(fd, &errfds)) *eof = 1; return len_got; } /*---------------------------------------------------------------------------*/ int secure_write(int fd, char *s, int len_write) { int len_write_left_curr = len_write; while (len_write_left_curr) { int len; len = write(fd, s, len_write_left_curr); if (len < 0) { if (len == -1 && errno == EAGAIN) { sleep(10); continue; } return -1; } if (len != len_write_left_curr) { /* queue busy.. */ sleep(10); } s += len; len_write_left_curr -= len; } return len_write; } /*---------------------------------------------------------------------------*/ char *get_fixed_filename(char *line, long size, unsigned int msg_crc, int generate_filename) { static char filename[1024]; char *p, *q, *r; *filename = 0; p = strchr(line, '\"'); if (p) { p++; } /* security.. */ q = strrchr((p ? p : line), '/'); if (q) { p = ++q; } q = strrchr((p ? p : line), '\\'); if (q) { p = ++q; } q = strrchr((p ? p : line), ':'); if (q) { p = ++q; } if (!p) { p = line; } while (*p && isspace(*p & 0xff)) p++; for (r = filename, q = p; *q; q++) { if (*q == '"' || *q == ';') { break; } *r++ = *q; } *r = 0; strtrim(filename); q = strrchr(filename, '.'); if (q) { if (!*(q+1)) { /* remove trailing dots */ *q = 0; } else { /* make suffix lowercase */ strlwc(q); } } if (!*filename && generate_filename) { sprintf(filename, "unknown-%ld%d%ld.bin", size, msg_crc, time(NULL)); } return filename; } /*---------------------------------------------------------------------------*/ /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ /* Linear day numbers of the respective 1sts in non-leap years. */ static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ /*---------------------------------------------------------------------------*/ long date_dos2unix(unsigned short time,unsigned short date) { int month,year; long secs; month = ((date >> 5) & 15)-1; year = date >> 9; secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && month < 2 ? 1 : 0)+3653); /* days since 1.1.70 plus 80's leap day */ return secs; } /*---------------------------------------------------------------------------*/ /* Convert linear UNIX date to a MS-DOS time/date pair. */ void date_unix2dos(int unix_date,unsigned short *time, unsigned short *date) { int day,year,nl_day,month; *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ (((unix_date/3600) % 24) << 11); day = unix_date/86400-3652; year = day/365; if ((year+3)/4+365*year > day) year--; day -= (year+3)/4+365*year; if (day == 59 && !(year & 3)) { nl_day = day; month = 2; } else { nl_day = (year & 3) || day <= 59 ? day : day-1; for (month = 0; month < 12; month++) if (day_n[month] > nl_day) break; } *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9); } ax25/axgetput/util.h000066400000000000000000000012711442776067000146420ustar00rootroot00000000000000/* * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ */ #ifndef UTIL_H #define UTIL_H extern char *strlwc(char *s); extern char *strtrim(char *s); extern int my_read(int fd, char *s, int len_max, int *eof, char *p_break); extern int secure_write(int fd, char *s, int len_write_left); extern char *get_fixed_filename(char *line, long size, unsigned int msg_crc, int generate_filename); extern long date_dos2unix(unsigned short time,unsigned short date); extern void date_unix2dos(int unix_date,unsigned short *time, unsigned short *date); #endif /* UTIL_H */ ax25/axparms.8000066400000000000000000000066141442776067000134250ustar00rootroot00000000000000.TH AXPARMS 8 "19 January 2017" Linux "Linux System Managers Manual" .SH NAME axparms \- Configure AX.25 interfaces. .SH SYNOPSIS .B axparms --assoc|--forward|--route|--setcall|--version ... .SH DESCRIPTION .LP The .B axparms command is designed to be a multi-function command that allows miscellaneous commands to be issued to the Linux AX.25 protocol layer. The different modes of the command are chosen by the first argument. Subsequent arguments depend upon this argument and so no generalised command format can be given. .SH "--assoc Argument" .LP The format of this option is: .LP .nf .B axparms --assoc .br .B axparms --assoc delete .br .B axparms --assoc policy [default|deny] .br .B axparms --assoc show .fi .LP This option manipulates the kernel uid/callsign mapping table, allowing callsigns to be associated and dis-associated with a user. The .B policy option permits the superuser to have all other uid's either default to the actual port name, or to block traffic. .LP At power up the table is blank and the policy is 'default', which is thus backward compatible. .SH "--forward Argument" .LP Allows the use of many receivers with one transmitter, known as packet forwarding in many systems. The format of this command is: .LP .nf .B axparms --forward .br .B axparms --forward delete .fi .LP Any packets to be transmitted on port portfrom will be transmitted on port portto. This will stay in force until the second form of the command is issued which will remove the association. .SH "--route Argument" .LP This option allows the internal AX.25 routing table to be manipulated. This table is available for reading in /proc/net/ax25_route, and will be built up dynamically by stations heard. However it is possible to add, delete and list entries via this option. .LP The formats of this option are: .LP .nf .B axparms --route add [] [--ipmode V|D] .br .B axparms --route del .fi .B axparms --route list .fi .LP Routes added via this command will not be removed from the internal routing table when they are \(lqold\(rq as normal entries are. The .B --ipmode option sets mode vc or mode datagram for this destination. .LP If the argument is set to \(lqdefault\(rq then this will set the default route for all outgoing AX.25 connections which will be used when there is no specific route to the required destination. .SH "--setcall Argument" .LP The format of this option is: .LP .B axparms --setcall .LP This changes the callsign associated at the given physical ax25 interface. .LP Cave: The interface name is not the symbolic port name from axports, but the real interface name (from ifconfig(8)): ax0, ax1, .., sp0, .., bpq0, ... etc.. .LP The change is permanent as long as the interface exists, or another \(lqaxparms --setcall\(rq is issued. .LP The interface has to exist already in order to use this option; it may be in state UP or DOWN. .SH "--version Argument" .LP This option displays the version of the AX.25 utilities that .B axparms belongs to. .SH FILES .LP /proc/net/bpqether .br /proc/net/ax25_calls .br /etc/ax25/axports .SH "SEE ALSO" .BR call (1), .BR getsockopt (2), .BR setsockopt (2), .BR ax25 (4), .BR axctl (8), .BR axports (5). .SH AUTHORS .nf Alan Cox GW4PTS .br Jonathan Naylor G4KLX .br Joerg Reuter DL1BKE .fi ax25/axparms.c000066400000000000000000000217611442776067000135000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" static void usage(void) { fprintf(stderr, "usage: axparms --assoc|--forward|--route|--setcall|--version ...\n"); } static void usageassoc(void) { fprintf(stderr, "usage: axparms --assoc show\n"); fprintf(stderr, "usage: axparms --assoc policy default|deny\n"); fprintf(stderr, "usage: axparms --assoc [callsign] [username]\n"); fprintf(stderr, "usage: axparms --assoc [callsign] delete\n"); } static void usageforward(void) { fprintf(stderr, "usage: axparms --forward \n"); fprintf(stderr, "usage: axparms --forward delete\n"); } static void usageroute(void) { fprintf(stderr, "usage: axparms --route add port callsign [digi ...] [--ipmode mode]\n"); fprintf(stderr, "usage: axparms --route del port callsign\n"); fprintf(stderr, "usage: axparms --route list\n"); } static void usagesetcall(void) { fprintf(stderr, "usage: axparms --setcall interface callsign\n"); } static int routes(int s, int argc, char *argv[], ax25_address *callsign) { struct ax25_routes_struct ax25_route; struct ax25_route_opt_struct ax25_opt; int i, j; int ip_mode = ' '; FILE* fp; char routebuf[80]; if (strcmp(argv[2], "add") == 0) { ax25_route.port_addr = *callsign; ax25_route.digi_count = 0; if (strcmp(argv[4], "default") == 0) { ax25_route.dest_addr = null_ax25_address; } else { if (ax25_aton_entry(argv[4], (char *)&ax25_route.dest_addr) == -1) return 1; } for (i = 5, j = 0; i < argc && j < 6; i++) { if (strncmp(argv[i], "--i", 3) == 0 || strncmp(argv[i], "-i", 2) == 0) { if (++i == argc) { fprintf(stderr, "axparms: -i must have a parameter\n"); return 1; } switch (*argv[i]) { case 'd': case 'D': ip_mode = 'D'; break; case 'v': case 'V': ip_mode = 'V'; break; default: ip_mode = ' '; break; } } else { if (ax25_aton_entry(argv[i], (char *)&ax25_route.digi_addr[j]) == -1) return 1; ax25_route.digi_count++; j++; } } if (ioctl(s, SIOCADDRT, &ax25_route) != 0) { perror("axparms: SIOCADDRT"); return 1; } ax25_opt.port_addr = *callsign; ax25_opt.dest_addr = ax25_route.dest_addr; ax25_opt.cmd = AX25_SET_RT_IPMODE; ax25_opt.arg = ip_mode; if (ioctl(s, SIOCAX25OPTRT, &ax25_opt) != 0) { perror("axparms: SIOCAX25OPTRT"); return 1; } } if (strcmp(argv[2], "del") == 0) { ax25_route.port_addr = *callsign; ax25_route.digi_count = 0; if (strcmp(argv[4], "default") == 0) { ax25_route.dest_addr = null_ax25_address; } else { if (ax25_aton_entry(argv[4], (char *)&ax25_route.dest_addr) == -1) return 1; } if (ioctl(s, SIOCDELRT, &ax25_route) != 0) { perror("axparms: SIOCDELRT"); return 1; } } if (strcmp(argv[2], "list") == 0) { fp = fopen(PROC_AX25_ROUTE_FILE, "r"); if (fp == NULL) { fprintf(stderr, "axparms: route: cannot open %s\n", PROC_AX25_ROUTE_FILE); return 1; } while (fgets(routebuf,80,fp)) printf("%s", routebuf); puts(""); } return 0; } static int setifcall(int s, char *ifn, char *name) { char call[7]; struct ifreq ifr; if (ax25_aton_entry(name, call) == -1) return 1; strcpy(ifr.ifr_name, ifn); memcpy(ifr.ifr_hwaddr.sa_data, call, 7); ifr.ifr_hwaddr.sa_family = AF_AX25; if (ioctl(s, SIOCSIFHWADDR, &ifr) != 0) { perror("axparms: SIOCSIFHWADDR"); return 1; } return 0; } static int associate(int s, int argc, char *argv[]) { char buffer[80], *u, *c, *endp; struct sockaddr_ax25 sax25; struct passwd *pw; uid_t uid; int opt; FILE *fp; if (strcmp(argv[2], "show") == 0) { if (argc < 3) { usageassoc(); exit(1); } fp = fopen(PROC_AX25_CALLS_FILE, "r"); if (fp == NULL) { fprintf(stderr, "axparms: associate: cannot open %s\n", PROC_AX25_CALLS_FILE); return 1; } fgets(buffer, 80, fp); printf("Userid Callsign\n"); while (fgets(buffer, 80, fp) != NULL) { u = strtok(buffer, " \t\n"); c = strtok(NULL, " \t\n"); pw = getpwuid(atoi(u)); if (pw != NULL) printf("%-10s %s\n", pw->pw_name, c); } fclose(fp); return 0; } if (strcmp(argv[2], "policy") == 0) { if (argc < 4) { usageassoc(); exit(1); } if (strcmp(argv[3], "default") == 0) { opt = AX25_NOUID_DEFAULT; if (ioctl(s, SIOCAX25NOUID, &opt) == -1) { perror("axparms: SIOCAX25NOUID"); return 1; } return 0; } if (strcmp(argv[3], "deny") == 0) { opt = AX25_NOUID_BLOCK; if (ioctl(s, SIOCAX25NOUID, &opt) == -1) { perror("axparms: SIOCAX25NOUID"); return 1; } return 0; } fprintf(stderr, "axparms: associate: 'default' or 'deny' required\n"); return 1; } if (argc < 4) { usageassoc(); exit(1); } if (ax25_aton_entry(argv[2], (char *)&sax25.sax25_call) == -1) { fprintf(stderr, "axparms: associate: invalid callsign %s\n", argv[2]); return 1; } if (strcmp(argv[3], "delete") == 0) { if (ioctl(s, SIOCAX25DELUID, &sax25) == -1) { perror("axparms: SIOCAX25DELUID"); return 1; } return 0; } /* * Tolerate spaces following a UID, it may happen in scripts */ errno = 0; uid = strtol(argv[3], &endp, 0); if (!errno && (*endp == '\0' || isspace(*endp))) { sax25.sax25_uid = uid; } else { pw = getpwnam(argv[3]); if (pw == NULL) { fprintf(stderr, "axparms: associate: unknown username %s\n", argv[3]); return 1; } sax25.sax25_uid = pw->pw_uid; } if (ioctl(s, SIOCAX25ADDUID, &sax25) == -1) { perror("axparms: SIOCAX25ADDUID"); return 1; } return 0; } static int forward(int s, int argc, char *argv[]) { #ifdef HAVE_AX25_FWD_STRUCT struct ax25_fwd_struct ax25_fwd; char *addr; if (argc < 4) { usageforward(); exit(1); } if (ax25_config_load_ports() == 0) { fprintf(stderr, "axparms: no AX.25 port data configured\n"); return 1; } addr = ax25_config_get_addr(argv[2]); if (addr == NULL) { fprintf(stderr, "axparms: invalid port name - %s\n", argv[2]); return 1; } if (ax25_aton_entry(addr, (char *)&ax25_fwd.port_from) == -1) { fprintf(stderr, "axparms: invalid port name - %s\n", argv[2]); return 1; } if (strcmp(argv[3], "delete") == 0) { if (ioctl(s, SIOCAX25DELFWD, &ax25_fwd) == -1) { perror("axparms: SIOCAX25DELFWD"); return 1; } return 0; } addr = ax25_config_get_addr(argv[3]); if (addr == NULL) { fprintf(stderr, "axparms: invalid port name - %s\n", argv[3]); return 1; } if (ax25_aton_entry(addr, (char *)&ax25_fwd.port_to) == -1) { fprintf(stderr, "axparms: invalid port name - %s\n", argv[3]); return 1; } if (ioctl(s, SIOCAX25ADDFWD, &ax25_fwd) == -1) { perror("axparms: SIOCAX25ADDFWD"); return 1; } #else fprintf(stderr, "axparms: Not compiled in with forwarding option.\n"); #endif /* HAVE_AX25_FWD_STRUCT */ return 0; } int main(int argc, char **argv) { ax25_address callsign; int s, n; char *addr; if (argc == 1) { usage(); return 1; } if (strncmp(argv[1], "--v", 3) == 0 || strncmp(argv[1], "-v", 2) == 0) { printf("axparms: %s\n", FULL_VER); return 0; } if (strncmp(argv[1], "--a", 3) == 0 || strncmp(argv[1], "-a", 2) == 0) { if (argc < 3) { usageassoc(); return 1; } s = socket(AF_AX25, SOCK_SEQPACKET, 0); if (s < 0) { perror("axparms: socket"); return 1; } n = associate(s, argc, argv); close(s); return n; } if (strncmp(argv[1], "--f", 3) == 0 || strncmp(argv[1], "-f", 2) == 0) { if (argc == 2) { usageforward(); return 1; } s = socket(AF_AX25, SOCK_SEQPACKET, 0); if (s < 0) { perror("axparms: socket"); return 1; } n = forward(s, argc, argv); close(s); return n; } if (strncmp(argv[1], "--r", 3) == 0 || strncmp(argv[1], "-r", 2) == 0) { if (argc < 3 ) { usageroute(); return 1; } if (strcmp(argv[2], "add") != 0 && strcmp(argv[2], "del") != 0 && strcmp(argv[2], "list") != 0) { usageroute(); return 1; } if (argc < 5 && strcmp(argv[2], "list") != 0) { usageroute(); return 1; } if (ax25_config_load_ports() == 0) { fprintf(stderr, "axparms: no AX.25 port data configured\n"); return 1; } addr = ax25_config_get_addr(argv[3]); if (addr == NULL) { fprintf(stderr, "axparms: invalid port name - %s\n", argv[3]); return 1; } if (ax25_aton_entry(addr, callsign.ax25_call) == -1) return 1; s = socket(AF_AX25, SOCK_SEQPACKET, 0); if (s < 0) { perror("axparms: socket"); return 1; } n = routes(s, argc, argv, &callsign); close(s); return n; } if (strncmp(argv[1], "--s", 3) == 0 || strncmp(argv[1], "-s", 2) == 0) { if (argc != 4) { usagesetcall(); return 1; } s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("axparms: socket"); return 1; } n = setifcall(s, argv[2], argv[3]); close(s); return n; } usage(); return 1; } ax25/axspawn.8000066400000000000000000000112301442776067000134210ustar00rootroot00000000000000.TH AXSPAWN 8 "13 April 2008" Linux "Linux System Managers Manual" .SH NAME axspawn \- Allow automatic login to a Linux system. .SH SYNOPSIS .B axspawn [--pwprompt PR0MPT, -p PR0MPT] [--changeuser, -c] [--rootlogin, -r] [--only-md5] [--wait, -w] .SH DESCRIPTION .LP .B Axspawn will check if the peer is an AX.25 connect, the callsign a valid Amateur Radio callsign, strip the SSID, check if UID/GID are valid, allow a password-less login if the password-entry in /etc/passwd is \(lq+\(rq or empty; in every other case login will prompt for a password. .LP .B Axspawn can create user accounts automatically. You may specify the user shell, first and maximum user id, group ID in the config file and (unlike WAMPES) create a file \(lq/etc/ax25/ax25.profile\(rq which will be copied to ~/.profile. .SH SECURITY .LP Auto accounting is a security problem by definition. Unlike WAMPES, which creates an empty password field, Axspawn adds an \(lqimpossible\(rq ('+') password to /etc/passwd. Login gets called with the \(lq-f\(rq option, thus new users have the chance to login without a password. (I guess this won't work with the shadow password system). .LP Of course .B axspawn does callsign checking: Only letters and numbers are allowed, the callsign must be longer than 4 characters and shorter than 6 characters (without SSID). There must be at least one digit, and max. two digits within the call. The SSID must be within the range of 0 and 15. Please drop me a note if you know a valid Amateur Radio callsign that does not fit this pattern _and_ can be represented correctly in AX.25. .LP axspawn also has the well known authentication mechanisms of the AX.25 bbs .B baycom (sys) and .B md5 standards. axspawn searches in /etc/ax25/bcpasswd (first) and ~user/.bcpasswd (second) for a match of the required authentication mechanism and password. md5 and baycom passwords may differ. md5 passwords gain over baycom passwords. Note: you could "lock" special "friends" out by specifying an empty password in /etc/ax25/bcpasswd (line "n0call:md5:"). -> md5 Passwords are enforced. But the length is shorter than the minimum (len 8 for md5, len 20 for baycom); user's password file is not searched because in /etc/ax25/bcpasswd its already found.. Syntax and caveeats for /etc/ax25/bcpasswd: - Has to be a regular file (no symlink). Not world-readable/writable. - Example lines: # Thomas dl9sau:md5:abcdefgh # Test te1st:sys:12345678901234567890 # root root:md5:ziz7AoxuAt6jeuthTheexet0uDa9iefuAeph3eelAetahmi0 # misconfiguration: thisbadlineisignored # With this line systempasswordonly # .. axspan will not look in user's homedir for his .bcpasswd Syntax and caveeats for user's .bcpasswd in his $HOME: - Has to be a regular file (no symlink). Neither group- nor world- read-/writable. Has to be owned by the user or uid 0 (root). - Example lines: # could be shorter md5:abcdefgh # should be longer sys:12345678901234567890 .SH OPTIONS .TP 5 .B -p DB0FHN or --pwprompt DB0FHN While baycom or md5 password authentication (see above), the password prompt is set to the first argument (DB0FHN in this example). This may be needed for some packet-radio terminal programs for detecting the password prompt properly. .TP 5 .B -c, --changeuser Allow connecting ax25 users to change their username for login. They'll be asked for their real login name. .TP 5 .B -e, --embedded Special treatment for axspawn on non-standard conform embedded devices. I.e. openwrt has no true /bin/login: if you use it as a real login program, it raises a security hole. .TP 5 .B -r, --rootlogin Permit login as user root. Cave: only md5 or baycom style is allowed; no plaintext password. .TP 5 .B --only-md5 Insist in md5 authentication during login. If no password for the user is found, or it is not md5, then no other login mechanism is granted. This option, in combination with -c and -r, may be a useful configuration for systems where no ax25 user accounts are available, but you as sysop would like to have a login access for your administrative tasks. .TP 5 .B -w, --wait Eats the first line the user sends. This feature is useful if you have TCP VC connects to the same Call+SSID. It is now obsolete, because ax25d is the right place for this and implements this functionality better. .TP 5 Theses are options and not part of the preferences because you _may_ like to have on every interface definition in ax25d.conf (where axspawn is started from) a different behaviour. .SH FILES .nf /etc/passwd .br /etc/ax25/ax25.profile .br /etc/ax25/axspawn.conf .fi /etc/ax25/bcpasswd .fi ~/.bcpasswd .fi .SH "SEE ALSO" .BR axspawn.conf (5), .BR ax25d (8). .SH AUTHOR Joerg Reuter DL1BKE ax25/axspawn.c000066400000000000000000001445131442776067000135070ustar00rootroot00000000000000/* * axspawn.c - run a program from ax25d. * * Copyright (c) 1996 Joerg Reuter DL1BKE (jreuter@poboxes.com) * * This program is a hack. * * 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. * * It might even kill your cat... ;-) * * Status: alpha (still...) * * usage: change the "default" lines in your /usr/local/etc/ax25d.conf: * * default * * * * * 1 root /usr/local/sbin/axspawn axspawn * * a line like this would wait for an incoming info frame first. * * default * * * * * 1 root /usr/local/sbin/axspawn axspawn --wait * * The program will check if the peer is an AX.25 socket, the * callsign is a valid amateur radio callsign, strip the SSID, * check if UID/GID are valid, allow a password-less login if the * password-entry in /etc/passwd is "+" or empty; in every other case * login will prompt for a password. * * Still on my TODO list: a TheNet compatible or MD5 based * authentication scheme... Won't help you if you changed the "+"-entry * in /etc/passwd to a valid passord and login with telnet, though. * A better solution could be a small program called from .profile. * * Axspawn can create user accounts automatically. You may specify * the user shell, first and maximum user id, group ID in the config * file and (unlike WAMPES) create a file "/usr/local/etc/ax25.profile" * which will be copied to ~/.profile. * * This is an example for the config file: * * # this is /usr/local/etc/axspawn.conf * # * # allow automatic creation of user accounts * create yes * * # allow empty password field (so user may login via telnet, or pop3 their * # mail) [default no] * create_empty_password no * * # pwcheck method: password or call or group [default: password] * # "password" means, that passwords with '+' force a login without * # prompting for a password (old behaviour; backward compatibility). * # "call" means, that ham calls via ax25/netrom/rose/.. should be able * # to login without password, even if it's set (for e.g. to secure * # from abuse of inet connections) * # "group" means, that if the gid of the user matches the configured * # default user_gid, then the login is granted without password. * #pwcheck call * #pwcheck group * #pwcheck password * * # create with system utility useradd(8)? [default no] * create_with_useradd no * * # guest user if above is 'no' or everything else fails. Disable with "no" * guest ax25 * * # group id or name for autoaccount * group ax25 * * # first user id to use * first_uid 400 * * # maximum user id * max_uid 2000 * * # where to add the home directory for the new user * home /home/hams * * # secure homedirectories (g-rwx) * #secure_home yes * * # user's shell * shell /bin/bash * * # bind user id to callsign for outgoing connects. * associate yes * * SECURITY: * * Umm... auto accounting is a security problem by definition. Unlike * WAMPES, which creates an empty password field, Axspawn adds an * "impossible" ('+') password to /etc/passwd. Login gets called with * the "-f" option, thus new users have the chance to login without * a password. (I guess this won't work with the shadow password system). * News: there are good reasons to use empty password fields. For e.g. * at our mailbox-system db0tud where we have no inet access, so * every user is a ham, not a hacker. We need the empty password * so new may use ax25 for automatical account generation, and then * could login via telnet or poll their mail with pop3. * But i acknowledge, that this package will mostly used by hams who * use their computer for both, inet and ampr. So the new option * create_empty_password is not the default, but a feature * needed because we won't patch this code after a new release. * the useradd method and compression implementation is the the work * of thomas , the finetune is by me. GPL * the compression code initialy was for tnt by dl4ybg (GPL). * - dl9sau for db0tud team. * * The "associate" option has to be used with great care. If a user * logs on it removes any existing callsign from the translation table * for this userid and replaces it with the callsign and SSID of the * user. This will happen with multiple connects (same callsign, * different SSIDs), too. Unless you want your users to be able * to call out from your machine disable "associate". * * Of course Axspawn does callsign checking: Only letters and numbers * are allowed, the callsign must be longer than 4 characters and * shorter than 6 characters (without SSID). There must be at least * one digit, and max. two digits within the call. The SSID must * be within the range of 0 and 15. Please drop me a note if you * know a valid Amateur Radio (sic!) callsign that does not fit this * pattern _and_ can be represented correctly in AX.25. */ /* removed -h from login command as it was causing hostname lookups with new login/libc - Terry, vk2ktj. */ /*#define QUEUE_DELAY 400 / * 400 usec */ #define QUEUE_DELAY 100000 /* 10 msec */ #define USERPROFILE ".profile" #define PASSWDFILE "/etc/passwd" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #include "axspawn.h" #include "access.h" #define MAXLEN strlen("DB0PRA-15") #define MINLEN strlen("KA9Q") #define AX_PACLEN 256 #define NETROM_PACLEN 236 #define ROSE_PACLEN 128 #define IS_DIGIT(x) ( (x >= '0') && (x <= '9') ) #define IS_LETTER(x) ( (x >= 'A') && (x <= 'Z') ) #define MSG_NOCALL "Sorry, you are not allowed to connect.\n" #define MSG_CANNOTFORK "Sorry, system is overloaded.\n" #define MSG_NOPTY "Sorry, all channels in use.\n" #define MSG_NOTINDBF "Sorry, you are not in my database.\n" #define write_ax25_static_line(s) { \ char *msg = strdup(s); \ if (msg) { \ write_ax25(msg, strlen(msg), 1); \ free(msg); \ } \ } #define EXITDELAY 10 #define USERADD_CONF "/etc/default/useradd" struct huffencodtab { unsigned short code; unsigned short len; }; struct huffdecodtab { unsigned short node1; unsigned short node2; }; /* huffman encoding table */ static struct huffencodtab huffencodtab[] = { {0xab2c, 15}, {0xaa84, 15}, {0x9fc4, 15}, {0xab3c, 15}, {0xab1c, 15}, {0xaafc, 15}, {0xaaec, 15}, {0xaad4, 15}, {0xaab4, 15}, {0xf340, 10}, {0xaaa4, 15}, {0x7d64, 15}, {0xaadc, 15}, {0xf400, 7}, {0xaa94, 15}, {0x9ff4, 15}, {0x9fd4, 15}, {0x7d74, 15}, {0xab44, 15}, {0xab34, 15}, {0xab24, 15}, {0xab14, 15}, {0xab04, 15}, {0xaaf4, 15}, {0xaae4, 15}, {0xab60, 14}, {0xab0c, 15}, {0xaacc, 15}, {0xaabc, 15}, {0xaaac, 15}, {0xaa9c, 15}, {0xaa8c, 15}, {0xc000, 3}, {0x3a80, 9}, {0xabc0, 10}, {0x0060, 11}, {0x7d40, 12}, {0xab5c, 14}, {0x0000, 12}, {0xab58, 14}, {0x7c00, 9}, {0x3c80, 9}, {0x7d00, 11}, {0x0010, 12}, {0x1200, 7}, {0x7a00, 7}, {0xb800, 6}, {0x3200, 7}, {0x2200, 7}, {0xf600, 8}, {0x3d00, 8}, {0x9e00, 9}, {0xbd80, 9}, {0x7c80, 9}, {0x0080, 9}, {0xaa00, 9}, {0xbd00, 9}, {0x9f00, 9}, {0x0300, 8}, {0xab78, 13}, {0xab68, 13}, {0x3c00, 9}, {0x3000, 9}, {0x0020, 11}, {0x7d50, 12}, {0x3800, 7}, {0x7800, 7}, {0x9c00, 7}, {0xfe00, 7}, {0x2400, 6}, {0xbc00, 8}, {0x0200, 8}, {0x0100, 8}, {0xf100, 8}, {0x0040, 11}, {0x3100, 8}, {0xf200, 8}, {0x3400, 7}, {0x1c00, 7}, {0x1e00, 7}, {0xbe00, 7}, {0xaba0, 11}, {0x3e00, 7}, {0x1400, 6}, {0x3600, 7}, {0xf380, 9}, {0xf080, 9}, {0x2000, 8}, {0xfc00, 8}, {0x9f80, 10}, {0x9e80, 9}, {0xab90, 12}, {0x3b80, 9}, {0xab80, 12}, {0xab54, 14}, {0x3a50, 13}, {0xab50, 14}, {0xa000, 5}, {0x1800, 6}, {0x9800, 6}, {0x7000, 5}, {0x4000, 3}, {0x0400, 6}, {0xac00, 6}, {0xf800, 6}, {0x6000, 4}, {0x3a00, 10}, {0xfd00, 8}, {0x2800, 5}, {0xb000, 6}, {0x8000, 4}, {0xb400, 6}, {0x1000, 7}, {0x7d20, 12}, {0xe000, 5}, {0x9000, 5}, {0xe800, 5}, {0x0800, 5}, {0xf700, 8}, {0xa800, 7}, {0x7d80, 9}, {0xf300, 10}, {0x7e00, 7}, {0xab48, 14}, {0x3a48, 13}, {0xab4c, 14}, {0x3a60, 12}, {0x9ffc, 15}, {0x9fec, 15}, {0x2100, 8}, {0x9fdc, 15}, {0x9fcc, 15}, {0xf000, 9}, {0x7d7c, 15}, {0x7d6c, 15}, {0x3a40, 14}, {0xab40, 15}, {0xab38, 15}, {0xab30, 15}, {0xab28, 15}, {0xab20, 15}, {0xab18, 15}, {0xab70, 13}, {0xab10, 15}, {0xab08, 15}, {0xab00, 15}, {0xaaf8, 15}, {0xaaf0, 15}, {0x3b00, 9}, {0xaae8, 15}, {0xaae0, 15}, {0xaad8, 15}, {0xaad0, 15}, {0xab64, 14}, {0x7d30, 12}, {0xaac8, 15}, {0xaac0, 15}, {0xaab8, 15}, {0xaab0, 15}, {0xaaa8, 15}, {0xaaa0, 15}, {0xaa98, 15}, {0xaa90, 15}, {0xaa88, 15}, {0xaa80, 15}, {0x9ff8, 15}, {0x9ff0, 15}, {0x9fe8, 15}, {0x9fe0, 15}, {0x9fd8, 15}, {0x9fd0, 15}, {0x9fc8, 15}, {0x9fc0, 15}, {0x7d78, 15}, {0x7d70, 15}, {0x3a58, 13}, {0x7d68, 15}, {0x7d60, 15}, {0xab46, 15}, {0xab42, 15}, {0xab3e, 15}, {0xab3a, 15}, {0xab36, 15}, {0xab32, 15}, {0xab2e, 15}, {0xab2a, 15}, {0xab26, 15}, {0xab22, 15}, {0xab1e, 15}, {0xab1a, 15}, {0xab16, 15}, {0xab12, 15}, {0xab0e, 15}, {0xab0a, 15}, {0xab06, 15}, {0xab02, 15}, {0xaafe, 15}, {0xaafa, 15}, {0xaaf6, 15}, {0xaaf2, 15}, {0xaaee, 15}, {0xaaea, 15}, {0xaae6, 15}, {0xaae2, 15}, {0xaade, 15}, {0xaada, 15}, {0xaad6, 15}, {0xaad2, 15}, {0xaace, 15}, {0xaaca, 15}, {0xaac6, 15}, {0xaac2, 15}, {0xaabe, 15}, {0xaaba, 15}, {0xaab6, 15}, {0xaab2, 15}, {0xaaae, 15}, {0xaaaa, 15}, {0xaaa6, 15}, {0xaaa2, 15}, {0xaa9e, 15}, {0x3a70, 12}, {0xaa9a, 15}, {0xaa96, 15}, {0xaa92, 15}, {0x3080, 9}, {0xaa8e, 15}, {0xaa8a, 15}, {0xaa86, 15}, {0xaa82, 15}, {0x9ffe, 15}, {0x9ffa, 15}, {0x9ff6, 15}, {0x9ff2, 15}, {0x9fee, 15}, {0x9fea, 15}, {0x9fe6, 15}, {0x9fe2, 15}, {0x9fde, 15}, {0x9fda, 15}, {0x9fd6, 15}, {0x9fd2, 15}, {0x9fce, 15}, {0x9fca, 15}, {0x9fc6, 15}, {0x9fc2, 15}, {0x7d7e, 15}, {0x7d7a, 15}, {0x7d76, 15}, {0x7d72, 15}, {0x7d6e, 15}, {0x7d6a, 15}, {0x7d66, 15}, {0x7d62, 15}, {0x3a46, 15}, {0x3a44, 15} }; /* huffman decoding table */ static struct huffdecodtab huffdecodtab[] = { { 79, 1},{ 2, 66},{ 24, 3},{ 4,208}, {292, 5}, { 6,298}, {317, 7}, { 16, 8}, { 9,173}, { 10,116}, { 41, 11}, { 12, 37}, {125, 13}, {357, 14}, { 15,438}, { 0, 0}, {229, 17}, { 18, 46}, { 19, 61}, { 20, 99}, { 21,161}, {404, 22}, { 23,483}, { 1, 0}, {306, 25}, {313, 26}, {294, 27}, {245, 28}, {221, 29}, {231, 30}, {277, 31}, { 32,103}, { 33,108}, { 34,339}, {421, 35}, { 36,500}, { 2, 0}, {122, 38}, {353, 39}, { 40,434}, { 3, 0}, {131, 42}, {128, 43}, {361, 44}, { 45,442}, { 4, 0}, { 56, 47}, { 52, 48}, {135, 49}, {370, 50}, { 51,450}, { 5, 0}, {138, 53}, {375, 54}, { 55,454}, { 6, 0}, {148, 57}, { 58, 94}, {381, 59}, { 60,460}, { 7, 0}, { 75, 62}, { 63,152}, {392, 64}, { 65,469}, { 8, 0}, {164, 67}, {311, 68}, { 69,246}, { 70, 97}, {253, 71}, {257, 72}, { 73,270}, {319, 74}, { 9, 0}, { 76,155}, {396, 77}, { 78,473}, { 10, 0}, {165, 80}, {296, 81}, {300, 82}, {295, 83}, {206, 84}, { 85,320}, {193, 86}, { 87,318}, {199, 88}, {184, 89}, { 90,112}, { 91,346}, {430, 92}, { 93,508}, { 11, 0}, {379, 95}, { 96,458}, { 12, 0}, { 98,218}, { 13, 0}, {100,158}, {400,101}, {102,478}, { 14, 0}, {331,104}, {105,328}, {408,106}, {107,487}, { 15, 0}, {109,336}, {417,110}, {111,496}, { 16, 0}, {113,343}, {425,114}, {115,504}, { 17, 0}, {117,141}, {118,186}, {119,321}, {351,120}, {121,432}, { 18, 0}, {355,123}, {124,436}, { 19, 0}, {359,126}, {127,440}, { 20, 0}, {364,129}, {130,444}, { 21, 0}, {132,145}, {368,133}, {134,448}, { 22, 0}, {372,136}, {137,452}, { 23, 0}, {377,139}, {140,456}, { 24, 0}, {142,234}, {143,236}, {144,383}, { 25, 0}, {366,146}, {147,446}, { 26, 0}, {387,149}, {385,150}, {151,462}, { 27, 0}, {390,153}, {154,467}, { 28, 0}, {394,156}, {157,471}, { 29, 0}, {398,159}, {160,475}, { 30, 0}, {402,162}, {163,481}, { 31, 0}, { 32, 0}, {175,166}, {214,167}, {211,168}, {169,195}, {243,170}, {171,281}, {286,172}, { 33, 0}, {265,174}, { 34, 0}, {176,202}, {177,315}, {178,297}, {179,232}, {180,252}, {181,228}, {189,182}, {255,183}, { 35, 0}, {185,242}, { 36, 0}, {284,187}, {192,188}, { 37, 0}, {190,241}, {191,201}, { 38, 0}, { 39, 0}, {194,227}, { 40, 0}, {196,267}, {197,220}, {237,198}, { 41, 0}, {200,309}, { 42, 0}, { 43, 0}, {203,260}, {204,268}, {308,205}, { 44, 0}, {244,207}, { 45, 0}, {304,209}, {210,223}, { 46, 0}, {212,258}, {238,213}, { 47, 0}, {215,303}, {216,249}, {273,217}, { 48, 0}, {219,316}, { 49, 0}, { 50, 0}, {222,278}, { 51, 0}, {224,264}, {250,225}, {230,226}, { 52, 0}, { 53, 0}, { 54, 0}, { 55, 0}, { 56, 0}, { 57, 0}, {251,233}, { 58, 0}, {363,235}, { 59, 0}, { 60, 0}, { 61, 0}, {239,256}, {240,480}, { 62, 0}, { 63, 0}, { 64, 0}, { 65, 0}, { 66, 0}, { 67, 0}, {299,247}, {275,248}, { 68, 0}, { 69, 0}, { 70, 0}, { 71, 0}, { 72, 0}, {271,254}, { 73, 0}, { 74, 0}, { 75, 0}, { 76, 0}, {259,269}, { 77, 0}, {293,261}, {262,263}, { 78, 0}, { 79, 0}, { 80, 0}, {279,266}, { 81, 0}, { 82, 0}, { 83, 0}, { 84, 0}, { 85, 0}, {342,272}, { 86, 0}, {274,335}, { 87, 0}, {276,302}, { 88, 0}, { 89, 0}, { 90, 0}, {283,280}, { 91, 0}, {374,282}, { 92, 0}, { 93, 0}, {291,285}, { 94, 0}, {301,287}, {288,326}, {323,289}, {290,427}, { 95, 0}, { 96, 0}, { 97, 0}, { 98, 0}, { 99, 0}, {100, 0}, {101, 0}, {102, 0}, {103, 0}, {104, 0}, {105, 0}, {106, 0}, {107, 0}, {108, 0}, {305,307}, {109, 0}, {110, 0}, {111, 0}, {112, 0}, {310,384}, {113, 0}, {312,314}, {114, 0}, {115, 0}, {116, 0}, {117, 0}, {118, 0}, {119, 0}, {120, 0}, {121, 0}, {122, 0}, {322,325}, {123, 0}, {349,324}, {124, 0}, {125, 0}, {327,476}, {126, 0}, {406,329}, {330,485}, {127, 0}, {412,332}, {410,333}, {334,489}, {128, 0}, {129, 0}, {415,337}, {338,494}, {130, 0}, {419,340}, {341,498}, {131, 0}, {132, 0}, {423,344}, {345,502}, {133, 0}, {428,347}, {348,506}, {134, 0}, {350,510}, {135, 0}, {352,433}, {136, 0}, {354,435}, {137, 0}, {356,437}, {138, 0}, {358,439}, {139, 0}, {360,441}, {140, 0}, {362,443}, {141, 0}, {142, 0}, {365,445}, {143, 0}, {367,447}, {144, 0}, {369,449}, {145, 0}, {371,451}, {146, 0}, {373,453}, {147, 0}, {148, 0}, {376,455}, {149, 0}, {378,457}, {150, 0}, {380,459}, {151, 0}, {382,461}, {152, 0}, {153, 0}, {154, 0}, {386,463}, {155, 0}, {388,464}, {389,466}, {156, 0}, {391,468}, {157, 0}, {393,470}, {158, 0}, {395,472}, {159, 0}, {397,474}, {160, 0}, {399,477}, {161, 0}, {401,479}, {162, 0}, {403,482}, {163, 0}, {405,484}, {164, 0}, {407,486}, {165, 0}, {409,488}, {166, 0}, {411,490}, {167, 0}, {413,491}, {414,493}, {168, 0}, {416,495}, {169, 0}, {418,497}, {170, 0}, {420,499}, {171, 0}, {422,501}, {172, 0}, {424,503}, {173, 0}, {426,505}, {174, 0}, {175, 0}, {429,507}, {176, 0}, {431,509}, {177, 0}, {178, 0}, {179, 0}, {180, 0}, {181, 0}, {182, 0}, {183, 0}, {184, 0}, {185, 0}, {186, 0}, {187, 0}, {188, 0}, {189, 0}, {190, 0}, {191, 0}, {192, 0}, {193, 0}, {194, 0}, {195, 0}, {196, 0}, {197, 0}, {198, 0}, {199, 0}, {200, 0}, {201, 0}, {202, 0}, {203, 0}, {204, 0}, {205, 0}, {206, 0}, {207, 0}, {208, 0}, {209, 0}, { 0,465}, {210, 0}, {211, 0}, {212, 0}, {213, 0}, {214, 0}, {215, 0}, {216, 0}, {217, 0}, {218, 0}, {219, 0}, {220, 0}, {221, 0}, {222, 0}, {223, 0}, {224, 0}, {225, 0}, {226, 0}, {227, 0}, {228, 0}, {229, 0}, {230, 0}, {231, 0}, {232, 0}, {233, 0}, {234, 0}, {235, 0}, { 0,492}, {236, 0}, {237, 0}, {238, 0}, {239, 0}, {240, 0}, {241, 0}, {242, 0}, {243, 0}, {244, 0}, {245, 0}, {246, 0}, {247, 0}, {248, 0}, {249, 0}, {250, 0}, {251, 0}, {252, 0}, {253, 0}, {512,511}, {254, 0}, {255, 0} }; static unsigned char mask8tab[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; static unsigned short mask16tab[16] = { 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100, 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001 }; static char policy_add_user = 1; static char policy_add_empty_password; static char policy_add_prog_useradd; static char policy_guest = 1; static char policy_associate; static char pwcheck = 1; static char secure_home; static gid_t user_gid = 400; static char *user_shell = "/bin/bash"; static int user_shell_configured = 0; static char *start_home = "/home/hams"; static char *guest = "guest"; static int start_uid = 400; static int end_uid = 65535; static int paclen = ROSE_PACLEN; /* Its the shortest ie safest */ static int huffman; static int bin; static int fdmaster; struct write_queue { struct write_queue *next; char *data; unsigned int len; }; static struct write_queue *wqueue_head; static struct write_queue *wqueue_tail; static long wqueue_length = 0L; /*---------------------------------------------------------------------------*/ /* Use private function because some platforms are broken, eg 386BSD */ static int Xtolower(int c) { return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; } /*---------------------------------------------------------------------------*/ int Strcasecmp(const char *s1, const char *s2) { while (Xtolower(*s1) == Xtolower(*s2)) { if (!*s1) return 0; s1++; s2++; } return Xtolower(*s1) - Xtolower(*s2); } /*---------------------------------------------------------------------------*/ static int Strncasecmp(const char *s1, const char *s2, int n) { while (--n >= 0 && Xtolower(*s1) == Xtolower(*s2)) { if (!*s1) return 0; s1++; s2++; } return n < 0 ? 0 : Xtolower(*s1) - Xtolower(*s2); } /* The buffer in src (first byte length-1) is decoded into dest (first byte length-1). If decoding is not successful, non-0 is returned */ static int encstathuf(char *src, int srclen, void *dptr, int *destlen) { char *srcptr; unsigned char *destptr; int wrklen; int bit16; int bit8; unsigned short huffcode; int hufflen; unsigned char *dest = dptr; if ((src == NULL) || (dest == NULL)) { syslog(LOG_NOTICE, "Huffman encode: src or dest NULL!"); return 1; } if (srclen > 255) { syslog(LOG_NOTICE, "Huffman encode: srclen > 255: %d", srclen); return 1; } srcptr = src; destptr = &dest[1]; *destlen = 0; wrklen = 0; bit8 = 0; *destptr = 0; huffcode = huffencodtab[(int)*srcptr].code; hufflen = huffencodtab[(int)*srcptr].len; bit16 = 0; for (;;) { if (huffcode & mask16tab[bit16]) *destptr |= mask8tab[bit8]; bit8++; if (bit8 > 7) { destptr++; (*destlen)++; if ((*destlen) >= srclen) { /* coding uneffective, use copy */ memcpy(&dest[1],src,srclen); dest[0] = 255; *destlen = srclen + 1; return 0; } bit8 = 0; *destptr = 0; } bit16++; if (bit16 == hufflen) { srcptr++; wrklen++; if (wrklen == srclen) break; huffcode = huffencodtab[(int)*srcptr].code; hufflen = huffencodtab[(int)*srcptr].len; bit16 = 0; } } if (bit8 != 0) (*destlen)++; (*destlen)++; dest[0] = (char)(srclen-1); return 0; } /* The buffer in src (first byte length-1) is decoded into dest (first byte length-1). If decoding is not successful, non-0 is returned */ static int decstathuf(char *src, char *dest, int srclen, int *destlen) { char *srcptr; char *destptr; int wrklen; unsigned char bitmask; unsigned short decod; unsigned short newnode; if ((src == NULL) || (dest == NULL)) return 1; srcptr = src; destptr = dest; *destlen = (int)((*srcptr)+1); srcptr++; if (--srclen == 0) { return 1; } if (*destlen == 0) { return 1; } if (*destlen == 256) { /* no compression, only copy */ memcpy(dest,src+1,srclen); *destlen = (unsigned char)(srclen); return 0; } wrklen = 0; decod = 0; bitmask = 0x80; for (;;) { while (bitmask > 0) { if ((*srcptr) & bitmask) { newnode = huffdecodtab[decod].node2; if (newnode == 0) { *destptr = (char)huffdecodtab[decod].node1; destptr++; wrklen++; if (wrklen >= *destlen) break; /* decoding finished */ decod = 0; } else decod = newnode; } else { newnode = huffdecodtab[decod].node1; if (huffdecodtab[decod].node2 == 0) { *destptr = (char)huffdecodtab[decod].node1; destptr++; wrklen++; if (wrklen >= *destlen) break; /* decoding finished */ decod = 0; } else decod = newnode; } if (decod) bitmask = bitmask >> 1; } if (wrklen >= *destlen) break; bitmask = 0x80; srcptr++; if (srclen == 0) return 1; srclen--; } return 0; } static int _write_ax25(const char *s, int len) { int i; if (!len) return 0; i = write(1, s, len); fflush(stdout); return i > 0 ? i : 0; /* on error, -1 is returned */ } static int read_ax25(char *s, int size) { int len; int k; char buffer[255]; char decomp[260]; int declen; struct termios termios; len = read(0, s, size); if (len < 0) return len; if (huffman) { if (!decstathuf(s, decomp, len, &declen)) { *(decomp+declen) = 0; strcpy(s, decomp); len = declen; } } if (bin) { return len; } for (k = 0; k < len; k++) if (s[k] == '\r') s[k] = '\n'; if (!huffman && !Strncasecmp(s, "//COMP ON\n", 10)) { sprintf(buffer,"\r//COMP 1\r"); write_ax25(buffer, strlen(buffer), 1); sleep(1); memset(&termios, 0, sizeof(termios)); termios.c_iflag = ICRNL | IXOFF | IGNBRK; termios.c_oflag = 0; termios.c_cflag = CS8 | CREAD | CLOCAL; termios.c_lflag = 0; termios.c_cc[VMIN] = 0; termios.c_cc[VTIME] = 0; tcsetattr(0, TCSANOW, &termios); huffman = 1; strcpy(s,"\n"); return 1; } if (huffman && !Strncasecmp(s, "//COMP OFF\n", 11)) { sprintf(buffer,"\r//COMP 0\r"); fflush(stdout); write_ax25(buffer, strlen(buffer), 1); sleep(1); huffman = 0; memset(&termios, 0, sizeof(termios)); termios.c_iflag = ICRNL | IXOFF; termios.c_oflag = OPOST | ONLCR; termios.c_cflag = CS8 | CREAD | CLOCAL; termios.c_lflag = ISIG | ICANON; termios.c_cc[VINTR] = 127; termios.c_cc[VQUIT] = 28; termios.c_cc[VERASE] = 8; termios.c_cc[VKILL] = 24; termios.c_cc[VEOF] = 4; cfsetispeed(&termios, B19200); cfsetospeed(&termios, B19200); tcsetattr(0, TCSANOW, &termios); strcpy(s,"\n"); return 1; } return len; } /* * We need to buffer the data from the pipe since bash does * a fflush() on every output line. We don't want it, it's * PACKET radio, isn't it? */ static void kick_wqueue(int dummy) { char *s, *p; struct write_queue *w_buf, *new_head; char *q, *r; int i; int len, curr_len; struct itimerval itv; int bufsize = (huffman ? 256-1 : paclen); char decoded[260]; int declen; signal(SIGALRM, SIG_IGN); itv.it_value.tv_sec = 0; itv.it_value.tv_usec = QUEUE_DELAY; for (;;) { if (wqueue_length == 0) { return; } /* recompute waitqueue */ if (wqueue_head->len < bufsize && wqueue_head->next) { int s_len; s = malloc(bufsize); if (!s) { break; } new_head = malloc(sizeof(struct write_queue)); if (!new_head) { free(s); break; } p = s; s_len = 0; while ((w_buf = wqueue_head)) { curr_len = (w_buf->len > bufsize-s_len ? bufsize-s_len : w_buf->len); memcpy(p, w_buf->data, curr_len); s_len += curr_len; p += curr_len; if (w_buf->len > curr_len) { /* rewrite current buffer, and break */ w_buf->len -= curr_len; for (q = w_buf->data, r = w_buf->data+curr_len, i = 0; i < w_buf->len; i++) *q++ = *r++; break; } wqueue_head = w_buf->next; free(w_buf->data); free(w_buf); } new_head->data = s; new_head->len = s_len; new_head->next = wqueue_head; wqueue_head = new_head; } w_buf = wqueue_head; curr_len = w_buf->len > bufsize ? bufsize : w_buf->len; if (huffman && !encstathuf(w_buf->data,curr_len,decoded,&declen)) { *(decoded+declen) = 0; s = decoded; len = declen; } else { s = w_buf->data; len = curr_len; } if (!_write_ax25(s, len) && errno == EAGAIN) { /* * socket busy? * don't block, user may want interrupt the jam */ itv.it_value.tv_sec = 1; break; } wqueue_length -= curr_len; if (w_buf->len > curr_len) { /* * there's still data to be written - copy restbuffer * to left */ w_buf->len -= curr_len; for (q = w_buf->data, r = w_buf->data+curr_len, i = 0; i < w_buf->len; i++) *q++ = *r++; } else { wqueue_head = w_buf->next; free(w_buf->data); free(w_buf); if (!wqueue_head) { wqueue_tail = NULL; return; } } /* * if only a few bytes are left, wait a few ms if * new data is available in order to send "full packets" */ if (wqueue_length < paclen) break; } itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itv, NULL); signal(SIGALRM, kick_wqueue); } int write_ax25(char *s, int len, int kick) { struct itimerval itv, oitv; struct write_queue * buf; struct termios termios; static struct termios save_termios; static struct termios save_termios_master; static int last_ended_with_CR; int i = 0; int j = 0; char *p; if (!len) return 0; signal(SIGALRM, SIG_IGN); if (!bin && !strncmp(s, "//BIN ON\r", 9)) { tcgetattr(fdmaster, &save_termios_master); tcgetattr(0, &save_termios); memset(&termios, 0, sizeof(termios)); termios.c_iflag = IGNBRK | IGNPAR; termios.c_oflag = 0; termios.c_cflag = CBAUD | CS8 | CREAD | CLOCAL; termios.c_cflag = ~(CSTOPB|PARENB|PARODD|HUPCL); termios.c_lflag = 0; termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; termios.c_cc[VSTART] = -1; termios.c_cc[VSTOP] = -1; tcsetattr(0, TCSANOW, &termios); tcsetattr(fdmaster, TCSANOW, &termios); *s= 0; len=0; bin = 1; kick_wqueue(0); return 0; } if (bin && !strncmp(s, "//BIN OFF\r", 10)) { kick_wqueue(0); bin = 0; tcsetattr(fdmaster, TCSANOW, &save_termios_master); tcsetattr(0, TCSANOW, &save_termios); last_ended_with_CR = 0; return 0; } if (!bin) { p = s; i = 0; j = 0; if (last_ended_with_CR) { /* * \r\n was splited. wrote already \r, now omitting \n */ if (*s == '\n') { s++, p++; len--; } last_ended_with_CR = 0; if (!len) { if (wqueue_head) kick_wqueue(0); return 0; } } while (j < len) { if ((j + 1 < len) && *(p + j) == '\r' && *(p + j + 1) == '\n') { *(p + i) = '\r'; j++; } else *(p + i) = *(p + j); i++; j++; } len = i; if (len && s[len-1] == '\r') last_ended_with_CR = 1; *(p+len) = 0; } buf = malloc(sizeof(struct write_queue)); if (buf == NULL) return 0; buf->data = malloc(len); if (buf->data == NULL) { free(buf); return 0; } memcpy(buf->data, s, len); buf->len = len; buf->next = NULL; if (wqueue_head == NULL) { wqueue_head = buf; wqueue_tail = buf; wqueue_length = len; } else { wqueue_tail->next = buf; wqueue_tail = buf; wqueue_length += len; } if (wqueue_length > 7*paclen || kick || bin) { kick_wqueue(0); } else { itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = QUEUE_DELAY; setitimer(ITIMER_REAL, &itv, &oitv); signal(SIGALRM, kick_wqueue); } return len; } static int get_assoc(struct sockaddr_ax25 *sax25) { FILE *fp; int uid; char buf[81]; fp = fopen(PROC_AX25_CALLS_FILE, "r"); if (!fp) return -1; fgets(buf, sizeof(buf)-1, fp); while (!feof(fp)) { if (fscanf(fp, "%d %s", &uid, buf) == 2) if (sax25->sax25_uid == uid) { ax25_aton_entry(buf, (char *) &sax25->sax25_call); fclose(fp); return 0; } } fclose(fp); return -1; } static void cleanup(char *tty) { struct utmp ut, *ut_line; struct timeval tv; FILE *fp; setutent(); ut.ut_type = LOGIN_PROCESS; strncpy(ut.ut_id, tty + 3, sizeof(ut.ut_id)); ut_line = getutid(&ut); if (ut_line != NULL) { ut_line->ut_type = DEAD_PROCESS; ut_line->ut_host[0] = '\0'; ut_line->ut_user[0] = '\0'; gettimeofday(&tv, NULL); ut_line->ut_tv.tv_sec = tv.tv_sec; ut_line->ut_tv.tv_usec = tv.tv_usec; fp = fopen(_PATH_WTMP, "r+"); if (fp != NULL) { fseek(fp, 0L, SEEK_END); if (fwrite(ut_line, sizeof(ut), 1, fp) != 1) syslog(LOG_ERR, "Ooops, I think I've just barbecued your wtmp file\n"); fclose(fp); } } endutent(); } /* On debian wheezy, useradd (from passwd / shadow package) is buggy. * Short before end of main, it starts the external program nscd for * cleaning user / group cache. Their handler for waiting the forked * process waits for child's or -1 and errno EINTR with waitpid() - else * it loops again. * When useradd is started by axspawn, their waitpid() returns -1 errno ECHILD, * which leads their do-while-function go into an endless loop, eating * 100% CPU power. * If we mask SIGCHLD before execve(), useradd works as it should. */ static void signal_handler_sigchild(int dummy) { exit(0); } /* * add a new user to /etc/passwd and do some init */ static void new_user(char *newuser) { struct passwd pw, *pwp; uid_t uid; FILE *fp; char username[80]; char homedir[256], userdir[256]; char buf[4096]; char subdir[4]; int cnt; char *q; char *p; struct stat fst; int fd_a, fd_b, fd_l; mode_t homedir_mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IXOTH|(secure_home ? 0 : (S_IRGRP|S_IXGRP)); /* * build path for home directory */ strncpy(subdir, newuser, 3); subdir[3] = '\0'; sprintf(username, "%s", newuser); sprintf(homedir, "%s/%s.../%s", start_home, subdir, newuser); strcpy(userdir, homedir); fd_l = open(LOCK_AXSPAWN_FILE, O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); flock(fd_l, LOCK_EX); retry: /* * find first free UID */ for (uid = start_uid; uid < 65535; uid++) { pwp = getpwuid(uid); if (pwp == NULL) break; } if (uid >= 65535 || uid < start_uid) goto out; /* * build directories for home */ p = homedir; while (*p == '/') p++; chdir("/"); while (p) { q = strchr(p, '/'); if (q) { *q = '\0'; q++; while (*q == '/') q++; if (*q == 0) q = NULL; } if (stat(p, &fst) < 0) { if (errno == ENOENT) { if (q == NULL && policy_add_prog_useradd) { /* Some useradd implementations * fail if the directory for the * new user already existss. That's * why we stop here now, just before * mkdir of ....../username/ * We had to use this function, * because we decided to make * directories in the form * /home/hams/dl9.../ - and if * for e.g. dl9.../ does not exist, * then useradd will refuse to make * the needed / missing subdirs * for /home/hams/dl9.../dl9sau/ */ goto end_mkdirs; } mkdir(p, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); if (q == NULL) { chown(p, uid, user_gid); chmod(p, homedir_mode); } } else goto out; } if (chdir(p) < 0) goto out; p = q; } end_mkdirs: /* * add the user now */ if (policy_add_prog_useradd) { pid_t pid = -1; struct stat statbuf; if (!user_shell_configured && stat(USERADD_CONF, &statbuf) == -1) { /* some programs need a shell explicitely specified * in /etc/passwd, although this field is not required * (and useradd does not set a shell when not * specified). * useradd has a config file. On debian for e.g., * there is no /etc/default/useradd. Thus we * explecitely give the shell option to useradd, when * no useradd config file is present. */ user_shell_configured = 1; } pid = fork(); if (pid == -1) goto out; else if (pid == 0) { int chargc = 0; char *chargv[20]; char *envp[] = { NULL }; char s_uid[32]; char s_gid[32]; sprintf(s_uid, "%d", uid); sprintf(s_gid, "%d", user_gid); chargv[chargc++] = "/usr/sbin/useradd"; chargv[chargc++] = "-p"; chargv[chargc++] = ((policy_add_empty_password) ? "" : "+"); chargv[chargc++] = "-c"; chargv[chargc++] = username; chargv[chargc++] = "-d"; chargv[chargc++] = userdir; chargv[chargc++] = "-u"; chargv[chargc++] = s_uid; chargv[chargc++] = "-g"; chargv[chargc++] = s_gid; chargv[chargc++] = "-m"; chargv[chargc++] = newuser; if (user_shell_configured) { chargv[chargc++] = "-s"; chargv[chargc++] = user_shell; } chargv[chargc] = NULL; /* signal SIGCHLD: see description above */ signal(SIGCHLD, signal_handler_sigchild); execve(chargv[0], chargv, envp); } else { int status; pid_t wpid = -1; wpid = waitpid(-1, &status, 0); if (wpid == -1) goto out; if (!WIFEXITED(status)) goto out; } } else { fp = fopen(PASSWDFILE, "a+"); if (fp == NULL) goto out; pw.pw_name = newuser; pw.pw_passwd = ((policy_add_empty_password) ? "" : "+"); pw.pw_uid = uid; pw.pw_gid = user_gid; pw.pw_gecos = username; pw.pw_dir = userdir; pw.pw_shell = user_shell; if (getpwuid(uid) != NULL) goto retry; /* oops?! */ if (putpwent(&pw, fp) < 0) goto out; fclose(fp); } /* * copy ax25.profile */ fd_a = open(CONF_AXSPAWN_PROF_FILE, O_RDONLY); if (fd_a > 0) { int first = 1; fd_b = open(USERPROFILE, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR); if (fd_b < 0) goto out; /* just 2b sure */ if (lseek(fd_b, 0L, SEEK_END) > 0L) write(fd_b, "\n", 1); while ( (cnt = read(fd_a, &buf, sizeof(buf))) > 0 ) { if (first) { /* fix: profiles never start with "#!/bin/sh", * but previous ax25.profile did. We fix this * now. We append this profile, because .profile * may came through /etc/skel. And because * its there, it's intended to be used. Then * if ax25.profile is present, it's regarded * as an optimization. * -> mark #!... as ## */ if (buf[0] == '#' && buf[1] == '!') buf[1] = '#'; first = 0; } write(fd_b, &buf, cnt); } close(fd_b); close(fd_a); chown(USERPROFILE, uid, user_gid); } out: flock(fd_l, LOCK_UN); } static void read_config(void) { FILE *fp = fopen(CONF_AXSPAWN_FILE, "r"); char buf[512]; char cmd[40], param[80]; char *p; if (fp == NULL) return; while (!feof(fp)) { fgets(buf, sizeof(buf), fp); p = strchr(buf, '#'); if (p || (p = strchr(buf, '\n'))) *p='\0'; if (buf[0] != '\0') { sscanf(buf, "%s %s", cmd, param); if (!strncmp(cmd, "create", 6) && !cmd[6]) { policy_add_user = (param[0] == 'y'); } else if (!strncmp(cmd, "create_empty_password", 21)) policy_add_empty_password = (param[0] == 'y'); else if (!strncmp(cmd, "create_with_useradd", 19)) policy_add_prog_useradd = (param[0] == 'y'); else if (!strncmp(cmd, "pwcheck", 7)) { if (!strncmp(param, "pass", 4)) pwcheck = 1; else if (!strncmp(param, "call", 4)) pwcheck = 2; else if (!strncmp(param, "group", 5)) pwcheck = 3; } else if (!strncmp(cmd, "guest", 5)) { if (!strcmp(param, "no")) { policy_guest = 0; } else { policy_guest = 1; guest = malloc(strlen(param)+1); strcpy(guest, param); } } else if (!strncmp(cmd, "group", 5)) { user_gid = strtol(param, &p, 0); if (*p != '\0') { struct group * gp = getgrnam(param); if (gp != NULL) user_gid = gp->gr_gid; else user_gid = 400; endgrent(); } } else if (!strncmp(cmd, "first", 5)) { start_uid = strtol(param, &p, 0); if (*p != '\0') start_uid = 400; } else if (!strncmp(cmd, "max", 3)) { end_uid = strtol(param, &p, 0); if (*p != '\0') end_uid = 0; } else if (!strncmp(cmd, "home", 4)) { start_home = malloc(strlen(param)+1); strcpy(start_home, param); } else if (!strncmp(cmd, "secure_home", 11)) { secure_home = (param[0] == 'y'); } else if (!strncmp(cmd, "assoc", 5)) { if (!strcmp(param, "yes")) policy_associate = 1; else policy_associate = 0; } else if (!strncmp(cmd, "shell", 5)) { user_shell = malloc(strlen(param)+1); strcpy(user_shell, param); user_shell_configured = 1; } else { printf("error in config: ->%s %s<-\n", cmd, param); } } } fclose(fp); } static char ptyslave[20]; static int child_pid; static void signal_handler(int dummy) { kill(child_pid, SIGHUP); cleanup(ptyslave+5); exit(1); } int main(int argc, char **argv) { char call[20], user[20], as_user[20]; char buf[2048]; int k, cnt, digits, letters, invalid, ssid, ssidcnt; socklen_t addrlen; struct timeval tv; pid_t pid = -1; char *p; fd_set fds_read, fds_err; struct passwd *pw; int chargc = 0; char *chargv[20]; int envc = 0; char *envp[20]; struct utmp ut_line; struct winsize win = { 0, 0, 0, 0}; struct sockaddr_ax25 sax25; union { struct full_sockaddr_ax25 fsax25; struct sockaddr_rose rose; } sockaddr; char *protocol = ""; char is_guest = 0; char wait_for_tcp = 0; char changeuser = 0; char user_changed = 0; char rootlogin = 0; char dumb_embedded_system = 0; int pwtype = 0; int pwtype_orig = 0; char prompt[20]; char *pwd = NULL; *prompt = 0; digits = letters = invalid = ssid = ssidcnt = 0; for (k = 1; k < argc; k++) { if (!strcmp(argv[k], "-w") || !strcmp(argv[k], "--wait")) wait_for_tcp = 1; if (!strcmp(argv[k], "-c") || !strcmp(argv[k], "--changeuser")) changeuser = 1; if (!strcmp(argv[k], "-r") || !strcmp(argv[k], "--rootlogin")) rootlogin = 1; if (!strcmp(argv[k], "-e") || !strcmp(argv[k], "--embedded")) dumb_embedded_system = 1; if ((!strcmp(argv[k], "-p") || !strcmp(argv[k], "--pwprompt")) && k < argc-1 ) { strncpy(prompt, argv[k+1], sizeof(prompt)); prompt[sizeof(prompt)-1] = '\0'; k++; } if (!strcmp(argv[k], "--only-md5")) pwtype = PW_MD5; } read_config(); if (!pwtype) pwtype = (PW_CLEARTEXT | PW_SYS | PW_MD5 | PW_UNIX); pwtype_orig = pwtype; if (!*prompt) { if (gethostname(buf, sizeof(buf)) < 0) { strcpy(prompt, "Check"); } else { p = strchr(buf, '.'); if (p) *p = 0; strncpy(prompt, buf, sizeof(prompt)); prompt[sizeof(prompt)-1] = 0; } } strupr(prompt); openlog("axspawn", LOG_PID, LOG_DAEMON); if (getuid() != 0) { printf("permission denied\n"); syslog(LOG_NOTICE, "user %d tried to run axspawn\n", getuid()); return 1; } addrlen = sizeof(struct full_sockaddr_ax25); k = getpeername(0, (struct sockaddr *) &sockaddr, &addrlen); if (k < 0) { syslog(LOG_NOTICE, "getpeername: %m\n"); return 1; } switch (sockaddr.fsax25.fsa_ax25.sax25_family) { case AF_AX25: strcpy(call, ax25_ntoa(&sockaddr.fsax25.fsa_ax25.sax25_call)); protocol = "AX.25"; paclen = AX_PACLEN; break; case AF_NETROM: strcpy(call, ax25_ntoa(&sockaddr.fsax25.fsa_ax25.sax25_call)); protocol = "NET/ROM"; paclen = NETROM_PACLEN; break; case AF_ROSE: strcpy(call, ax25_ntoa(&sockaddr.rose.srose_call)); protocol = "Rose"; paclen = ROSE_PACLEN; break; default: syslog(LOG_NOTICE, "peer is not an AX.25, NET/ROM or Rose socket\n"); return 1; } for (k = 0; k < strlen(call); k++) { if (ssidcnt) { if (!IS_DIGIT(call[k])) invalid++; else { if (ssidcnt > 2) invalid++; else if (ssidcnt == 1) ssid = (int) (call[k] - '0'); else { ssid *= 10; ssid += (int) (call[k] - '0'); if (ssid > 15) invalid++; } ssidcnt++; } } else if (IS_DIGIT(call[k])) { digits++; if (k > 3) invalid++; } else if (IS_LETTER(call[k])) letters++; else if (call[k] == '-') { if (k < MINLEN) invalid++; else ssidcnt++; } else invalid++; } if ( invalid || (k < MINLEN) || (digits > 2) || (digits < 1) ) { write_ax25_static_line(MSG_NOCALL); syslog(LOG_NOTICE, "%s is not an Amateur Radio callsign\n", call); sleep(EXITDELAY); return 1; } if (wait_for_tcp) { /* incoming TCP/IP connection? */ if (read_ax25(buf, sizeof(buf)) < 0) exit(0); } strcpy(user, call); strlwr(user); p = strchr(user, '-'); if (p) *p = '\0'; *as_user = 0; if (changeuser) { char *p_buf; sprintf(buf, "Login (%s): ", user); write_ax25(buf, strlen(buf), 1); cnt = read_ax25(buf, sizeof(buf) - 1); if (cnt < 0) exit(1); buf[cnt] = 0; /* skip leading blanks */ for (p_buf = buf; *p_buf && *p_buf != '\n' && !isalnum(*p_buf & 0xff); p_buf++) ; /* skip trailing junk, blanks, \n, .. */ for (p = p_buf; *p && isalnum(*p & 0xff); p++) ; *p = 0; if (*p_buf) { strncpy(as_user, p_buf, sizeof(as_user)); as_user[sizeof(as_user)-1] = 0; user_changed = 1; } } if (!*as_user) strcpy(as_user, user); pw = getpwnam(as_user); endpwent(); if (pw == NULL) { if (user_changed) { syslog(LOG_NOTICE, "%s (callsign: %s) not found in /etc/passwd\n", as_user, call); sleep(EXITDELAY); return 1; } if (policy_add_user) { new_user(as_user); pw = getpwnam(as_user); endpwent(); if (pw == NULL) { syslog(LOG_NOTICE, "%s (callsign: %s) not found in /etc/passwd, even aver new_user()\n", as_user, call); sleep(EXITDELAY); return 1; } } if (pw == NULL && policy_guest) { strcpy(as_user,guest); pw = getpwnam(guest); endpwent(); is_guest = 1; } } if (!pw) { write_ax25_static_line(MSG_NOTINDBF); syslog(LOG_NOTICE, "%s (callsign: %s) not found in /etc/passwd\n", as_user, call); sleep(EXITDELAY); return 1; } if (!rootlogin && (pw->pw_uid == 0 || pw->pw_gid == 0)) { write_ax25_static_line(MSG_NOCALL); syslog(LOG_NOTICE, "root login of %s (callsign: %s) denied\n", as_user, call); sleep(EXITDELAY); return 1; } again: pwd = read_pwd(pw, &pwtype); if (!pwd) { if ((!pwtype || pwtype != PW_CLEARTEXT) && (pwtype != PW_UNIX)) { sleep (EXITDELAY); return 1; } } if (pwtype == PW_UNIX) { pwtype = PW_CLEARTEXT; pwcheck = 1; } if (pwtype != PW_CLEARTEXT) { char pass_want[PASSSIZE+1]; if (pwtype == PW_MD5) ask_pw_md5(prompt, pass_want, pwd); else ask_pw_sys(prompt, pass_want, pwd); cnt = read_ax25(buf, sizeof(buf)-1); if (cnt <= 0) { sprintf(buf,"no response\r"); write_ax25(buf, strlen(buf),1); sleep (EXITDELAY); return -11; } buf[cnt] = 0; p = strchr(buf, '\n'); if (p) *p = 0; if ((pwtype & PW_MD5) && !strcmp(buf, "sys") && (pwtype_orig & PW_SYS)) { pwtype = (pwtype_orig & ~PW_MD5); if (pwd) free(pwd); pwd = NULL; goto again; } if (!strstr(buf, pass_want)) { sprintf(buf,"authentication failed\r"); write_ax25(buf, strlen(buf), 1); sleep (EXITDELAY); return -11; } if (pwd) free(pwd); pwd = NULL; } else { if (pw->pw_uid == 0 || pw->pw_gid == 0) { sprintf(buf, "Sorry, root logins are only allowed with md5- or baycom-password!\r"); write_ax25(buf, strlen(buf), 1); syslog(LOG_NOTICE, "root login of %s (callsign: %s) denied (only with md5- or baycom-Login)!\n", user, call); sleep(EXITDELAY); return 1; } } if (pwd) free(pwd); pwd = NULL; /* * associate UID with callsign (or vice versa?) */ if (policy_associate) { int fds = socket(AF_AX25, SOCK_SEQPACKET, 0); if (fds != -1) { sax25.sax25_uid = pw->pw_uid; if (get_assoc(&sax25) != -1) ioctl(fds, SIOCAX25DELUID, &sax25); switch (sockaddr.fsax25.fsa_ax25.sax25_family) { case AF_AX25: case AF_NETROM: sax25.sax25_call = sockaddr.fsax25.fsa_ax25.sax25_call; break; case AF_ROSE: sax25.sax25_call = sockaddr.rose.srose_call; break; } ioctl(fds, SIOCAX25ADDUID, &sax25); close(fds); } } fcntl(1, F_SETFL, O_NONBLOCK); pid = forkpty(&fdmaster, ptyslave, NULL, &win); if (pid == 0) { struct termios termios; char *shell = "/bin/sh"; memset(&termios, 0, sizeof(termios)); ioctl(0, TIOCSCTTY, (char *) 0); termios.c_iflag = ICRNL | IXOFF; termios.c_oflag = OPOST | ONLCR; termios.c_cflag = CS8 | CREAD | CLOCAL; termios.c_lflag = ISIG | ICANON; termios.c_cc[VINTR] = /* 127 */ 0x03; termios.c_cc[VQUIT] = 28; termios.c_cc[VERASE] = 8; termios.c_cc[VKILL] = 24; termios.c_cc[VEOF] = 4; cfsetispeed(&termios, B19200); cfsetospeed(&termios, B19200); tcsetattr(0, TCSANOW, &termios); setutent(); ut_line.ut_type = LOGIN_PROCESS; ut_line.ut_pid = getpid(); strncpy(ut_line.ut_line, ptyslave + 5, sizeof(ut_line.ut_line)); strncpy(ut_line.ut_id, ptyslave + 8, sizeof(ut_line.ut_id)); strncpy(ut_line.ut_user, "LOGIN", sizeof(ut_line.ut_user)); strncpy(ut_line.ut_host, protocol, sizeof(ut_line.ut_host)); gettimeofday(&tv, NULL); ut_line.ut_tv.tv_sec = tv.tv_sec; ut_line.ut_tv.tv_usec = tv.tv_usec; ut_line.ut_addr = 0; pututline(&ut_line); endutent(); /* become process group leader, if we not already are */ if (getpid() != getsid(0)) { if (setsid() == -1) exit(1); } chargc = 0; envc = 0; if (dumb_embedded_system) { int ret = -1; char *p = NULL; chown(ptyslave, pw->pw_uid, 0); chmod(ptyslave, 0622); ret = -1; if (pw->pw_dir && *(pw->pw_dir)) p = pw->pw_dir; ret = chdir(p); if (ret != 0) { p = "/tmp"; chdir(p); } envp[envc] = malloc(strlen(p) + 6); if (envp[envc]) sprintf(envp[envc++], "HOME=%s", p); envp[envc] = malloc(strlen(pw->pw_name) + 6); if (envp[envc]) sprintf(envp[envc++], "USER=%s", pw->pw_name); envp[envc] = malloc(strlen(pw->pw_name) + 9); if (envp[envc]) sprintf(envp[envc++], "LOGNAME=%s", pw->pw_name); if (pw->pw_shell && *(pw->pw_shell)) { shell = pw->pw_shell; } else { shell = "/bin/sh"; } p = strrchr(shell, '/'); if (p) { if (p[1]) { p = strdup(p); if (p) *p = '-'; } else p = NULL; } if (!p) p = shell; chargv[chargc++] = p; envp[envc] = malloc(strlen(shell) + 7); if (envp[envc]) sprintf(envp[envc++], "SHELL=%s", shell); if (pw->pw_uid == 0) p = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"; else p = "/bin:/usr/bin:/usr/local/bin"; envp[envc] = malloc(strlen(p) + 6); if (envp[envc]) sprintf(envp[envc++], "PATH=%s", p); } else { chargv[chargc++] = "/bin/login"; chargv[chargc++] = "-p"; /* there exist several conectps: * Historically, the special character '+' in the password * field indicated that users may login via ax25, netrom, rose, * etc.. - but not via other protocols like telnet. * This secures the digipeater from abuse by inet access of * non-hams. * On the other hand, this leads to the problem that telent * via HF, pop3, etc.. do not work. * The "pwcheck == 2 method means, that the password is used on * every login mechanism other than this axspawn program; * here we do not rely on the password - the ax25 call of * the ham is enough. We have already checked above, that * the call of the user is valid (and not root, httpd, etc..); * thus this method is safe here. * Another mechanism (pwcheck == 3) is to check if the gid * equals to user_gid preference. I prefer this way, because * this approach gives the chance to temporary lock a user * out (abuse, ..) by changing his gid in passwd to for e.g. * 65534 (nogroup). */ chargv[chargc++] = "-h"; chargv[chargc++] = protocol; if (pwtype != PW_CLEARTEXT /* PW_SYS or PW_MD5 are already authenticated */ || pwcheck == 2 || (pwcheck == 3 && (pw->pw_gid == user_gid || is_guest)) || !strcmp(pw->pw_passwd, "+")) chargv[chargc++] = "-f"; chargv[chargc++] = as_user; } chargv[chargc] = NULL; envp[envc] = malloc(30); if (envp[envc]) sprintf(envp[envc++], "AXCALL=%s", call); envp[envc] = malloc(30); if (envp[envc]) sprintf(envp[envc++], "CALL=%s", user); envp[envc] = malloc(30); if (envp[envc]) sprintf(envp[envc++], "PROTOCOL=%s", protocol); envp[envc] = malloc(30); if (envp[envc]) sprintf(envp[envc++], "TERM=dumb"); /* SuSE bug (dump - tsts) */ /* other useful defaults */ envp[envc] = malloc(30); if (envp[envc]) sprintf(envp[envc++], "EDITOR=/usr/bin/ex"); envp[envc] = malloc(30); if (envp[envc]) sprintf(envp[envc++], "LESS=-d -E -F"); envp[envc] = NULL; if (dumb_embedded_system) { if (setgid(pw->pw_gid) == -1) exit(1); if (setuid(pw->pw_uid) == -1) exit(1); execve(shell, chargv, envp); /* point of no return */ exit(1); } execve(chargv[0], chargv, envp); /* point of no return */ exit(1); } else if (pid > 0) { child_pid = 0; signal(SIGHUP, signal_handler); signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); signal(SIGQUIT, signal_handler); while (1) { FD_ZERO(&fds_read); FD_ZERO(&fds_err); FD_SET(0, &fds_read); FD_SET(0, &fds_err); if (wqueue_length <= paclen*7) FD_SET(fdmaster, &fds_read); FD_SET(fdmaster, &fds_err); k = select(fdmaster+1, &fds_read, NULL, &fds_err, NULL); if (k > 0) { if (FD_ISSET(0, &fds_err)) { if (huffman) { sprintf(buf,"\r//COMP 0\r"); write_ax25(buf, strlen(buf), 1); sleep(EXITDELAY); } kill(pid, SIGHUP); cleanup(ptyslave+5); return 1; } if (FD_ISSET(fdmaster, &fds_err)) { /* give the last packet in the timer controlled sendqueue a chance.. */ if (wqueue_length) { continue; } if (huffman) { sprintf(buf,"\r//COMP 0\r"); write_ax25(buf, strlen(buf), 1); sleep(EXITDELAY); } cleanup(ptyslave+5); return 1; } if (FD_ISSET(0, &fds_read)) { cnt = read_ax25(buf, sizeof(buf)); if (cnt < 0) /* Connection died */ { kill(pid, SIGHUP); cleanup(ptyslave+5); return 1; } else write(fdmaster, buf, cnt); } if (FD_ISSET(fdmaster, &fds_read)) { cnt = read(fdmaster, buf, (huffman ? 254 : sizeof(buf))); if (cnt < 0) { /* give the last packet in the timer controlled sendqueue a chance.. */ if (wqueue_length) { continue; } if (huffman) { sprintf(buf,"\r//COMP 0\r"); write_ax25(buf, strlen(buf), 1); sleep(EXITDELAY); } cleanup(ptyslave+5); return 1; /* Child died */ } write_ax25(buf, cnt, 0); } } else if (k < 0 && errno != EINTR) { if (huffman) { sprintf(buf,"\r//COMP 0\r"); write_ax25(buf, strlen(buf), 1); sleep(EXITDELAY); } kill(pid, SIGHUP); /* just in case... */ cleanup(ptyslave+5); return 0; } } } else { write_ax25_static_line(MSG_CANNOTFORK); syslog(LOG_ERR, "cannot fork %m, closing connection to %s\n", call); sleep(EXITDELAY); return 1; } sleep(EXITDELAY); return 0; } ax25/axspawn.conf000066400000000000000000000027621442776067000142110ustar00rootroot00000000000000# /etc/ax25/axspawn.conf # # allow automatic creation of user accounts create yes # allow empty password field (so user may login via telnet, too) [default no] create_empty_password no #create_empty_password yes # create with system utility useradd(8)? [default no] #create_with_useradd no create_with_useradd yes # # pwcheck method: password or call or group [default: password] # "password" means, that passwords with '+' force a login without # prompting for a password (old behaviour; backward compatibility). # "call" means, that ham calls via ax25/netrom/rose/.. should be able # to login without password, even if it's set (for e.g. to secure # from abuse of inet connections) # "group" means, that if the gid of the user matches the configured # default user_gid, then the login is granted without password. #pwcheck call #pwcheck group #pwcheck password # # guest user if above is 'no' or everything else fails. Disable with "no" #guest ax25 guest guest # # group id or name for autoaccount #group ax25 group hams # # first user id to use first_uid 400 # # maximum user id max_uid 2000 # # where to add the home directory for the new user #home /home/ax25 home /home/hams # # secure homedirectories (g-rwx) #secure_home yes # # user's shell (if not configured, it's /bin/bash, or if # create_with_useradd is set, useradd uses the shell # configured in /etc/default/useradd). shell /bin/bash # # bind user id to callsign for outgoing connects. associate no ax25/axspawn.conf.5000066400000000000000000000036471442776067000143570ustar00rootroot00000000000000.TH AXSPAWN.CONF 5 "16 January 2014" Linux "Linux Programmer's Manual" .SH NAME axspawn.conf \- Control the operation of axspawn. .SH DESCRIPTION .LP The .B axspawn.conf file controls the operation of the axspawn(8) program. The operation of the config file can best be seen in an example: .LP .RS # this is /etc/ax25/axspawn.conf .br # .br # allow automatic creation of user accounts .br create yes .br # allow empty password field (so user may login via telnet, too) [default no] .br create_empty_password no .br #create_empty_password yes .br # create with system utility useradd(8)? [default no] .br #create_with_useradd no .br create_with_useradd yes .br # .br # pwcheck method: password or call or group. [default: password] .br #pwcheck call .br #pwcheck group .br pwcheck password .br # .br # guest user if above is 'no' or everything else .br # fails. Disable with "no" .br guest guest .br # .br # group id or name for autoaccount .br group hams .br # .br # first user id to use .br first_uid 400 .br # .br # maximum user id .br max_uid 2000 .br # .br # where to add the home directory for the new user .br home /home/hams .br # .br # secure homedirectories (g-rwx) .br #secure_home yes .br # .br # user's shell (if not configured, it's /bin/bash, or if .br # create_with_useradd is set, useradd uses the shell .br # configured in /etc/default/useradd). .br shell /bin/bash .br # .br # bind user id to callsign for outgoing connects. .br associate yes .RE .LP The \(lqassociate\(rq option has to be used with great care. If a user logs on it removes any existing callsign from the translation table for this userid and replaces it with the callsign and SSID of the user. This will happen with multiple connects (same callsign, different SSIDs), too. Unless you want your users to be able to call out from your machine disable \(lqassociate\(rq. .SH FILES .LP /etc/ax25/axspawn.conf .SH "SEE ALSO" .BR axspawn (8). ax25/axspawn.h000066400000000000000000000002361442776067000135050ustar00rootroot00000000000000#ifndef AXSPAWN_H #define AXSPAWN_H #define EXITDELAY 10 int write_ax25(char *s, int len, int kick); int Strcasecmp(const char *s1, const char *s2); #endif ax25/axwrapper.8000066400000000000000000000016011442776067000137520ustar00rootroot00000000000000.TH AXWRAPPER 8 "26 July 2017" Linux "Linux System Managers Manual" .SH NAME axwrapper \- Run non-ax.25-aware programs from ax25d .SH SYNOPSIS .B axwrapper [ -p paclen ] server-program argv[0] ... .SH DESCRIPTION .LP .B Axwrapper first creates a pipe and then forks and execs the program with arguments given at the axwrapper command line. The argv[0] argument is mandatory; further arguments are optional. The parent process then sits and waits for any I/O to and from the user and converts any carriage return characters from the user to line feeds and any line feeds from the program to carriage returns. This is useful when starting non-AX.25-aware programs from ax25d. .SH OPTIONS .BI "\-c paclen" .IP Specify a the size of the output buffer. The default length is 256 bytes. .SH "SEE ALSO" .BR ax25 (8) .BR ax25.conf (5) .SH AUTHOR Tomi Manninen OH2BNS ax25/axwrapper.c000066400000000000000000000117531442776067000140360ustar00rootroot00000000000000/* * axwrapper.c - Convert end-of-line sequences for non-AX.25 aware programs - * version 1.1 * * Copyright (C) 1996-2001 Tomi Manninen, OH2BNS, . * * Compile with: gcc -Wall -O6 -o axwrapper axwrapper.c * * Usage: axwrapper [-p ] ... * * Axwrapper first creates a pipe and then forks and execs the program * with arguments given at the axwrapper command line. * The parent process then sits and waits for any I/O to and from the * user and converts any 's from user to 's and any 's from * the program to 's. This is useful when starting non-AX.25 aware * programs from ax25d. * * * 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. * */ #include #include #include #include #include #include #include #include #define FLUSHTIMEOUT 500000 /* 0.5 sec */ #define PERROR(s) fprintf(stderr, "*** %s: %s\r", (s), strerror(errno)) #define USAGE() fputs("Usage: axwrapper [-p ] ...\r", stderr) static void convert_cr_lf(unsigned char *buf, int len) { while (len-- > 0) { if (*buf == '\r') *buf = '\n'; buf++; } } static void convert_lf_cr(unsigned char *buf, int len) { while (len-- > 0) { if (*buf == '\n') *buf = '\r'; buf++; } } int main(int argc, char **argv) { unsigned char buf[4096]; char *stdoutbuf; int pipe_in[2]; int pipe_out[2]; int pipe_err[2]; int len; int pid; int paclen = 256; fd_set fdset; struct timeval tv; while ((len = getopt(argc, argv, "p:")) != -1) { switch (len) { case 'p': paclen = atoi(optarg); break; case ':': case '?': USAGE(); exit(1); } } if (argc - optind < 2) { USAGE(); exit(1); } stdoutbuf = malloc(paclen); if (stdoutbuf == NULL) { PERROR("axwrapper: malloc"); exit(1); } setvbuf(stdout, stdoutbuf, _IOFBF, paclen); if (pipe(pipe_in) == -1) { PERROR("axwrapper: pipe"); exit(1); } if (pipe(pipe_out) == -1) { PERROR("axwrapper: pipe"); exit(1); } if (pipe(pipe_err) == -1) { PERROR("axwrapper: pipe"); exit(1); } signal(SIGCHLD, SIG_IGN); pid = fork(); if (pid == -1) { /* fork error */ PERROR("axwrapper: fork"); exit(1); } if (pid == 0) { /* child */ dup2(pipe_in[0], STDIN_FILENO); close(pipe_in[1]); dup2(pipe_out[1], STDOUT_FILENO); close(pipe_out[0]); dup2(pipe_err[1], STDERR_FILENO); close(pipe_err[0]); execve(argv[optind], argv + optind + 1, NULL); /* execve() should not return */ perror("axwrapper: execve"); exit(1); } /* parent */ close(pipe_in[0]); close(pipe_out[1]); close(pipe_err[1]); if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) { perror("axwrapper: fcntl"); exit(1); } if (fcntl(pipe_out[0], F_SETFL, O_NONBLOCK) == -1) { perror("axwrapper: fcntl"); exit(1); } if (fcntl(pipe_err[0], F_SETFL, O_NONBLOCK) == -1) { perror("axwrapper: fcntl"); exit(1); } while (1) { FD_ZERO(&fdset); int maxfd = -1; FD_SET(STDIN_FILENO, &fdset); if (STDIN_FILENO > maxfd) maxfd = STDIN_FILENO + 1; FD_SET(pipe_out[0], &fdset); if (pipe_out[0] > maxfd) maxfd = pipe_out[0] + 1; FD_SET(pipe_err[0], &fdset); if (pipe_err[0] > maxfd) maxfd = pipe_err[0] + 1; tv.tv_sec = 0; tv.tv_usec = FLUSHTIMEOUT; len = select(maxfd, &fdset, NULL, NULL, &tv); if (len == -1) { if (errno == EINTR) continue; perror("axwrapper: select"); exit(1); } if (len == 0) { fflush(stdout); } if (FD_ISSET(STDIN_FILENO, &fdset)) { len = read(STDIN_FILENO, buf, sizeof(buf)); if (len < 0 && errno != EAGAIN) { perror("axwrapper: read"); break; } if (len == 0) break; convert_cr_lf(buf, len); write(pipe_in[1], buf, len); } if (FD_ISSET(pipe_out[0], &fdset)) { len = read(pipe_out[0], buf, sizeof(buf)); if (len < 0 && errno != EAGAIN) { perror("axwrapper: read"); break; } if (len == 0) break; convert_lf_cr(buf, len); fwrite(buf, 1, len, stdout); } if (FD_ISSET(pipe_err[0], &fdset)) { len = read(pipe_err[0], buf, sizeof(buf)); if (len < 0 && errno != EAGAIN) { perror("axwrapper: read"); break; } if (len == 0) break; convert_lf_cr(buf, len); fwrite(buf, 1, len, stderr); } } kill(pid, SIGTERM); close(pipe_in[1]); close(pipe_out[0]); close(pipe_err[0]); return 0; } ax25/beacon.8000066400000000000000000000033071442776067000131750ustar00rootroot00000000000000.TH BEACON 8 "4 July 2016" Linux "Linux System Managers Manual" .SH NAME beacon \- transmit periodic messages on an AX.25 port. .SH SYNOPSIS .B beacon [-c ] [-d [digi ..]] [-f] [-H] [-l] [-m] [-s] [-t interval] [-v] port \(lqmessage\(rq .SH DESCRIPTION .LP .B Beacon transmits the message text on an AX.25 port every thirty minutes. The message is addressed to \(lqIDENT\(rq and is sent using the AX.25 callsign of the port specified on the command line. Typically the message text will contain spaces and/or other characters, therefore the message text should be enclosed in quotes to ensure they are passed to the .B beacon program untranslated. .SH OPTIONS .TP 16 .BI \-c Configure the source callsign for beacons. The default is to use the interface callsign. .TP 16 .BI \-d Configure the destination callsign for beacons. The default is \(lqIDENT\(rq. Optional: Digipeaters may follow, separated with spaces. Use \-d "DEST DIGI1 DIGI2 .." .TP 16 .BI \-f Do not fork into the background. .TP 16 .BI \-l Enables the logging of errors to the system log, the default is off. .TP 16 .BI \-H Start interval relative to the hour. .TP 16 .BI \-m Changes the destination address to \(lqMAIL\(rq and sends the message text once only. This option overrides any destination callsign given with the \-d option. .TP 16 .BI \-s Sends the message text once only. .TP 16 .BI "\-t interval" The time interval between messages, the interval is given in minutes and the default is thirty minutes. .TP 16 .BI \-v Display the version. .SH "SEE ALSO" .BR ax25 (4), .BR axports (5). .SH AUTHORS .nf Alan Cox GW4PTS .br Jonathan Naylor G4KLX .br David Brooke G6GZH .fi ax25/beacon.c000066400000000000000000000077341442776067000132600ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int logging = FALSE; static int mail = FALSE; static int single = FALSE; #define STR_USAGE "usage: beacon [-c ] [-d ] [-f] [-H] [-l] [-m] [-s] [-t interval] [-v] \n" static void terminate(int sig) { if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } exit(0); } int main(int argc, char *argv[]) { struct full_sockaddr_ax25 dest; struct full_sockaddr_ax25 src; int s, n, dlen, len; char *addr, *port, *message, *portcall; char *srccall = NULL, *destcall = NULL; int dofork = 1; time_t interval = 30; int interval_relative = 0; while ((n = getopt(argc, argv, "c:d:fHlmst:v")) != -1) { switch (n) { case 'c': srccall = optarg; break; case 'd': destcall = optarg; break; case 'f': dofork = 0; break; case 'H': interval_relative = 1; break; case 'l': logging = TRUE; break; case 'm': mail = TRUE; /* falls through */ case 's': single = TRUE; break; case 't': interval = (time_t ) atoi(optarg); if (interval < 1) { fprintf(stderr, "beacon: interval must be greater than on minute\n"); return 1; } break; case 'v': printf("beacon: %s\n", FULL_VER); return 0; case '?': case ':': fprintf(stderr, STR_USAGE); return 1; } } if (interval_relative && interval > 60L) { fprintf(stderr, "beacon: can't align interval > 60min to an hour\n"); return 1; } signal(SIGTERM, terminate); if (optind == argc || optind == argc - 1) { fprintf(stderr, STR_USAGE); return 1; } port = argv[optind]; message = argv[optind + 1]; if (ax25_config_load_ports() == 0) { fprintf(stderr, "beacon: no AX.25 ports defined\n"); return 1; } portcall = ax25_config_get_addr(port); if (portcall == NULL) { fprintf(stderr, "beacon: invalid AX.25 port setting - %s\n", port); return 1; } addr = NULL; if (mail) addr = strdup("MAIL"); else if (destcall != NULL) addr = strdup(destcall); else addr = strdup("IDENT"); if (addr == NULL) return 1; dlen = ax25_aton(addr, &dest); if (dlen == -1) { fprintf(stderr, "beacon: unable to convert callsign '%s'\n", addr); return 1; } if (addr != NULL) { free(addr); addr = NULL; } if (srccall != NULL && strcmp(srccall, portcall) != 0) { addr = malloc(strlen(srccall) + 1 + strlen(portcall) + 1); if (addr == NULL) return 1; sprintf(addr, "%s %s", srccall, portcall); } else { addr = strdup(portcall); if (addr == NULL) return 1; } len = ax25_aton(addr, &src); if (len == -1) { fprintf(stderr, "beacon: unable to convert callsign '%s'\n", addr); return 1; } if (addr != NULL) { free(addr); addr = NULL; } if (!single && dofork) { if (!daemon_start(FALSE)) { fprintf(stderr, "beacon: cannot become a daemon\n"); return 1; } } if (logging) { openlog("beacon", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting"); } /* interval has a one minute resolution */ interval *= 60L; for (;;) { if (interval_relative) { time_t t_sleep = interval - (time(NULL) % interval); if (t_sleep > 0) sleep(t_sleep); } s = socket(AF_AX25, SOCK_DGRAM, 0); if (s == -1) { if (logging) { syslog(LOG_ERR, "socket: %m"); closelog(); } return 1; } if (bind(s, (struct sockaddr *)&src, len) == -1) { if (logging) { syslog(LOG_ERR, "bind: %m"); closelog(); } return 1; } if (sendto(s, message, strlen(message), 0, (struct sockaddr *)&dest, dlen) == -1) { if (logging) { syslog(LOG_ERR, "sendto: %m"); closelog(); } return 1; } close(s); if (single) break; if (!interval_relative) sleep(interval); } return 0; } ax25/bpqparms.8000066400000000000000000000024211442776067000135670ustar00rootroot00000000000000.TH BPQPARMS 8 "4 September 1996" Linux "Linux System Managers Manual" .SH NAME bpqparms \- Configure BPQ ethernet devices. .SH SYNOPSIS .B bpqparms device [-a ethaddr] [-d ethaddr] [-vV] .SH DESCRIPTION .LP .B Bpqparms allows the setting of the BPQ Ethernet options for a particular device. Each BPQ Ethernet device appears as a device named bpq0...bpqN which overlays the original Ethernet device, usually eth0...ethN. This device is an AX.25 device driver and allows AX.25 frames to be transmitted over an Ethernet to another machine using the same protocol. The default for the device is to send and receive BPQ Ethernet packets to the broadcast address. This program replaces the previous \(lqaxparms -dev\(rq option. .SH OPTIONS .TP 15 .BI "\-a ethaddr" Allows the setting of which ethernet address will be accepted by the BPQ Ethernet device. .TP 15 .BI "\-d ethaddr" If specified on its own, will set the destination ethernet address will be used for transmitting and for receiving of BPQ ethernet packets. An address of \(lqbroadcast\(rq sets it to the ethernet broadcast address. .TP 15 .BI \-v Displays the version number. .TP 15 .BI \-V The original version messages. .SH "SEE ALSO" .BR ax25 (4), .BR axports (5), .BR ifconfig (8). .SH AUTHOR Joerg Reuter DL1BKE ax25/bpqparms.c000066400000000000000000000065721442776067000136550ustar00rootroot00000000000000/* bpqparms.c Copyright 1996, by Joerg Reuter jreuter@poboxes.com This program is free software; you can redistribute it and/or modify it under the terms of the (modified) GNU General Public License delivered with the LinuX kernel source. 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 find a copy of the GNU General Public License in /usr/src/linux/COPYING; */ #include #include #include #include #include #include #include #include #include #include #include /* is this really needed ?? */ #include /* xlz - dammit, we need this again */ #include #include static void usage(void) { fprintf(stderr, "usage : bpqparms dev -d address [-a address]\n"); fprintf(stderr, "examples: bpqparms bpq0 -d 00:80:AD:1B:05:26\n"); fprintf(stderr, " bpqparms bpq0 -d broadcast -a 00:80:AD:1B:05:26\n"); exit(1); } static int get_hwaddr(unsigned char *k, char *s) { unsigned char broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; unsigned int eth[ETH_ALEN]; int n; if (strcmp(s, "default") == 0 || strcmp(s, "broadcast") == 0) { memcpy(k, broadcast, ETH_ALEN); } else { n = sscanf(s, "%x:%x:%x:%x:%x:%x", ð[0], ð[1], ð[2], ð[3], ð[4], ð[5]); if (n < 6) return 1; for (n = 0; n < ETH_ALEN; n++) k[n] = eth[n]; } return 0; } int main(int argc, char **argv) { int fd; int cmd, flag; struct ifreq ifr; char dev[40]; struct bpq_ethaddr addr; flag = 0; while ((cmd = getopt(argc, argv, "d:a:vVh")) != EOF) { switch (cmd) { case 'd': flag |= 1; if (get_hwaddr(addr.destination, optarg)) { fprintf(stderr, "bpqparms: invalid 'destination' address %s\n", optarg); return 1; } break; case 'a': flag |= 2; if (get_hwaddr(addr.accept, optarg)) { fprintf(stderr, "bpqparms: invalid 'accept' address %s\n", optarg); return 1; } break; case 'V': printf("bpqparms version " FULL_VER "\n"); printf("Copyright 1996, Jörg Reuter (jreuter@poboxes.com)\n"); printf("This program is free software; you can redistribute it and/or modify\n"); printf("it under the terms of the GNU General Public License as published by\n"); printf("the Free Software Foundation; either version 2 of the License, or\n"); printf(" (at your option) any later version.\n\n"); printf("This program is distributed in the hope that it will be useful,\n"); printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); return 0; case 'v': printf("bpqparms: %s\n", FULL_VER); return 0; case 'h': case ':': case '?': usage(); } } if (!(flag & 0x01) || optind+1 > argc) usage(); strcpy(dev, argv[optind]); if ((flag & 0x02) == 0) memcpy(addr.accept, addr.destination, ETH_ALEN); fd = socket(AF_INET, SOCK_DGRAM, 0); strcpy(ifr.ifr_name, dev); ifr.ifr_data = (caddr_t) &addr; if (ioctl(fd, SIOCSBPQETHADDR, &ifr) < 0) { perror("bpqparms SIOCSBPQETHADDR"); close(fd); return 1; } close(fd); return 0; } ax25/md5.c000066400000000000000000000254031442776067000125070ustar00rootroot00000000000000 /* *********************************************************************** ** md5.c -- the source code for MD5 routines ** ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** ** Created: 2/17/90 RLR ** ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** *********************************************************************** */ /* *********************************************************************** ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** ** ** ** License to copy and use this software is granted provided that ** ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** ** Digest Algorithm" in all material mentioning or referencing this ** ** software or this function. ** ** ** ** License is also granted to make and use derivative works ** ** provided that such works are identified as "derived from the RSA ** ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** ** material mentioning or referencing the derived work. ** ** ** ** RSA Data Security, Inc. makes no representations concerning ** ** either the merchantability of this software or the suitability ** ** of this software for any particular purpose. It is provided "as ** ** is" without express or implied warranty of any kind. ** ** ** ** These notices must be retained in any copies of any part of this ** ** documentation and/or software. ** *********************************************************************** */ #include "md5.h" /* *********************************************************************** ** Message-digest routines: ** ** To form the message digest for a message M ** ** (1) Initialize a context buffer mdContext using MD5Init ** ** (2) Call MD5Update on mdContext and M ** ** (3) Call MD5Final on mdContext ** ** The message digest is now in mdContext->digest[0...15] ** *********************************************************************** */ /* forward declaration */ static void Transform(UINT4 *buf, UINT4 *in); static unsigned char PADDING[64] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* F, G, H and I are basic MD5 functions */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ /* Rotation is separate from addition to prevent recomputation */ #define FF(a, b, c, d, x, s, ac) \ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) \ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) \ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) \ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /* The routine MD5Init initializes the message-digest context mdContext. All fields are set to zero. */ void MD5Init(MD5_CTX *mdContext) { mdContext->i[0] = mdContext->i[1] = (UINT4)0; /* Load magic initialization constants. */ mdContext->buf[0] = (UINT4)0x67452301; mdContext->buf[1] = (UINT4)0xefcdab89; mdContext->buf[2] = (UINT4)0x98badcfe; mdContext->buf[3] = (UINT4)0x10325476; } /* The routine MD5Update updates the message-digest context to account for the presence of each of the characters inBuf[0..inLen-1] in the message whose digest is being computed. */ void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) { UINT4 in[16]; int mdi; unsigned int i, ii; /* compute number of bytes mod 64 */ mdi = (int)((mdContext->i[0] >> 3) & 0x3F); /* update number of bits */ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) mdContext->i[1]++; mdContext->i[0] += ((UINT4)inLen << 3); mdContext->i[1] += ((UINT4)inLen >> 29); while (inLen--) { /* add new character to buffer, increment mdi */ mdContext->in[mdi++] = *inBuf++; /* transform if necessary */ if (mdi == 0x40) { for (i = 0, ii = 0; i < 16; i++, ii += 4) in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | (((UINT4)mdContext->in[ii+2]) << 16) | (((UINT4)mdContext->in[ii+1]) << 8) | ((UINT4)mdContext->in[ii]); Transform (mdContext->buf, in); mdi = 0; } } } /* The routine MD5Final terminates the message-digest computation and ends with the desired message digest in mdContext->digest[0...15]. */ void MD5Final(MD5_CTX *mdContext) { UINT4 in[16]; int mdi; unsigned int i, ii; unsigned int padLen; /* save number of bits */ in[14] = mdContext->i[0]; in[15] = mdContext->i[1]; /* compute number of bytes mod 64 */ mdi = (int)((mdContext->i[0] >> 3) & 0x3F); /* pad out to 56 mod 64 */ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); MD5Update (mdContext, PADDING, padLen); /* append length in bits and transform */ for (i = 0, ii = 0; i < 14; i++, ii += 4) in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | (((UINT4)mdContext->in[ii+2]) << 16) | (((UINT4)mdContext->in[ii+1]) << 8) | ((UINT4)mdContext->in[ii]); Transform (mdContext->buf, in); /* store buffer in digest */ for (i = 0, ii = 0; i < 4; i++, ii += 4) { mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); mdContext->digest[ii+1] = (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); mdContext->digest[ii+2] = (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); mdContext->digest[ii+3] = (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); } } /* Basic MD5 step. Transforms buf based on in. */ static void Transform(UINT4 *buf, UINT4 *in) { UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; /* Round 1 */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 FF ( a, b, c, d, in[ 0], S11, 3614090360UL); /* 1 */ FF ( d, a, b, c, in[ 1], S12, 3905402710UL); /* 2 */ FF ( c, d, a, b, in[ 2], S13, 606105819UL); /* 3 */ FF ( b, c, d, a, in[ 3], S14, 3250441966UL); /* 4 */ FF ( a, b, c, d, in[ 4], S11, 4118548399UL); /* 5 */ FF ( d, a, b, c, in[ 5], S12, 1200080426UL); /* 6 */ FF ( c, d, a, b, in[ 6], S13, 2821735955UL); /* 7 */ FF ( b, c, d, a, in[ 7], S14, 4249261313UL); /* 8 */ FF ( a, b, c, d, in[ 8], S11, 1770035416UL); /* 9 */ FF ( d, a, b, c, in[ 9], S12, 2336552879UL); /* 10 */ FF ( c, d, a, b, in[10], S13, 4294925233UL); /* 11 */ FF ( b, c, d, a, in[11], S14, 2304563134UL); /* 12 */ FF ( a, b, c, d, in[12], S11, 1804603682UL); /* 13 */ FF ( d, a, b, c, in[13], S12, 4254626195UL); /* 14 */ FF ( c, d, a, b, in[14], S13, 2792965006UL); /* 15 */ FF ( b, c, d, a, in[15], S14, 1236535329UL); /* 16 */ /* Round 2 */ #define S21 5 #define S22 9 #define S23 14 #define S24 20 GG ( a, b, c, d, in[ 1], S21, 4129170786UL); /* 17 */ GG ( d, a, b, c, in[ 6], S22, 3225465664UL); /* 18 */ GG ( c, d, a, b, in[11], S23, 643717713UL); /* 19 */ GG ( b, c, d, a, in[ 0], S24, 3921069994UL); /* 20 */ GG ( a, b, c, d, in[ 5], S21, 3593408605UL); /* 21 */ GG ( d, a, b, c, in[10], S22, 38016083UL); /* 22 */ GG ( c, d, a, b, in[15], S23, 3634488961UL); /* 23 */ GG ( b, c, d, a, in[ 4], S24, 3889429448UL); /* 24 */ GG ( a, b, c, d, in[ 9], S21, 568446438UL); /* 25 */ GG ( d, a, b, c, in[14], S22, 3275163606UL); /* 26 */ GG ( c, d, a, b, in[ 3], S23, 4107603335UL); /* 27 */ GG ( b, c, d, a, in[ 8], S24, 1163531501UL); /* 28 */ GG ( a, b, c, d, in[13], S21, 2850285829UL); /* 29 */ GG ( d, a, b, c, in[ 2], S22, 4243563512UL); /* 30 */ GG ( c, d, a, b, in[ 7], S23, 1735328473UL); /* 31 */ GG ( b, c, d, a, in[12], S24, 2368359562UL); /* 32 */ /* Round 3 */ #define S31 4 #define S32 11 #define S33 16 #define S34 23 HH ( a, b, c, d, in[ 5], S31, 4294588738UL); /* 33 */ HH ( d, a, b, c, in[ 8], S32, 2272392833UL); /* 34 */ HH ( c, d, a, b, in[11], S33, 1839030562UL); /* 35 */ HH ( b, c, d, a, in[14], S34, 4259657740UL); /* 36 */ HH ( a, b, c, d, in[ 1], S31, 2763975236UL); /* 37 */ HH ( d, a, b, c, in[ 4], S32, 1272893353UL); /* 38 */ HH ( c, d, a, b, in[ 7], S33, 4139469664UL); /* 39 */ HH ( b, c, d, a, in[10], S34, 3200236656UL); /* 40 */ HH ( a, b, c, d, in[13], S31, 681279174UL); /* 41 */ HH ( d, a, b, c, in[ 0], S32, 3936430074UL); /* 42 */ HH ( c, d, a, b, in[ 3], S33, 3572445317UL); /* 43 */ HH ( b, c, d, a, in[ 6], S34, 76029189UL); /* 44 */ HH ( a, b, c, d, in[ 9], S31, 3654602809UL); /* 45 */ HH ( d, a, b, c, in[12], S32, 3873151461UL); /* 46 */ HH ( c, d, a, b, in[15], S33, 530742520UL); /* 47 */ HH ( b, c, d, a, in[ 2], S34, 3299628645UL); /* 48 */ /* Round 4 */ #define S41 6 #define S42 10 #define S43 15 #define S44 21 II ( a, b, c, d, in[ 0], S41, 4096336452UL); /* 49 */ II ( d, a, b, c, in[ 7], S42, 1126891415UL); /* 50 */ II ( c, d, a, b, in[14], S43, 2878612391UL); /* 51 */ II ( b, c, d, a, in[ 5], S44, 4237533241UL); /* 52 */ II ( a, b, c, d, in[12], S41, 1700485571UL); /* 53 */ II ( d, a, b, c, in[ 3], S42, 2399980690UL); /* 54 */ II ( c, d, a, b, in[10], S43, 4293915773UL); /* 55 */ II ( b, c, d, a, in[ 1], S44, 2240044497UL); /* 56 */ II ( a, b, c, d, in[ 8], S41, 1873313359UL); /* 57 */ II ( d, a, b, c, in[15], S42, 4264355552UL); /* 58 */ II ( c, d, a, b, in[ 6], S43, 2734768916UL); /* 59 */ II ( b, c, d, a, in[13], S44, 1309151649UL); /* 60 */ II ( a, b, c, d, in[ 4], S41, 4149444226UL); /* 61 */ II ( d, a, b, c, in[11], S42, 3174756917UL); /* 62 */ II ( c, d, a, b, in[ 2], S43, 718787259UL); /* 63 */ II ( b, c, d, a, in[ 9], S44, 3951481745UL); /* 64 */ buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } /* *********************************************************************** ** End of md5.c ** ******************************** (cut) ******************************** */ ax25/md5.h000066400000000000000000000062541442776067000125170ustar00rootroot00000000000000 /* *********************************************************************** ** md5.h -- header file for implementation of MD5 ** ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** ** Created: 2/17/90 RLR ** ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** ** Revised (for MD5): RLR 4/27/91 ** ** -- G modified to have y&~z instead of y&z ** ** -- FF, GG, HH modified to add in last register done ** ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** ** -- distinct additive constant for each step ** ** -- round 4 added, working mod 7 ** *********************************************************************** */ /* *********************************************************************** ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** ** ** ** License to copy and use this software is granted provided that ** ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** ** Digest Algorithm" in all material mentioning or referencing this ** ** software or this function. ** ** ** ** License is also granted to make and use derivative works ** ** provided that such works are identified as "derived from the RSA ** ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** ** material mentioning or referencing the derived work. ** ** ** ** RSA Data Security, Inc. makes no representations concerning ** ** either the merchantability of this software or the suitability ** ** of this software for any particular purpose. It is provided "as ** ** is" without express or implied warranty of any kind. ** ** ** ** These notices must be retained in any copies of any part of this ** ** documentation and/or software. ** *********************************************************************** */ #ifndef MD5_H #define MD5_H /* typedef a 32-bit type */ #ifdef _LP64 typedef unsigned int UINT4; #else typedef unsigned long int UINT4; #endif /* Data structure for MD5 (Message-Digest) computation */ typedef struct { UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ UINT4 buf[4]; /* scratch buffer */ unsigned char in[64]; /* input buffer */ unsigned char digest[16]; /* actual digest after MD5Final call */ } MD5_CTX; void MD5Init(MD5_CTX *mdContext); void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); void MD5Final(MD5_CTX *mdContext); #endif /* *********************************************************************** ** End of md5.h ** ******************************** (cut) ******************************** */ ax25/mheard.1000066400000000000000000000037511442776067000132020ustar00rootroot00000000000000.TH MHEARD 1 "4 July 2016" Linux "Linux Programmer's Manual" .SH NAME mheard \- display AX.25 calls recently heard. .SH SYNOPSIS .B mheard [-d cmnsa] [-n] [-o cfpt] [-v] [port...] .SH DESCRIPTION .LP .B Mheard displays information about most recently heard AX.25 callsigns, the interface upon which they were heard, the total packets heard, the time at which the last one was heard and other information. .B Mheard displays different information, in different orders depending on the settings of the arguments. Information on specific ports can be displayed by giving the port names as arguments. .SH OPTIONS .TP 13 .BI "\-d cmnsa" Sets the information that is displayed for each AX.25 callsign heard. The different arguments are: .RS .TP 5 .BI c Display all the information with regard to callsigns, from-callsign, to-callsign, port name, and any digipeaters that may be in use. .TP 5 .BI m Display miscellaneous information, the from-callsign, port name, no frames heard, the last type of frames heard, and which different PIDs have been heard from that station. .TP 5 .BI n Display the default information. This is the from-callsign, port name, no frames heard and the date and time last heard. .TP 5 .BI s Displays statistics about the station heard, the from-callsign, port name, no I frames, no S frames, no U frames, time first heard, and time last heard. .TP 5 .BI a Display the data from c and s combined in one view. Requires wide screen. .RE .TP 13 .BI \-n Suppresses the displaying of titles. .TP 13 .BI "\-o cfpt" Sets the ordering of the information displayed. The meanings of the different arguments are: .RS .TP 5 .BI c Sort list by from-callsign. .TP 5 .BI f Sort list by number of frames heard. .TP 5 .BI p Sort list by port name. .TP 5 .BI t Sort list by the time last heard, this is the default. .RE .TP 13 .BI \-v Display the version. .SH FILES .LP /var/ax25/mheard/mheard.dat .br /etc/ax25/axports .SH "SEE ALSO" .BR ax25 (4), .BR mheardd (8). .SH AUTHOR Jonathan Naylor G4KLX ax25/mheard.c000066400000000000000000000241661442776067000132670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" struct PortRecord { struct mheard_struct entry; struct PortRecord *Next; }; static char *types[] = { "SABM", "SABME", "DISC", "UA", "DM", "RR", "RNR", "REJ", "FRMR", "I", "UI", "????"}; static struct PortRecord *PortList; static void PrintHeader(int data) { switch (data) { case 0: printf("Callsign Port Packets Last Heard\n"); break; case 1: printf("Callsign Port\n"); break; case 2: printf("Callsign Port #I #S #U First Heard Last Heard\n"); break; case 3: printf("Callsign Port Packets Type PIDs\n"); break; case 4: printf("Callsign Port #I #S #U First Heard Last Heard Packets Type PIDs Targets\n"); break; } } static void PrintPortEntry(struct PortRecord *pr, int data) { char lh[20], fh[20], *call, *s; char buffer[80]; int i; struct tm *loc; /* port record data can be garbled. We cannot trust the data. time operations may returns NULL, also assure that pr->entry.type is < sizeof(types), and in 'case 4:', only up to 8 digipeaters are copied. */ switch (data) { case 0: if (!pr->entry.count) return; if (pr->entry.last_heard < 0) return; if ((loc = localtime(&pr->entry.last_heard)) == NULL || strftime(lh, sizeof(lh),"%Y-%m-%d %H:%M:%S", loc) == 0) return; call = ax25_ntoa(&pr->entry.from_call); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; printf("%-9s %-5s %8u %s\n", call, pr->entry.portname, pr->entry.count, lh); break; case 1: buffer[0] = '\0'; call = ax25_ntoa(&pr->entry.from_call); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; strcat(buffer, call); call = ax25_ntoa(&pr->entry.to_call); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; strcat(buffer, ">"); strcat(buffer, call); for (i = 0; i < pr->entry.ndigis && i < 4; i++) { strcat(buffer, ","); call = ax25_ntoa(&pr->entry.digis[i]); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; strcat(buffer, call); } if (pr->entry.ndigis >= 4) strcat(buffer, ",..."); printf("%-70s %-5s\n", buffer, pr->entry.portname); break; case 2: if (pr->entry.last_heard < 0 || pr->entry.first_heard < 0) return; if ((loc = localtime(&pr->entry.last_heard)) == NULL || strftime(lh, sizeof(lh),"%Y-%m-%d %H:%M:%S", loc) == 0) return; if ((loc = localtime(&pr->entry.first_heard)) == NULL || strftime(fh, sizeof(fh),"%Y-%m-%d %H:%M:%S", loc) == 0) return; call = ax25_ntoa(&pr->entry.from_call); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; printf("%-9s %-5s %8u %8u %8u %s %s\n", call, pr->entry.portname, pr->entry.iframes, pr->entry.sframes, pr->entry.uframes, fh, lh); break; case 3: if (!pr->entry.count) return; call = ax25_ntoa(&pr->entry.from_call); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; if (pr->entry.type > sizeof(types)) return; printf("%-9s %-5s %8u %5s ", call, pr->entry.portname, pr->entry.count, types[pr->entry.type]); if (pr->entry.mode & MHEARD_MODE_ARP) printf(" ARP"); if (pr->entry.mode & MHEARD_MODE_FLEXNET) printf(" FlexNet"); if (pr->entry.mode & MHEARD_MODE_IP_DG) printf(" IP-DG"); if (pr->entry.mode & MHEARD_MODE_IP_VC) printf(" IP-VC"); if (pr->entry.mode & MHEARD_MODE_NETROM) printf(" NET/ROM"); if (pr->entry.mode & MHEARD_MODE_ROSE) printf(" Rose"); if (pr->entry.mode & MHEARD_MODE_SEGMENT) printf(" Segment"); if (pr->entry.mode & MHEARD_MODE_TEXNET) printf(" TexNet"); if (pr->entry.mode & MHEARD_MODE_TEXT) printf(" Text"); if (pr->entry.mode & MHEARD_MODE_PSATFT) printf(" PacsatFT"); if (pr->entry.mode & MHEARD_MODE_PSATPB) printf(" PacsatPB"); if (pr->entry.mode & MHEARD_MODE_UNKNOWN) printf(" Unknown"); printf("\n"); break; case 4: if (!pr->entry.count) return; if (pr->entry.type > sizeof(types)) return; if (pr->entry.last_heard < 0 || pr->entry.first_heard < 0) return; if ((loc = localtime(&pr->entry.last_heard)) == NULL || strftime(lh, sizeof(lh),"%Y-%m-%d %H:%M:%S", loc) == 0) return; if ((loc = localtime(&pr->entry.first_heard)) == NULL || strftime(fh, sizeof(fh),"%Y-%m-%d %H:%M:%S", loc) == 0) return; call = ax25_ntoa(&pr->entry.from_call); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; printf("%-9s %-5s %8u %8u %8u %s %s %8u %5s ", call, pr->entry.portname, pr->entry.iframes, pr->entry.sframes, pr->entry.uframes, fh, lh, pr->entry.count, types[pr->entry.type]); if (pr->entry.mode & MHEARD_MODE_ARP) printf(" ARP "); if (pr->entry.mode & MHEARD_MODE_FLEXNET) printf(" FlexNet "); if (pr->entry.mode & MHEARD_MODE_IP_DG) printf(" IP-DG "); if (pr->entry.mode & MHEARD_MODE_IP_VC) printf(" IP-VC "); if (pr->entry.mode & MHEARD_MODE_NETROM) printf(" NET/ROM "); if (pr->entry.mode & MHEARD_MODE_ROSE) printf(" Rose "); if (pr->entry.mode & MHEARD_MODE_SEGMENT) printf(" Segment "); if (pr->entry.mode & MHEARD_MODE_TEXNET) printf(" TexNet "); if (pr->entry.mode & MHEARD_MODE_TEXT) printf(" Text "); if (pr->entry.mode & MHEARD_MODE_PSATFT) printf(" PacsatFT "); if (pr->entry.mode & MHEARD_MODE_PSATPB) printf(" PacsatPB "); if (pr->entry.mode & MHEARD_MODE_UNKNOWN) printf(" Unknown "); if (pr->entry.mode == 0) printf(" "); buffer[0] = '\0'; for (i = 0; i < pr->entry.ndigis && i < 8; i++) { if (i) strcat(buffer, ","); call = ax25_ntoa(&pr->entry.digis[i]); s = strstr(call, "-0"); if (s != NULL) *s = '\0'; strcat(buffer, call); } printf("%s\n", buffer); break; } } static void ListAllPorts(int data) { struct PortRecord *pr; for (pr = PortList; pr != NULL; pr = pr->Next) PrintPortEntry(pr, data); } static void ListOnlyPort(char *name, int data) { struct PortRecord *pr; for (pr = PortList; pr != NULL; pr = pr->Next) if (strcmp(pr->entry.portname, name) == 0) PrintPortEntry(pr, data); } static void LoadPortData(void) { FILE *fp; struct PortRecord *pr; struct mheard_struct mheard; fp = fopen(DATA_MHEARD_FILE, "r"); if (fp == NULL) { fprintf(stderr, "mheard: cannot open mheard data file\n"); exit(1); } while (fread(&mheard, sizeof(struct mheard_struct), 1, fp) == 1) { pr = malloc(sizeof(struct PortRecord)); pr->entry = mheard; /* assure 0-terminated portname in case the record is garbled */ pr->entry.portname[19] = '\0'; pr->Next = PortList; PortList = pr; } fclose(fp); } static void SortByTime(void) { struct PortRecord *p = PortList; struct PortRecord *n; PortList = NULL; while (p != NULL) { struct PortRecord *w = PortList; n = p->Next; if (w == NULL || p->entry.last_heard > w->entry.last_heard) { p->Next = w; PortList = p; p = n; continue; } while (w->Next != NULL && p->entry.last_heard <= w->Next->entry.last_heard) w = w->Next; p->Next = w->Next; w->Next = p; p = n; } } static void SortByPort(void) { struct PortRecord *p = PortList; struct PortRecord *n; PortList = NULL; while (p != NULL) { struct PortRecord *w = PortList; n = p->Next; if (w == NULL || strcmp(p->entry.portname, w->entry.portname) < 0) { p->Next = w; PortList = p; p = n; continue; } while (w->Next != NULL && strcmp(p->entry.portname, w->Next->entry.portname) >= 0) w = w->Next; p->Next = w->Next; w->Next = p; p = n; } } static void SortByCall(void) { struct PortRecord *p = PortList; struct PortRecord *n; PortList = NULL; while (p != NULL) { struct PortRecord *w = PortList; n = p->Next; if (w == NULL || memcmp(&p->entry.from_call, &w->entry.from_call, sizeof(ax25_address)) < 0) { p->Next = w; PortList = p; p = n; continue; } while (w->Next != NULL && memcmp(&p->entry.from_call, &w->Next->entry.from_call, sizeof(ax25_address)) >= 0) w = w->Next; p->Next = w->Next; w->Next = p; p = n; } } static void SortByFrame(void) { struct PortRecord *p = PortList; struct PortRecord *n; PortList = NULL; while (p != NULL) { struct PortRecord *w = PortList; n = p->Next; if (w == NULL || p->entry.count > w->entry.count) { p->Next = w; PortList = p; p = n; continue; } while (w->Next != NULL && p->entry.count <= w->Next->entry.count) w = w->Next; p->Next = w->Next; w->Next = p; p = n; } } int main(int argc, char *argv[]) { int headers = TRUE; int mode = 0; int data = 0; int c; while ((c = getopt(argc, argv, "d:no:v")) != -1) { switch (c) { case 'd': switch (*optarg) { case 'c': data = 1; break; case 'm': data = 3; break; case 'n': data = 0; break; case 's': data = 2; break; case 'a': data = 4; /* s + c */ break; default: fprintf(stderr, "mheard: invalid display type '%s'\n", optarg); return 1; } break; case 'n': headers = FALSE; break; case 'o': switch (*optarg) { case 'c': mode = 2; break; case 'f': mode = 3; break; case 'p': mode = 1; break; case 't': mode = 0; break; default: fprintf(stderr, "mheard: invalid ordering type '%s'\n", optarg); return 1; } break; case 'v': printf("mheard: %s\n", FULL_VER); return 0; case '?': case ':': fprintf(stderr, "Usage: %s [-d cmnsa] [-n] [-o cfpt] [-v] [port ...]\n", argv[0]); return 1; } } LoadPortData(); switch (mode) { case 0: SortByTime(); break; case 1: SortByPort(); break; case 2: SortByCall(); break; case 3: SortByFrame(); break; } if (argc == optind) { if (headers) PrintHeader(data); ListAllPorts(data); } else { while (argv[optind] != NULL) { if (headers) { printf("Port %s:\n", argv[optind]); PrintHeader(data); } ListOnlyPort(argv[optind], data); optind++; } } return 0; } ax25/mheard.dat000066400000000000000000000000001442776067000135720ustar00rootroot00000000000000ax25/mheardd.8000066400000000000000000000030461442776067000133520ustar00rootroot00000000000000.TH MHEARDD 8 "10 December 2005" Linux "Linux Programmer's Manual" .SH NAME mheardd \- collect information about packet activity .SH SYNOPSIS .B mheardd [-f] [-l] [-n number] [-p [!]port1[,port2,..]] [-v] .SH DESCRIPTION .LP .B Mheardd is a daemon that collects the statistics about the activity on all the AX.25 channels that are configured. The list generated is available for viewing by the .BR mheard (1) program. The information collected is a superset of the information normally collected by similar mheard programs and this is reflected in the options available for the viewing program. Logging to the system log file may be enabled which will enable monitoring of pathalogical conditions, for example invalid frame types and invalid/unknown protocol IDs. .SH OPTIONS .TP 10 .BI \-f Deletes the existing mheard logging file at program startup, this is not the default. .TP 10 .BI \-l Enables logging to the system log file. The default is off. .TP 10 .BI "\-n number" Sets the number of entries in the activity list file, the default is 100. The minimum value allowed is 10 and the maximum is 1000. .TP 10 .BI "\-p [!]port1[,port2,..]" With -p, you instruct mheardd to only listen on specified ax25 ports. You may give more ports, separated by ','. If you introduce the port(s) by a leading '!', then mheardd will listen on all ports except those you specified. .TP 10 .BI \-v Display the version. .SH FILES .LP /var/ax25/mheard/mheard.dat .br /etc/ax25/axports .SH "SEE ALSO" .BR mheard (1), .BR ax25 (4). .SH AUTHOR Jonathan Naylor G4KLX ax25/mheardd.c000066400000000000000000000240451442776067000134270ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #define KISS_MASK 0x0F #define KISS_DATA 0x00 #define PID_SEGMENT 0x08 #define PID_ARP 0xCD #define PID_NETROM 0xCF #define PID_IP 0xCC #define PID_ROSE 0x01 #define PID_TEXNET 0xC3 #define PID_FLEXNET 0xCE #define PID_TEXT 0xF0 #define PID_PSATFT 0xBB #define PID_PSATPB 0xBD #define I 0x00 #define S 0x01 #define RR 0x01 #define RNR 0x05 #define REJ 0x09 #define U 0x03 #define SABM 0x2F #define SABME 0x6F #define DISC 0x43 #define DM 0x0F #define UA 0x63 #define FRMR 0x87 #define UI 0x03 #define PF 0x10 #define EPF 0x01 #define MMASK 7 #define HDLCAEB 0x01 #define SSID 0x1E #define SSSID_SPARE 0x40 #define ESSID_SPARE 0x20 #define ALEN 6 #define AXLEN 7 struct mheard_list_struct { int in_use; struct mheard_struct entry; long position; }; static struct mheard_list_struct *mheard_list; #define MHEARD_LIST_SIZE 1000 static int mheard_list_size = MHEARD_LIST_SIZE/10; static int logging = FALSE; static int ftype(char *, int *, int); static struct mheard_list_struct *findentry(ax25_address *, char *); static void terminate(int sig) { if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } exit(0); } int main(int argc, char **argv) { struct mheard_list_struct *mheard; char buffer[1500]; char *data; int size, s; char *port = NULL; struct sockaddr sa; socklen_t asize; long position; int ctlen, type, end, extseq, flush = FALSE; FILE *fp; char *p; char ports[1024]; int ports_excl = 0; *ports = 0; while ((s = getopt(argc, argv, "fln:p:v")) != -1) { switch (s) { case 'l': logging = TRUE; break; case 'f': flush = TRUE; break; case 'n': mheard_list_size = atoi(optarg); if (mheard_list_size < 10 || mheard_list_size > MHEARD_LIST_SIZE) { fprintf(stderr, "mheardd: list size must be between 10 and %d\n", MHEARD_LIST_SIZE); return 1; } break; case 'p': if (strlen(optarg) > sizeof(ports)-4) { fprintf(stderr, "mheardd: too many ports specified."); return 1; } if (*optarg == '!') { ports_excl = 1; optarg++; } sprintf(ports, "|%s|", optarg); for (p = ports; *p; p++) { if (*p == ' ' || *p == ',') *p = '|'; } break; case 'v': printf("mheardd: %s\n", FULL_VER); return 0; case ':': fprintf(stderr, "mheardd: option -n needs an argument\n"); return 1; case '?': fprintf(stderr, "Usage: mheardd [-f] [-l] [-n number] [-p [!]port1[,port2,..]] [-v]\n"); return 1; } } signal(SIGTERM, terminate); if (ax25_config_load_ports() == 0) { fprintf(stderr, "mheardd: no AX.25 port data configured\n"); return 1; } mheard_list = calloc(mheard_list_size, sizeof(struct mheard_list_struct)); if (mheard_list == NULL) { fprintf(stderr, "mheardd: cannot allocate memory\n"); return 1; } if (flush) unlink(DATA_MHEARD_FILE); /* Load an existing heard list */ if ((fp = fopen(DATA_MHEARD_FILE, "r")) != NULL) { s = 0; position = ftell(fp); while (fread(buffer, sizeof(struct mheard_struct), 1, fp) == 1 && s < mheard_list_size) { memcpy(&mheard_list[s].entry, buffer, sizeof(struct mheard_struct)); mheard_list[s].in_use = TRUE; mheard_list[s].position = position; position = ftell(fp); s++; } fclose(fp); } else { fp = fopen(DATA_MHEARD_FILE, "w"); if (fp != NULL) fclose(fp); } s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_AX25)); if (s == -1) { perror("mheardd: socket"); return 1; } if (!daemon_start(FALSE)) { fprintf(stderr, "mheardd: cannot become a daemon\n"); return 1; } /* Use syslog for error messages rather than perror/fprintf */ if (logging) { openlog("mheardd", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting"); } for (;;) { asize = sizeof(sa); size = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &asize); if (size == -1) { if (logging) { syslog(LOG_ERR, "recv: %m"); closelog(); } return 1; } port = ax25_config_get_name(sa.sa_data); if (port == NULL) { if (logging) syslog(LOG_WARNING, "unknown port '%s'\n", sa.sa_data); continue; } if (*ports) { char testport[sizeof(sa.sa_data)+2]; sprintf(testport, "|%s|", sa.sa_data); if (ports_excl) { if (strstr(ports, testport)) { continue; } } else { if (!strstr(ports, testport)) { continue; } } } data = buffer; if ((*data & KISS_MASK) != KISS_DATA) continue; data++; size--; if (size < (AXLEN + AXLEN + 1)) { if (logging) syslog(LOG_WARNING, "packet too short\n"); continue; } mheard = findentry((ax25_address *)(data + AXLEN), port); if (!ax25_validate(data + 0) || !ax25_validate(data + AXLEN)) { if (logging) syslog(LOG_WARNING, "invalid callsign on port %s\n", port); continue; } memcpy(&mheard->entry.from_call, data + AXLEN, sizeof(ax25_address)); memcpy(&mheard->entry.to_call, data + 0, sizeof(ax25_address)); strcpy(mheard->entry.portname, port); mheard->entry.ndigis = 0; extseq = ((data[AXLEN + ALEN] & SSSID_SPARE) != SSSID_SPARE); end = (data[AXLEN + ALEN] & HDLCAEB); data += (AXLEN + AXLEN); size -= (AXLEN + AXLEN); while (!end) { memcpy(&mheard->entry.digis[mheard->entry.ndigis], data, sizeof(ax25_address)); mheard->entry.ndigis++; end = (data[ALEN] & HDLCAEB); data += AXLEN; size -= AXLEN; } if (size == 0) { if (logging) syslog(LOG_WARNING, "packet too short\n"); continue; } ctlen = ftype(data, &type, extseq); mheard->entry.count++; switch (type) { case SABM: mheard->entry.type = MHEARD_TYPE_SABM; mheard->entry.uframes++; break; case SABME: mheard->entry.type = MHEARD_TYPE_SABME; mheard->entry.uframes++; break; case DISC: mheard->entry.type = MHEARD_TYPE_DISC; mheard->entry.uframes++; break; case UA: mheard->entry.type = MHEARD_TYPE_UA; mheard->entry.uframes++; break; case DM: mheard->entry.type = MHEARD_TYPE_DM; mheard->entry.uframes++; break; case RR: mheard->entry.type = MHEARD_TYPE_RR; mheard->entry.sframes++; break; case RNR: mheard->entry.type = MHEARD_TYPE_RNR; mheard->entry.sframes++; break; case REJ: mheard->entry.type = MHEARD_TYPE_REJ; mheard->entry.sframes++; break; case FRMR: mheard->entry.type = MHEARD_TYPE_FRMR; mheard->entry.uframes++; break; case I: mheard->entry.type = MHEARD_TYPE_I; mheard->entry.iframes++; break; case UI: mheard->entry.type = MHEARD_TYPE_UI; mheard->entry.uframes++; break; default: if (logging) syslog(LOG_WARNING, "unknown packet type %02X\n", *data); mheard->entry.type = MHEARD_TYPE_UNKNOWN; break; } data += ctlen; size -= ctlen; if (type == I || type == UI) { unsigned char pid = *data; switch (pid) { case PID_TEXT: mheard->entry.mode |= MHEARD_MODE_TEXT; break; case PID_SEGMENT: mheard->entry.mode |= MHEARD_MODE_SEGMENT; break; case PID_ARP: mheard->entry.mode |= MHEARD_MODE_ARP; break; case PID_NETROM: mheard->entry.mode |= MHEARD_MODE_NETROM; break; case PID_IP: mheard->entry.mode |= (type == I) ? MHEARD_MODE_IP_VC : MHEARD_MODE_IP_DG; break; case PID_ROSE: mheard->entry.mode |= MHEARD_MODE_ROSE; break; case PID_TEXNET: mheard->entry.mode |= MHEARD_MODE_TEXNET; break; case PID_FLEXNET: mheard->entry.mode |= MHEARD_MODE_FLEXNET; break; case PID_PSATPB: mheard->entry.mode |= MHEARD_MODE_PSATPB; break; case PID_PSATFT: mheard->entry.mode |= MHEARD_MODE_PSATFT; break; default: if (logging) syslog(LOG_WARNING, "unknown PID %02X\n", *data); mheard->entry.mode |= MHEARD_MODE_UNKNOWN; break; } } if (mheard->entry.first_heard == 0) time(&mheard->entry.first_heard); time(&mheard->entry.last_heard); fp = fopen(DATA_MHEARD_FILE, "r+"); if (fp == NULL) { if (logging) syslog(LOG_ERR, "cannot open mheard data file\n"); continue; } if (mheard->position == 0xFFFFFF) { fseek(fp, 0L, SEEK_END); mheard->position = ftell(fp); } fseek(fp, mheard->position, SEEK_SET); fwrite(&mheard->entry, sizeof(struct mheard_struct), 1, fp); fclose(fp); } } static int ftype(char *data, int *type, int extseq) { if (extseq) { if ((*data & 0x01) == 0) { /* An I frame is an I-frame ... */ *type = I; return 2; } if (*data & 0x02) { *type = *data & ~PF; return 1; } else { *type = *data; return 2; } } else { if ((*data & 0x01) == 0) { /* An I frame is an I-frame ... */ *type = I; return 1; } if (*data & 0x02) { /* U-frames use all except P/F bit for type */ *type = *data & ~PF; return 1; } else { /* S-frames use low order 4 bits for type */ *type = *data & 0x0F; return 1; } } } static struct mheard_list_struct *findentry(ax25_address *callsign, char *port) { struct mheard_list_struct *oldest = NULL; int i; for (i = 0; i < mheard_list_size; i++) if (mheard_list[i].in_use && ax25_cmp(&mheard_list[i].entry.from_call, callsign) == 0 && strcmp(mheard_list[i].entry.portname, port) == 0) return mheard_list + i; for (i = 0; i < mheard_list_size; i++) { if (!mheard_list[i].in_use) { mheard_list[i].in_use = TRUE; mheard_list[i].position = 0xFFFFFF; return mheard_list + i; } } for (i = 0; i < mheard_list_size; i++) { if (mheard_list[i].in_use) { if (oldest == NULL) { oldest = mheard_list + i; } else { if (mheard_list[i].entry.last_heard < oldest->entry.last_heard) oldest = mheard_list + i; } } } memset(&oldest->entry, 0x00, sizeof(struct mheard_struct)); return oldest; } ax25/rxecho.8000066400000000000000000000022121442776067000132300ustar00rootroot00000000000000.TH RXECHO 8 "27 April 2008" Linux "Linux System Managers Manual" .SH NAME rxecho \- Route AX.25 packets between ports transparently. .SH SYNOPSIS .B rxecho [-l] [-v] .SH DESCRIPTION .LP .B Rxecho copies AX.25 frames between interfaces without altering their contents. The purpose of this utility is to allow other AX.25 aware programs/computers to share the same AX.25 ports as the Linux kernel AX.25 code. It could be used to route packets out onto another serial port to allow another machine running DOS based programs to share the same radio ports as the Linux machine, or it could route packets out onto a pseudo-tty to another application on the same machine. The copying could even be to another radio port. .LP The copying of the packets is controlled by a configuration file rxecho.conf(5), which can be set up to selectively copy packets. .SH OPTIONS .TP 10 .BI \-l Enables logging of errors to the system log, the default is off. .TP 10 .BI \-v Display the version. .SH FILES .nf /etc/ax25/axports .br /etc/ax25/rxecho.conf .fi .SH "SEE ALSO" .BR axports (5), .BR rxecho.conf (5), .BR kissattach (8). .SH AUTHOR Tomi Manninen OH2BNS ax25/rxecho.c000066400000000000000000000242031442776067000133070ustar00rootroot00000000000000/* * rxecho.c - Copies AX.25 packets from an interface to another interface. * Reads CONFIGFILE (see below) and uses that information to * decide what packets should be copied and where. * * CONFIGFILE format is: * * # this is a comment * 144 kiss0 oh2bns-1,oh2bns-2 * kiss0 144 * * * This means that packets received on port 144 are copied to port * kiss0 if they are destined to oh2bns-1 or oh2bns-2. Packets * from port kiss0 are all copied to port 144. * * There may be empty lines and an arbirary amount of white * space around the tokens but the callsign field must not * have any spaces in it. There can be up to MAXCALLS call- * signs in the callsign field (see below). * * Copyright (C) 1996 by Tomi Manninen, OH2BNS, . * * *** Modified 9/9/96 by Heikki Hannikainen, OH7LZB, : * * One port can actually be echoed to multiple ports (with a * different recipient callsign, of course). The old behaviour was * to give up on the first matching port, even if the recipient * callsign didn't match (and the frame wasn't echoed anywhere). * * *** 20021206 dl9sau: * - fixed a bug preventing echo to multible ports; it may also * lead to retransmission on the interface where it came from * - fixed problem that frames via sendto(...,alen) had a wrong * protocol (because alen became larger than the size of * struct sockaddr). * - added support for new PF_PACKET family with sockaddr_ll * * *** * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include /* for the glibc version number */ #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #define MAXCALLS 8 struct config { char from[IFNAMSIZ]; int from_idx; char to[IFNAMSIZ]; int to_idx; ax25_address calls[MAXCALLS];/* list of calls to echo */ int ncalls; /* number of calls to echo */ struct config *next; }; static int logging = FALSE; static void terminate(int sig) { if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } exit(0); } /* * Read string "call1,call2,call3,..." into p. */ static int read_calls(struct config *p, char *s) { char *cp, *cp1; if (p == NULL || s == NULL) return -1; p->ncalls = 0; if (strcmp(s, "*") == 0) return 0; cp = s; while ((cp1 = strchr(cp, ',')) != NULL && p->ncalls < MAXCALLS) { *cp1 = 0; if (ax25_aton_entry(cp, p->calls[p->ncalls].ax25_call) == -1) return -1; p->ncalls++; cp = ++cp1; } if (p->ncalls < MAXCALLS) { if (ax25_aton_entry(cp, p->calls[p->ncalls].ax25_call) == -1) return -1; p->ncalls++; } return p->ncalls; } static struct config *readconfig(void) { FILE *fp; char line[80], *cp, *dev; struct config *p, *list = NULL; fp = fopen(CONF_RXECHO_FILE, "r"); if (fp == NULL) { fprintf(stderr, "rxecho: cannot open config file\n"); return NULL; } while (fgets(line, 80, fp) != NULL) { cp = strtok(line, " \t\r\n"); if (cp == NULL || cp[0] == '#') continue; p = calloc(1, sizeof(struct config)); if (p == NULL) { perror("rxecho: malloc"); return NULL; } dev = ax25_config_get_dev(cp); if (dev == NULL) { fprintf(stderr, "rxecho: invalid port name - %s\n", cp); return NULL; } strcpy(p->from, dev); p->from_idx = -1; cp = strtok(NULL, " \t\r\n"); if (cp == NULL) { fprintf(stderr, "rxecho: config file error.\n"); return NULL; } dev = ax25_config_get_dev(cp); if (dev == NULL) { fprintf(stderr, "rxecho: invalid port name - %s\n", cp); return NULL; } strcpy(p->to, dev); p->to_idx = -1; if (read_calls(p, strtok(NULL, " \t\r\n")) == -1) { fprintf(stderr, "rxecho: config file error.\n"); return NULL; } p->next = list; list = p; } fclose(fp); if (list == NULL) fprintf(stderr, "rxecho: Empty config file!\n"); return list; } /* * Slightly modified from linux/include/net/ax25.h and * linux/net/ax25/ax25_subr.c: */ #if 0 #define C_COMMAND 1 #define C_RESPONSE 2 #define LAPB_C 0x80 #endif #define LAPB_E 0x01 #define AX25_ADDR_LEN 7 #define AX25_REPEATED 0x80 typedef struct { ax25_address calls[AX25_MAX_DIGIS]; unsigned char repeated[AX25_MAX_DIGIS]; char ndigi; char lastrepeat; } ax25_digi; /* * Given an AX.25 address pull of to, from, digi list, and the start of data. */ static unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi) { int d = 0; if (len < 14) return NULL; #if 0 if (flags != NULL) { *flags = 0; if (buf[6] & LAPB_C) { *flags = C_COMMAND; } if (buf[13] & LAPB_C) { *flags = C_RESPONSE; } } if (dama != NULL) *dama = ~buf[13] & DAMA_FLAG; #endif /* Copy to, from */ if (dest != NULL) memcpy(dest, buf + 0, AX25_ADDR_LEN); if (src != NULL) memcpy(src, buf + 7, AX25_ADDR_LEN); buf += 2 * AX25_ADDR_LEN; len -= 2 * AX25_ADDR_LEN; digi->lastrepeat = -1; digi->ndigi = 0; while (!(buf[-1] & LAPB_E)) { if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ if (len < 7) return NULL; /* Short packet */ if (digi != NULL) { memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); digi->ndigi = d + 1; if (buf[6] & AX25_REPEATED) { digi->repeated[d] = 1; digi->lastrepeat = d; } else { digi->repeated[d] = 0; } } buf += AX25_ADDR_LEN; len -= AX25_ADDR_LEN; d++; } return buf; } /* * Check if frame should be echoed. Return 0 if it should and -1 if not. */ static int check_calls(struct config *cfg, unsigned char *buf, int len) { ax25_address dest; ax25_digi digi; ax25_address *axp; int i; if ((buf[0] & 0x0F) != 0) return -1; /* don't echo non-data */ if (cfg->ncalls == 0) return 0; /* copy everything */ if (ax25_parse_addr(++buf, --len, NULL, &dest, &digi) == NULL) return -1; /* invalid ax.25 header */ /* * If there are no digis or all digis are already repeated * use destination address. Else use first non-repeated digi. */ if (digi.ndigi == 0 || digi.ndigi == digi.lastrepeat + 1) axp = &dest; else axp = &digi.calls[digi.lastrepeat + 1]; for (i = 0; i < cfg->ncalls; i++) if (ax25_cmp(&cfg->calls[i], axp) == 0) return 0; return -1; } int main(int argc, char **argv) { struct sockaddr_ll sll; struct sockaddr *psa = (struct sockaddr *)&sll; const socklen_t sa_len = sizeof(struct sockaddr_ll); int from_idx; int s, size; socklen_t alen; unsigned char buf[1500]; struct config *p, *list; while ((s = getopt(argc, argv, "lv")) != -1) { switch (s) { case 'l': logging = TRUE; break; case 'v': printf("rxecho: %s\n", FULL_VER); return 0; default: fprintf(stderr, "usage: rxecho [-l] [-v]\n"); return 1; } } signal(SIGTERM, terminate); if (ax25_config_load_ports() == 0) { fprintf(stderr, "rxecho: no AX.25 port data configured\n"); return 1; } list = readconfig(); if (list == NULL) return 1; s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_AX25)); if (s == -1) { perror("rxecho: socket:"); return 1; } for (p = list; p != NULL; p = p->next) { int i; for (i = 0; i < 2; i++) { struct config *q; struct ifreq ifr; char *p_name = (i ? p->to : p->from); int *p_idx = (i ? &p->to_idx : &p->from_idx); /* already set? */ if (*p_idx >= 0) continue; strncpy(ifr.ifr_name, p_name, sizeof(ifr.ifr_name)-1); ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0; if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { perror("SIOCGIFINDEX"); return 1; } *p_idx = ifr.ifr_ifindex; for (q = p->next; q != NULL; q = q->next) { if (q->from_idx < 0 && !strcmp(q->from, p_name)) q->from_idx = *p_idx; if (q->to_idx < 0 && !strcmp(q->to, p_name)) q->to_idx = *p_idx; } } } if (!daemon_start(FALSE)) { fprintf(stderr, "rxecho: cannot become a daemon\n"); close(s); return 1; } if (logging) { openlog("rxecho", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting"); } for (;;) { alen = sa_len; size = recvfrom(s, buf, 1500, 0, psa, &alen); if (size == -1) { if (logging) { syslog(LOG_ERR, "recvfrom: %m"); closelog(); } return 1; } from_idx = sll.sll_ifindex; for (p = list; p != NULL; p = p->next) if (p->from_idx == from_idx && (check_calls(p, buf, size) == 0)) { sll.sll_ifindex = p->to_idx; /* * cave: alen (set by recvfrom()) may > salen * which means it may point beyound of the * end of the struct. thats why we use the * correct len (sa_len). this fixes a bug * where skpt->protocol on sockaddr structs * pointed behind the struct, leading to * funny protocol IDs. * btw, the linux kernel internaly always * maps to sockaddr to sockaddr_pkt * on sockets of type SOCK_PACKET on family * AF_INET. sockaddr_pkt is len 18, sockaddr * is 16. */ if (sendto(s, buf, size, 0, psa, sa_len) == -1) { if (logging) { syslog(LOG_ERR, "sendto: %m"); closelog(); } } } } } ax25/rxecho.conf000066400000000000000000000006571442776067000140210ustar00rootroot00000000000000# /etc/ax25/rxecho.conf # # This means that packets received on port '1' are copied to port '2' if they # are destined to oh2bns-1 or oh2bns-2. All packets from port '2' are copied # to port '1'. # # There may be empty lines and an arbirary amount of white space around the # tokens but the callsign field must not have any spaces in it. There can be # up to MAXCALLS callsigns in the callsign field. # 1 2 oh2bns-1,oh2bns-2 2 1 * ax25/rxecho.conf.5000066400000000000000000000014051442776067000141540ustar00rootroot00000000000000.TH RXECHO.CONF 5 "2 August 1996" Linux "Linux Programmer's Manual" .SH NAME rxecho.conf \- control rxecho AX.25 packet routing. .SH DESCRIPTION .LP .B Rxecho.conf controls the copying of packets between AX.25 ports performed by the program rxecho. The format of the configuration file is: portin portout * | callsign... Each entry in the file represents a one-way packet flow between ports. Any packet received on portin is copied to portout, if its destination callsign or the next callsign to digipeat the frame is in the list of callsigns. If the callsign list is replaced by a \(lq*\(rq then all packets are copied. The configuration file may contain comments that begin with a #. .SH FILES .LP /etc/ax25/rxecho.conf .SH "SEE ALSO" .BR axports (8), .BR rxecho (8). configure.ac000066400000000000000000000050151442776067000133620ustar00rootroot00000000000000AC_PREREQ([2.59]) AC_INIT([ax25-tools],[0.0.10-rc5],[linux-hams@vger.kernel.org]) AC_CONFIG_SRCDIR(ax25/ax25d.c) AM_INIT_AUTOMAKE([1.7]) AC_CONFIG_HEADERS(config.h) dnl Checks for programs. AC_PROG_AWK AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL dnl Check for X and set a conditional AC_PATH_XTRA AM_CONDITIONAL(HAVE_X, test -z "$no_x") dnl Checks for libraries. AC_SUBST(Z_LIB) AC_SUBST(AX25_LIB) AC_SUBST(UTIL_LIB) AC_SUBST(FLTK_LIB) AC_CHECK_LIB(z, zlibVersion,Z_LIB="-lz",Z_LIB=) AC_CHECK_LIB(ax25, ax25_config_load_ports, AX25_LIB="-lax25", AC_MSG_ERROR([Could not find the libax25 libraries; aborting])) AC_CHECK_LIB(util, openpty, UTIL_LIB="-lutil",AC_MSG_ERROR([Could not find libutil; aborting])) AC_CHECK_LIB([fltk], [main], [FLTK_LIB=`fltk-config --ldflags`], FLTK_LIB=) AM_CONDITIONAL([HAVE_FLTK], [test -n "$FLTK_LIB"]) dnl Checks for header files. dnl AC_PATH_X AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(fcntl.h limits.h paths.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h zlib.h) dnl Checks for working glibc 2.1 headers AC_CHECK_TYPES([struct ax25_fwd_struct], [], [AC_MSG_ERROR([Both glibc and libax25 are too old or both installed])], [[#include ]]) AC_CHECK_MEMBER([struct nr_route_struct.ndigis], [], [AC_MSG_ERROR([Both glibc and libax25 are too old or both installed])], [[#include ]]) AC_CHECK_TYPES([struct rose_facilities_struct], [], [AC_MSG_ERROR([Both glibc and libax25 are too old or both installed])], [[#include /* Or will blow up */ #include ]]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_UID_T AC_TYPE_PID_T AC_HEADER_TIME AC_STRUCT_TM dnl Checks for library functions. AC_PROG_GCC_TRADITIONAL AC_FUNC_MEMCMP AC_FUNC_STRFTIME AC_FUNC_WAIT3 AC_CHECK_FUNCS(gethostname gettimeofday mkdir select socket strdup strerror strspn strstr strtol strtoul uname) dnl Only use -Wall if we have gcc if test "x$GCC" = "xyes"; then if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then CFLAGS="$CFLAGS -Wall" fi fi basever=$(echo $PACKAGE_VERSION | sed -e 's@-.*$@@') AC_SUBST(BASEVERSION, [$basever]) extraver=$(echo $PACKAGE_VERSION | sed -e 's@@<:@^-@:>@*-@@' -e 's@-@_@') AC_SUBST(EXTRAVERSION, [$extraver]) AC_CONFIG_FILES([netrom/Makefile tcpip/Makefile ax25/Makefile ax25/axgetput/Makefile Makefile rose/Makefile user_call/Makefile kiss/Makefile 6pack/Makefile hdlcutil/Makefile hdlcutil/fl/Makefile yamdrv/Makefile dmascc/Makefile ax25-tools.spec]) AC_OUTPUT dmascc/000077500000000000000000000000001442776067000123255ustar00rootroot00000000000000dmascc/.gitignore000066400000000000000000000000461442776067000143150ustar00rootroot00000000000000.deps Makefile Makefile.in dmascc_cfg dmascc/Makefile.am000066400000000000000000000002461442776067000143630ustar00rootroot00000000000000 sbin_PROGRAMS = dmascc_cfg dmascc_cfg_SOURCE = dmascc_cfg.c dist_man_MANS = dmascc_cfg.8 dist_doc_DATA = README.dmascc AM_CPPFLAGS = -D_GNU_SOURCE installconf: dmascc/README.dmascc000066400000000000000000000062051442776067000144410ustar00rootroot00000000000000The dmascc_cfg utility ====================== Use this utility to configure the dmascc driver. Usage: ------ dmascc_cfg [ options ... ] Options: -------- --show Show all configurable parameters for this port. May be specified at any time by any user. All other options change parameters and thus may only be specified by root and only when the interface is down. f ... floating-point argument, i ... integer argument. --frequency f Set frequency of baud rate generator. A value of 0 disables the baud rate generator and the digital PLL. Use the --show option to check whether the frequency you selected could be approximated with sufficient accuracy. --nrzi 0 | 1 0 selects NRZ mode, 1 selects NRZI mode. --clocks i Set clock mode. You may OR together three choices (other values are not supported and may cause strange results): TX clock pin: 0x00 input 0x05 output TX clock * 0x06 output baud rate generator * 0x07 output digital PLL * TX clock source: 0x00 RX clock pin 0x08 TX clock pin # 0x10 baud rate generator 0x18 digital PLL + RX clock source: 0x00 RX clock pin 0x20 TX clock pin 0x40 baud rate generator 0x60 digital PLL + * Not allowed on PI2 Port A if J3 is installed. # TX clock pin must be configured as input + The BRG frequency must be equal to 32 times the baud rate. --txdelay f Set the transmit delay (in ms). --txpause f Set the delay (in ms) between two transmitted packets. This parameter sometimes helps to reduce RX FIFO overruns, if the receiving station has a slower CPU than the transmitting station and cannot re-initialize the SCC's receiver quickly enough. --txtimeout f Set the time (in ms), after which the transmitter may not squeeze in another packet, but rather has to go off-air. --txtail f Set the time (in ms) between the end of the last packet and the RTS line actually going inactive. --rtsoff f Set the DCD settling time (in ms) after switching off the transmitter. During this period the DCD line is being ignored. This parameter is especially useful for the S5SCC/DMA, which frequently generates short DCD pulses after switching from TX to RX. --dcdon f Set the DCD settling time (in ms) after the DCD line has become active. This parameter should be < txdelay. --dcdoff f Set the DCD settling time (in ms) after the DCD line has become inactive. This parameter should be >= rtsoff. --slottime f Set the slot time (in ms). --persist i Set the persistence parameter (1 <= i <= 256). After the DCD off settling time has expired, the driver waits random(0..255)/persist*slottime before activating the RTS line. Integer arithmetic is being performed; persist==256 thus means zero delay in any case (which only makes sense in a two-station scenario). --waittime f Set the minimum time (in ms) before a station may start transmitting again (after the RTS off settling time has expired). This value should be larger than 255/persist*slottime. --dma i Set the DMA channel (-1, 0, 1, or 3). -1 disables DMA. 0 is only valid on the S5SCC/DMA. Remember, DMA can be used only on channel A (dmascc0, dmascc2, and so on). dmascc/dmascc_cfg.8000066400000000000000000000047711442776067000145000ustar00rootroot00000000000000.TH DMASCC_CFG 8 "30 June 1999" Linux "Linux Programmer's Manual" .SH NAME dmascc_cfg \- Configure dmascc devices .SH SYNOPSIS .B dmascc_cfg [] .SH DESCRIPTION .LP .B dmascc_cfg is used to configure dmascc devices such as PI2 and PackeTwin cards. The \fB\-\-show\fR option can be used by any user, all other options must be used by root. .SH OPTIONS .TP 10 .BI "\-\-speed "\fIfrequency\fR Set frequency of baud rate generator to \fIfrequency\fR. A value of 0 disables the baud rate generator and the digital PLL. Use the \fB\-\-show\fR option to check whether the frequency you selected could be approximated with sufficient accuracy. .TP 10 .BI "\-\-nrzi "\fR[\fI0\fR|\fI1\fR] \fI0\fR selects NRZ mode, \fI1\fR selects NRZI mode. .TP 10 .BI "\-\-clocks "\fIinteger\fR Set the clock mode. You may \fBOR\fR together three choices (other values are not supported and may cause strange results). .PP TX clock pin: .IP .ta 1.5i 0x00 input .br 0x05 output TX clock * .br 0x06 output baud rate generator * .br 0x07 output digital PLL * .PP TX clock source: .IP .ta 1.5i 0x00 RX clock pin .br 0x08 TX clock pin # .br 0x10 baud rate generator .br 0x18 digital PLL + .br .PP RX clock source: .IP .ta 1.5i 0x00 RX clock pin .br 0x20 TX clock pin .br 0x40 baud rate generator .br 0x60 digital PLL + .br .PP * Not allowed on PI2 Port A if J3 is installed. .PP # TX clock pin must be configured as input. .PP + Speed must be equal to 32 times the baud rate. .TP 10 .BI "\-\-txdelay "\fImilliseconds\fR Set transmit delay to \fImilliseconds\fR. Maximum is 2500 ms. .TP 10 .BI "\-\-txtime "\fIseconds\fR Set maximum time the transmitter may be active to \fIseconds\fR. .TP 10 .BI "\-\-sqdelay "\fImilliseconds\fR Set the squelch delay to \fImilliseconds\fR. Maximum delay is 2500 ms. .TP 10 .BI "\-\-slottime "\fImilliseconds\fR Set the slot time to \fImilliseconds\fR. Maximum slottime is 2500 ms. .TP 10 .BI "\-\-waittime "\fImilliseconds\fR Set the minimum time between the transmitter turning off to when it turns on to \fImilliseconds\fR. Maximum wait time is 2500 ms. .TP 10 .BI "\-\-persist "\fIf\fR Set the persistence parameter to \fIf\fR. Must be between 0 and 255 (inclusive). .TP 10 .BI "\-\-dma "\fIchannel\fR Set the DMA channel to \fIchannel\fR. Can be 1 or 3. Setting to 0 disables DMA. .LP .SH BUGS .B dmascc_cfg does not check the parameters for validity. The driver or kernel may crash if you specify invalid values. .SH AUTHORS .nf Klaus Kudielka .br This manual page written by Craig Small .fi dmascc/dmascc_cfg.c000066400000000000000000000263021442776067000145450ustar00rootroot00000000000000/* * Configuration utility for dmascc driver * Copyright (C) 1997,2000 Klaus Kudielka * * 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. */ #include #include #include #include #include #include #include #include #ifndef _SC_CLK_TCK #include #endif #ifndef _SC_CLK_TCK #include #endif /* Ioctls */ #define SIOCGSCCPARAM SIOCDEVPRIVATE #define SIOCSSCCPARAM (SIOCDEVPRIVATE+1) /* Frequency of timer 0 */ #define TMR_0_HZ 25600 /* Configurable parameters */ struct scc_param { int pclk_hz; /* frequency of BRG input (don't change) */ int brg_tc; /* BRG terminal count; BRG disabled if < 0 */ int nrzi; /* 0 (nrz), 1 (nrzi) */ int clocks; /* see dmascc_cfg documentation */ int txdelay; /* [1/TMR_0_HZ] */ int txtimeout; /* [1/HZ] */ int txtail; /* [1/TMR_0_HZ] */ int waittime; /* [1/TMR_0_HZ] */ int slottime; /* [1/TMR_0_HZ] */ int persist; /* 1 ... 256 */ int dma; /* -1 (disable), 0, 1, 3 */ int txpause; /* [1/TMR_0_HZ] */ int rtsoff; /* [1/TMR_0_HZ] */ int dcdon; /* [1/TMR_0_HZ] */ int dcdoff; /* [1/TMR_0_HZ] */ int reserved[35]; }; static void usage(void) { fprintf(stderr, "usage: dmascc_cfg [ options ... ]\n\n" "options: --show show updated configuration\n" " --frequency f frequency of baud rate generator in Hz\n" " --nrzi n NRZ (0) or NRZI (1) encoding\n" " --clocks n clock mode (see manual page)\n" " --txdelay t transmit delay in ms\n" " --txpause t inter-packet delay in ms\n" " --txtimeout t stop transmitting packets after t ms\n" " --txtail t transmit tail in ms\n" " --rtsoff t DCD settling time in ms (after RTS off)\n" " --dcdon t DCD settling time in ms (after DCD on)\n" " --dcdoff t DCD settling time in ms (after DCD off)\n" " --slottime t slot time in ms\n" " --persist n persistence parameter (1..256)\n" " --waittime t wait time after transmit in ms\n" " --dma n " "DMA channel: -1 (no DMA), 0 (S5SCC/DMA only), 1, 3\n" ); } int main(int argc, char *argv[]) { int sk, show = 0, set = 0, old = 0, secondary; struct ifreq ifr; struct scc_param param; char **option, *end, *error = NULL; long hz = -1L; if (argc < 2) { usage(); return 1; } sk = socket(AF_INET, SOCK_DGRAM, 0); if (sk < 0) { perror("socket"); return 2; } memset(¶m, 0, sizeof(param)); param.txpause = -1; if (strncmp(argv[1], "dmascc", 6)) { fprintf(stderr, "invalid interface name.\n"); return 5; } strncpy(ifr.ifr_name, argv[1], IFNAMSIZ); ifr.ifr_data = (caddr_t) ¶m; if (ioctl(sk, SIOCGSCCPARAM, &ifr) < 0) { perror("ioctl"); close(sk); return 3; } if (param.txpause == -1) { param.txpause = 0; old = 1; } #ifdef _SC_CLK_TCKxx hz = sysconf(_SC_CLK_TCK); if (hz == -1) perror("sysconf(_SC_CLK_TCK)"); #endif if (hz == -1) { #ifdef HZ hz = HZ; #else hz = 100; #endif fprintf(stderr, "warning: cannot dermine the clock rate HZ on which this system is running.\n"); fprintf(stderr, " Assuming %ld, what may be wrong.\n", hz); } secondary = argv[1][strlen(argv[1])-1]%2; option = argv + 2; while (!error && *option != NULL) { if (!strcmp(*option, "--show")) { show = 1; option++; } else if (!strcmp(*option, "--frequency")) { option++; if (*option != NULL) { double f; set = 1; f = strtod(*option++, &end); if (*end) error = "frequency not a number"; else { if (f < 0.0) error = "frequency < 0"; else if (f == 0.0) param.brg_tc = -1; else { param.brg_tc = param.pclk_hz / (f * 2) - 2; if (param.brg_tc > 0xffff) error = "frequency too low"; if (param.brg_tc < 0) error = "frequency too high"; } } } else error = "--frequency requires parameter"; } else if (!strcmp(*option, "--nrzi")) { option++; if (*option != NULL) { set = 1; param.nrzi = strtol(*option++, &end, 0); if (*end || param.nrzi < 0 || param.nrzi > 1) error = "nrzi must be 0 or 1"; } else error = "--nrzi requires parameter"; } else if (!strcmp(*option, "--clocks")) { option++; if (*option != NULL) { set = 1; param.clocks = strtol(*option++, &end, 0); if (*end) error = "clock mode not a number"; else if ((param.clocks & ~0x7f)) error = "invalid clock mode"; } else error = "--clocks requires parameter"; } else if (!strcmp(*option, "--txdelay")) { option++; if (*option != NULL) { set = 1; param.txdelay = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "txdelay not a number"; else if (param.txdelay < 0) error = "txdelay < 0"; else if (param.txdelay > 0xffff) error = "txdelay too large"; } else error = "--txdelay requires parameter"; } else if (!strcmp(*option, "--txpause")) { option++; if (*option != NULL) { set = 1; param.txpause = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "txpause not a number"; else if (param.txpause < 0) error = "txpause < 0"; else if (param.txpause > 0xffff) error = "txpause too large"; if (old && param.txpause != 0) fprintf(stderr, "warning: old driver; txpause not supported.\n"); } else error = "--txpause requires parameter"; } else if (!strcmp(*option, "--txtimeout")) { option++; if (*option != NULL) { set = 1; param.txtimeout = hz * strtod(*option++, &end) / 1000.0; if (*end) error = "txtimeout not a number"; else if (param.txtimeout < 0) error = "txtimeout < 0"; } else error = "--txtimeout requires parameter"; } else if (!strcmp(*option, "--txtail")) { option++; if (*option != NULL) { set = 1; param.txtail = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "txtail not a number"; else if (param.txtail < 0) error = "txtail < 0"; else if (param.txtail > 0xffff) error = "txtail too large"; } else error = "--txtail requires parameter"; } else if (!strcmp(*option, "--rtsoff")) { option++; if (*option != NULL) { set = 1; param.rtsoff = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "rtsoff not a number"; else if (param.rtsoff < 0) error = "rtsoff < 0"; else if (param.rtsoff > 0xffff) error = "rtsoff too large"; if (old && param.rtsoff != 0) fprintf(stderr, "warning: old driver; rtsoff not supported.\n"); } else error = "--rtsoff requires parameter"; } else if (!strcmp(*option, "--dcdon")) { option++; if (*option != NULL) { set = 1; param.dcdon = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "dcdon not a number"; else if (param.dcdon < 0) error = "dcdon < 0"; else if (param.dcdon > 0xffff) error = "dcdon too large"; if (old && param.dcdon != 0) fprintf(stderr, "warning: old driver; dcdon not supported.\n"); } else error = "--dcdon requires parameter"; } else if (!strcmp(*option, "--dcdoff")) { option++; if (*option != NULL) { set = 1; param.dcdoff = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "dcdoff not a number"; else if (param.dcdoff < 0) error = "dcdoff < 0"; else if (param.dcdoff > 0xffff) error = "dcdoff too large"; if (old && param.dcdoff != 0) fprintf(stderr, "warning: old driver; dcdoff not supported.\n"); } else error = "--dcdoff requires parameter"; } else if (!strcmp(*option, "--slottime")) { option++; if (*option != NULL) { set = 1; param.slottime = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "slottime not a number"; else if (param.slottime < 0) error = "slottime < 0"; else if (param.slottime > 0xffff) error = "slottime too large"; } else error = "--slottime requires parameter"; } else if (!strcmp(*option, "--persist")) { option++; if (*option != NULL) { set = 1; param.persist = strtol(*option++, &end, 0); if (*end) error = "persist not a number"; else if (param.persist < 1) error = "persist < 1"; else if (param.persist > 256) error = "persist > 256"; } else error = "--persist requires parameter"; } else if (!strcmp(*option, "--waittime")) { option++; if (*option != NULL) { set = 1; param.waittime = TMR_0_HZ * strtod(*option++, &end) / 1000.0; if (*end) error = "waittime not a number"; else if (param.waittime < 0) error = "waittime < 0"; else if (param.waittime > 0xffff) error = "waittime too large"; } else error = "--waittime requires parameter"; } else if (!strcmp(*option, "--dma")) { option++; if (*option != NULL) { int dma = param.dma; set = 1; param.dma = strtol(*option++, &end, 0); if (*end) error = "DMA channel not a number"; else if (secondary && param.dma != -1) error = "SCC port B must have DMA disabled"; else if (param.dma < -1 || param.dma == 2 || param.dma > 3) error = "invalid DMA channel"; else if (param.pclk_hz != 9830400 && param.dma == 0) error = "only S5SCC/DMA supports DMA channel 0"; else if (old && param.dma == 0) error = "old driver; DMA channel 0 not supported"; else if (old && param.dma == -1 && dma > 0) error = "old driver; reload module or reboot to disable DMA"; else if (old && param.dma == -1 && dma == 0) param.dma = 0; } else error = "--dma requires parameter"; } else error = "invalid option"; } if (error) { fprintf(stderr, "usage error: %s.\n", error); close(sk); return 1; } if (set) { if (ioctl(sk, SIOCSSCCPARAM, &ifr) < 0) { perror("ioctl"); close(sk); return 4; } } if (show) { double f; if (param.brg_tc < 0) f = 0.0; else f = ((double) param.pclk_hz) / ( 2 * (param.brg_tc + 2)); printf("dmascc_cfg %s \\\n--frequency %.2f --nrzi %d --clocks 0x%02X " "--txdelay %.2f \\\n--txpause %.2f --txtimeout %.2f " "--txtail %.2f --rtsoff %.2f \\\n--dcdon %.2f --dcdoff %.2f " "--slottime %.2f --persist %d \\\n--waittime %.2f --dma %d\n", argv[1], f, param.nrzi, param.clocks, param.txdelay * 1000.0 / TMR_0_HZ, param.txpause * 1000.0 / TMR_0_HZ, param.txtimeout * 1000.0 / hz, param.txtail * 1000.0 / TMR_0_HZ, param.rtsoff * 1000.0 / TMR_0_HZ, param.dcdon * 1000.0 / TMR_0_HZ, param.dcdoff * 1000.0 / TMR_0_HZ, param.slottime * 1000.0 / TMR_0_HZ, param.persist, param.waittime * 1000.0 / TMR_0_HZ, (old && param.dma == 0) ? -1 : param.dma); } close(sk); return 0; } hdlcutil/000077500000000000000000000000001442776067000127035ustar00rootroot00000000000000hdlcutil/.gitignore000066400000000000000000000000661442776067000146750ustar00rootroot00000000000000*.o .deps Makefile Makefile.in sethdlc smdiag smmixer hdlcutil/Makefile.am000066400000000000000000000012521442776067000147370ustar00rootroot00000000000000 if HAVE_X X11_bin_programs = smdiag endif SUBDIRS = . fl bin_PROGRAMS = sethdlc smmixer $(X11_bin_programs) sethdlc_SOURCES = sethdlc.c hdrvcomm.c hdrvcomm.h usersmdiag.h soundmodem.h smmixer_SOURCES = smmixer.c hdrvcomm.c hdrvcomm.h usersmdiag.h soundmodem.h smdiag_SOURCES = smdiag.c hdrvcomm.c hdrvcomm.h usersmdiag.h soundmodem.h smdiag_LDADD = $(X_LIBS) -lX11 $(X_EXTRA_LIBS) AM_CPPFLAGS = -D_GNU_SOURCE \ -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" AX25_SYSCONFDIR=$(sysconfdir)/ax25/ AX25_LOCALSTATEDIR=$(localstatedir)/ax25/ dist_man_MANS = sethdlc.8 smmixer.8 baycom.9 hdlcdrv.9 soundmodem.9 smdiag.8 hdlcutil/baycom.9000066400000000000000000000041151442776067000142500ustar00rootroot00000000000000.\" Copyright 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) .\" May be distributed under the GNU General Public License .\" " .TH BAYCOM 9 "27 April 2008" "Linux 2.1.x" "Kernel Reference Guide" .SH NAME baycom \- amateur (AX.25) packet radio network driver for baycom modems .SH SYNOPSIS .nf .B #include .B #include .fi .SH DESCRIPTION The driver currently supports three different modems: ser12, par96 and par97. .SS ser12 This is a very simple 1200 baud AFSK modem. The modem consists only of a modulator/demodulator chip, usually a TI TCM3105. The computer is responsible for regenerating the receiver bit clock. The modem connects to a serial port, hence the name. Since the serial port is not used as an async serial port, the kernel driver for serial ports cannot be used, and this driver only supports standard serial hardware (8250, 16450, 16550). .SS par96 This is a modem for 9600 baud FSK compatible to the G3RUH standard. The modem does all the filtering and regenerates the receiver clock. Data is transferred from and to the PC via a shift register. The shift register is filled with 16 bits and an interrupt is signalled. The PC then empties the shift register in a burst. This modem connects to the parallel port, hence the name. .SS par97 This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem is protocol compatible to par96, but uses only three low power ICs and can therefore be fed from the parallel port and does not require an additional power supply. .SH "IOCTL CALLS" The \fBioctl\fP calls follow the implementation in the \fIhdlcdrv\fP. .TP .B BAYCOMCTL_GETMODEMTYPE returns the modem type (i.e. \fIser12\fP or \fIpar96\fP) and the options in effect (currently only the source of the DCD signal) .TP .B BAYCOMCTL_SETMODEMTYPE sets the modem type and the options. Only superuser can do this. .TP .B BAYCOMCTL_GETDEBUG return some debugging values. Not always available. .SH "SEE ALSO" .BR baycom " (9), " soundmodem " (9)," linux/drivers/net/hdlcdrv.c, .SH AUTHOR baycom was written by Thomas Sailer, HB9JNX/AE4WA, (t.sailer@alumni.ethz.ch). hdlcutil/fl/000077500000000000000000000000001442776067000133045ustar00rootroot00000000000000hdlcutil/fl/.gitignore000066400000000000000000000001031442776067000152660ustar00rootroot00000000000000.deps Makefile Makefile.in xfhdlcchpar xfhdlcst xfsmdiag xfsmmixer hdlcutil/fl/Makefile.am000066400000000000000000000007601442776067000153430ustar00rootroot00000000000000 if HAVE_FLTK sbin_PROGRAMS = xfsmmixer xfhdlcchpar xfhdlcst xfsmdiag endif AM_CPPFLAGS = -D_GNU_SOURCE $(X_CFLAGS) -I$(srcdir)/.. LDADD = ../hdrvcomm.o $(X_LIBS) -lX11 $(FLTK_LIB) $(LDDADD) xfsmmixer_SOURCES = xfsmmixer.cxx xfsmmixer_main.cxx xfsmmixer.h xfhdlcchpar_SOURCES = xfhdlcchpar.cxx xfhdlcchpar_main.cxx xfhdlcchpar.h xfhdlcst_SOURCES = xfhdlcst.cxx xfhdlcst_main.cxx xfhdlcst.h xfsmdiag_SOURCES = xfsmdiag.cxx xfsmdiag_main.cxx xfsmdiag.h xfsmdiag_main.h hdrvcomm.o: ../hdrvcomm.o hdlcutil/fl/xfhdlcchpar.cxx000066400000000000000000000042521442776067000163210ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #include "xfhdlcchpar.h" Fl_Window *chpar=(Fl_Window *)0; Fl_Value_Slider *txdelay=(Fl_Value_Slider *)0; Fl_Value_Slider *txtail=(Fl_Value_Slider *)0; Fl_Value_Slider *slottime=(Fl_Value_Slider *)0; Fl_Value_Slider *ppersist=(Fl_Value_Slider *)0; Fl_Check_Button *fulldup=(Fl_Check_Button *)0; Fl_Button *quit=(Fl_Button *)0; Fl_Output *ifname=(Fl_Output *)0; Fl_Window* create_the_forms() { Fl_Window* w; { Fl_Window* o = chpar = new Fl_Window(390, 260, "HDLC channel parameters"); w = o; o->box(FL_NO_BOX); o->labeltype(FL_NORMAL_LABEL); { Fl_Box* o = new Fl_Box(0, 0, 390, 260); o->box(FL_UP_BOX); } { Fl_Box* o = new Fl_Box(10, 10, 370, 240); o->box(FL_DOWN_BOX); } { Fl_Value_Slider* o = txdelay = new Fl_Value_Slider(24, 30, 341, 30, "Tx Delay (ms)"); o->type(3); o->labelsize(8); o->callback((Fl_Callback*)cb_update); } { Fl_Value_Slider* o = txtail = new Fl_Value_Slider(24, 80, 341, 30, "Tx Tail (ms)"); o->type(3); o->labelsize(8); o->callback((Fl_Callback*)cb_update); } { Fl_Value_Slider* o = slottime = new Fl_Value_Slider(24, 130, 341, 30, "Slottime (ms)"); o->type(3); o->labelsize(8); o->callback((Fl_Callback*)cb_update); } { Fl_Value_Slider* o = ppersist = new Fl_Value_Slider(24, 180, 341, 30, "P-Persistence"); o->type(3); o->labelsize(8); o->callback((Fl_Callback*)cb_update); } { Fl_Check_Button* o = fulldup = new Fl_Check_Button(20, 220, 90, 20, "Full Duplex"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)cb_update); } { Fl_Button* o = quit = new Fl_Button(290, 220, 70, 20, "Quit"); o->callback((Fl_Callback*)cb_quit, (void*)(0)); } { Fl_Box* o = new Fl_Box(150, 220, 60, 20, "Interface"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = ifname = new Fl_Output(220, 220, 60, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } o->end(); } return w; } hdlcutil/fl/xfhdlcchpar.h000066400000000000000000000013651442776067000157500ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #ifndef xfhdlcchpar_h #define xfhdlcchpar_h #include #include extern Fl_Window *chpar; #include #include extern void cb_update(Fl_Widget*, void*); extern void cb_update(Fl_Value_Slider*, void*); extern Fl_Value_Slider *txdelay; extern Fl_Value_Slider *txtail; extern Fl_Value_Slider *slottime; extern Fl_Value_Slider *ppersist; #include extern void cb_update(Fl_Check_Button*, void*); extern Fl_Check_Button *fulldup; #include extern void cb_quit(Fl_Button*, long); extern Fl_Button *quit; #include extern Fl_Output *ifname; Fl_Window* create_the_forms(); #endif hdlcutil/fl/xfhdlcchpar_main.cxx000066400000000000000000000072641442776067000173330ustar00rootroot00000000000000/*****************************************************************************/ /* * xfhdlcchpar_main.C -- kernel hdlc radio modem driver channel parameter setting utility. * * Copyright (C) 1996,1997,2000 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 14.12.1996 Started * 0.2 11.05.1997 introduced hdrvcomm.h * 0.3 05.01.2000 upgraded to fltk 1.0.7 */ /*****************************************************************************/ #include #include #include #include #include #include #include "hdrvcomm.h" #include "xfhdlcchpar.h" #include #include /* ---------------------------------------------------------------------- */ static char *progname; /* ---------------------------------------------------------------------- */ void cb_update(Fl_Widget *widget, void *udata) { int ret; struct hdrvc_channel_params cp; cp.tx_delay = (int)(0.1*txdelay->value()); cp.tx_tail = (int)(0.1*txtail->value()); cp.slottime = (int)(0.1*slottime->value()); cp.ppersist = (int)(ppersist->value()); cp.fulldup = !!fulldup->value(); ret = hdrvc_set_channel_access_param(cp); if (ret < 0) perror("hdrvc_set_channel_access_param"); } /* ---------------------------------------------------------------------- */ void cb_quit(Fl_Button *, long) { exit(0); } /* ---------------------------------------------------------------------- */ static const char *usage_str = "[-i smif]\n" " -i: specify the name of the baycom kernel driver interface\n\n"; /* ---------------------------------------------------------------------- */ int main(int argc, char *argv[]) { int c, i; int ret; struct hdrvc_channel_params cp; progname = *argv; printf("%s: Version " FULL_VER "; (C) 1996,1997,2000 by Thomas Sailer HB9JNX/AE4WA\n", progname); hdrvc_args(&argc, argv, "bc0"); for (i = 1; i < argc; ) { c = i; Fl::arg(argc, argv, c); if (c <= 0) { i++; continue; } memmove(&argv[i], &argv[i+c], (argc-i) * sizeof(char *)); argc -= c; } #if 0 while ((ret = getopt(argc, argv, "")) != -1) { switch (ret) { default: printf("usage: %s %s", progname, usage_str); exit(-1); } } #endif hdrvc_init(); create_the_forms(); ifname->value(hdrvc_ifname()); /* * set channel params */ ret = hdrvc_get_channel_access_param(&cp); if (ret < 0) { perror("hdrvc_get_channel_access_param"); exit(1); } txdelay->step(10); txdelay->bounds(0, 2550); txdelay->value(cp.tx_delay*10); txtail->step(10); txtail->bounds(0, 2550); txtail->value(cp.tx_tail*10); slottime->step(10); slottime->bounds(0, 2550); slottime->value(cp.slottime*10); ppersist->step(1); ppersist->bounds(0, 255); ppersist->value(cp.ppersist); fulldup->value(!!cp.fulldup); chpar->show(); Fl::run(); exit(0); } hdlcutil/fl/xfhdlcst.cxx000066400000000000000000000114401442776067000156470ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #include "xfhdlcst.h" Fl_Window *hdlcst=(Fl_Window *)0; Fl_Round_Button *dcd=(Fl_Round_Button *)0; Fl_Round_Button *ptt=(Fl_Round_Button *)0; Fl_Output *txpacket=(Fl_Output *)0; Fl_Output *txerror=(Fl_Output *)0; Fl_Output *rxpacket=(Fl_Output *)0; Fl_Output *rxerror=(Fl_Output *)0; Fl_Output *modcyc=(Fl_Output *)0; Fl_Output *demodcyc=(Fl_Output *)0; Fl_Output *intfreq=(Fl_Output *)0; Fl_Output *dmares=(Fl_Output *)0; Fl_Box *tmodcyc=(Fl_Box *)0; Fl_Box *tdemodcyc=(Fl_Box *)0; Fl_Box *tintfreq=(Fl_Box *)0; Fl_Box *tdmares=(Fl_Box *)0; Fl_Output *modename=(Fl_Output *)0; Fl_Output *drivername=(Fl_Output *)0; Fl_Button *quit=(Fl_Button *)0; Fl_Window* create_the_forms() { Fl_Window* w; { Fl_Window* o = hdlcst = new Fl_Window(250, 300, "HDLC driver state"); w = o; o->box(FL_NO_BOX); { Fl_Box* o = new Fl_Box(0, 0, 250, 300); o->box(FL_UP_BOX); } { Fl_Box* o = new Fl_Box(10, 230, 230, 60); o->box(FL_DOWN_BOX); } { Fl_Box* o = new Fl_Box(10, 10, 230, 220); o->box(FL_DOWN_BOX); } { Fl_Round_Button* o = dcd = new Fl_Round_Button(160, 20, 20, 20); o->down_box(FL_ROUND_DOWN_BOX); o->selection_color(3); o->align(FL_ALIGN_CENTER); } { Fl_Round_Button* o = ptt = new Fl_Round_Button(160, 40, 20, 20); o->down_box(FL_ROUND_DOWN_BOX); o->align(FL_ALIGN_CENTER); } { Fl_Output* o = txpacket = new Fl_Output(160, 60, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = txerror = new Fl_Output(160, 80, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = rxpacket = new Fl_Output(160, 100, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = rxerror = new Fl_Output(160, 120, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = modcyc = new Fl_Output(160, 140, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = demodcyc = new Fl_Output(160, 160, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = intfreq = new Fl_Output(160, 180, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = dmares = new Fl_Output(160, 200, 70, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = new Fl_Box(20, 60, 110, 20, "Transmit Packets"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = new Fl_Box(20, 80, 110, 20, "Transmit Errors"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = new Fl_Box(20, 100, 110, 20, "Receive Packets"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = new Fl_Box(20, 120, 110, 20, "Receive Errors"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = new Fl_Box(20, 40, 110, 20, "PTT"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = new Fl_Box(20, 20, 110, 20, "DCD"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = tmodcyc = new Fl_Box(20, 140, 110, 20, "Modulator Cycles"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = tdemodcyc = new Fl_Box(20, 160, 110, 20, "Demodulator Cycles"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = tintfreq = new Fl_Box(20, 180, 110, 20, "Interrupt Frequency"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = tdmares = new Fl_Box(20, 200, 110, 20, "DMA residue"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = modename = new Fl_Output(20, 240, 210, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); } { Fl_Output* o = drivername = new Fl_Output(20, 260, 210, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE); } { Fl_Button* o = quit = new Fl_Button(190, 20, 40, 30, "Quit"); o->callback((Fl_Callback*)cb_quit, (void*)(0)); } o->end(); } return w; } hdlcutil/fl/xfhdlcst.h000066400000000000000000000014661442776067000153030ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #ifndef xfhdlcst_h #define xfhdlcst_h #include #include extern Fl_Window *hdlcst; #include #include extern Fl_Round_Button *dcd; extern Fl_Round_Button *ptt; #include extern Fl_Output *txpacket; extern Fl_Output *txerror; extern Fl_Output *rxpacket; extern Fl_Output *rxerror; extern Fl_Output *modcyc; extern Fl_Output *demodcyc; extern Fl_Output *intfreq; extern Fl_Output *dmares; extern Fl_Box *tmodcyc; extern Fl_Box *tdemodcyc; extern Fl_Box *tintfreq; extern Fl_Box *tdmares; extern Fl_Output *modename; extern Fl_Output *drivername; #include extern void cb_quit(Fl_Button*, long); extern Fl_Button *quit; Fl_Window* create_the_forms(); #endif hdlcutil/fl/xfhdlcst_main.cxx000066400000000000000000000104741442776067000166610ustar00rootroot00000000000000/*****************************************************************************/ /* * xfhdlcst_main.C -- kernel hdlc radio modem driver status display utility. * * Copyright (C) 1996,1997,2000 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 14.12.1996 Started * 0.2 11.05.1997 introduced hdrvcomm.h * 0.3 05.01.2000 upgraded to fltk 1.0.7 */ /*****************************************************************************/ #include #include #include #include #include #include #include "hdrvcomm.h" #include "xfhdlcst.h" #include #include /* ---------------------------------------------------------------------- */ static char *progname; static bool update = true; /* ---------------------------------------------------------------------- */ void cb_quit(Fl_Button *, long) { exit(0); } /* ---------------------------------------------------------------------- */ void cb_timer(void *) { update = true; } /* ---------------------------------------------------------------------- */ static const char *usage_str = "[-i smif]\n" " -i: specify the name of the baycom kernel driver interface\n\n"; /* ---------------------------------------------------------------------- */ int main(int argc, char *argv[]) { int c, i; int ret; struct hdrvc_channel_state cs; struct sm_ioctl smi; char buf[32]; char name[64]; progname = *argv; printf("%s: Version " FULL_VER "; (C) 1996,1997,2000 by Thomas Sailer HB9JNX/AE4WA\n", *argv); hdrvc_args(&argc, argv, "bc0"); for (i = 1; i < argc; ) { c = i; Fl::arg(argc, argv, c); if (c <= 0) { i++; continue; } memmove(&argv[i], &argv[i+c], (argc-i) * sizeof(char *)); argc -= c; } #if 0 while ((ret = getopt(argc, argv, "")) != -1) { switch (ret) { default: printf("usage: %s %s", *argv, usage_str); exit(-1); } } #endif hdrvc_init(); create_the_forms(); Fl::add_timeout(0.1, cb_timer); /* * set driver and modem name */ ret = hdrvc_get_mode_name(name, sizeof(name)); if (ret < 0) { perror("hdrvc_get_mode_name"); modename->hide(); } else modename->value(name); ret = hdrvc_get_driver_name(name, sizeof(name)); if (ret < 0) { perror("hdrvc_get_driver_name"); drivername->hide(); } else drivername->value(name); /* * check for soundmodem driver */ ret = hdrvc_sm_ioctl(SMCTL_GETDEBUG, &smi); if (ret < 0) { tdemodcyc->hide(); tmodcyc->hide(); tintfreq->hide(); tdmares->hide(); demodcyc->hide(); modcyc->hide(); intfreq->hide(); dmares->hide(); } hdlcst->show(); for (;;) { Fl::wait(); if (!update) continue; update = false; Fl::add_timeout(0.5, cb_timer); /* * display state */ ret = hdrvc_get_channel_state(&cs); if (ret >= 0) { ptt->value(cs.ptt); dcd->value(cs.dcd); sprintf(buf, "%ld", cs.tx_packets); txpacket->value(buf); sprintf(buf, "%ld", cs.tx_errors); txerror->value(buf); sprintf(buf, "%ld", cs.rx_packets); rxpacket->value(buf); sprintf(buf, "%ld", cs.rx_errors); rxerror->value(buf); } ret = hdrvc_sm_ioctl(SMCTL_GETDEBUG, &smi); if (ret >= 0) { sprintf(buf, "%d", smi.data.dbg.int_rate); intfreq->value(buf); sprintf(buf, "%d", smi.data.dbg.mod_cycles); modcyc->value(buf); sprintf(buf, "%d", smi.data.dbg.demod_cycles); demodcyc->value(buf); sprintf(buf, "%d", smi.data.dbg.dma_residue); dmares->value(buf); } } exit (0); } hdlcutil/fl/xfsmdiag.cxx000066400000000000000000000074501442776067000156400ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #include "xfsmdiag.h" Fl_Window *scopewindow=(Fl_Window *)0; scope *scdisp=(scope *)0; Fl_Button *cleargr=(Fl_Button *)0; Fl_Group *scopemode=(Fl_Group *)0; Fl_Check_Button *sm_off=(Fl_Check_Button *)0; Fl_Check_Button *sm_input=(Fl_Check_Button *)0; Fl_Check_Button *sm_demod=(Fl_Check_Button *)0; Fl_Check_Button *sm_constell=(Fl_Check_Button *)0; Fl_Check_Button *sm_dcd=(Fl_Check_Button *)0; Fl_Button *quit=(Fl_Button *)0; Fl_Round_Button *st_dcd=(Fl_Round_Button *)0; Fl_Round_Button *st_ptt=(Fl_Round_Button *)0; Fl_Output *modename=(Fl_Output *)0; Fl_Output *drivername=(Fl_Output *)0; Fl_Window* create_the_forms() { Fl_Window* w; { Fl_Window* o = scopewindow = new Fl_Window(700, 550, "SoundCard Modem Driver Diagnose Tool"); w = o; o->box(FL_NO_BOX); { Fl_Box* o = new Fl_Box(0, 0, 700, 550); o->box(FL_UP_BOX); } { scope* o = scdisp = new scope(10, 10, 530, 530); o->box(FL_DOWN_BOX); } { Fl_Button* o = cleargr = new Fl_Button(640, 150, 50, 60, "Clear\nGraph"); o->callback((Fl_Callback*)cb_cleargr, (void*)(0)); } { Fl_Box* o = new Fl_Box(550, 310, 140, 70, "(C) 1996 by\nTom Sailer\nHB9JNX/AE4WA"); o->box(FL_EMBOSSED_BOX); o->labelsize(18); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Group* o = scopemode = new Fl_Group(550, 10, 140, 130); o->align(FL_ALIGN_CENTER); { Fl_Box* o = new Fl_Box(550, 10, 140, 130); o->box(FL_DOWN_BOX); } { Fl_Check_Button* o = sm_off = new Fl_Check_Button(560, 20, 120, 20, "Off"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)cb_mode, (void*)(0)); } { Fl_Check_Button* o = sm_input = new Fl_Check_Button(560, 40, 120, 20, "Input"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)cb_mode, (void*)(1)); } { Fl_Check_Button* o = sm_demod = new Fl_Check_Button(560, 60, 120, 20, "Demodulator"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)cb_mode, (void*)(2)); } { Fl_Check_Button* o = sm_constell = new Fl_Check_Button(560, 80, 120, 20, "Constellation"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)cb_mode, (void*)(3)); } { Fl_Check_Button* o = sm_dcd = new Fl_Check_Button(560, 110, 120, 20, "Gated with DCD"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)cb_mode, (void*)(256)); } o->end(); } { Fl_Button* o = quit = new Fl_Button(550, 270, 140, 30, "Quit"); o->callback((Fl_Callback*)cb_quit, (void*)(0)); } { Fl_Box* o = new Fl_Box(550, 150, 80, 60); o->box(FL_DOWN_BOX); } { Fl_Round_Button* o = st_dcd = new Fl_Round_Button(600, 160, 20, 20); o->down_box(FL_ROUND_DOWN_BOX); o->selection_color(3); o->align(FL_ALIGN_CENTER); } { Fl_Round_Button* o = st_ptt = new Fl_Round_Button(600, 180, 20, 20); o->down_box(FL_ROUND_DOWN_BOX); o->align(FL_ALIGN_CENTER); } { Fl_Box* o = new Fl_Box(560, 180, 40, 20, "PTT"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Box* o = new Fl_Box(560, 160, 40, 20, "DCD"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = modename = new Fl_Output(550, 220, 140, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Output* o = drivername = new Fl_Output(550, 240, 140, 20); o->box(FL_EMBOSSED_BOX); o->selection_color(49); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } o->end(); } return w; } hdlcutil/fl/xfsmdiag.h000066400000000000000000000016461442776067000152660ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #ifndef xfsmdiag_h #define xfsmdiag_h #include #include extern Fl_Window *scopewindow; #include #include "xfsmdiag_main.h" extern scope *scdisp; #include extern void cb_cleargr(Fl_Button*, long); extern Fl_Button *cleargr; #include extern Fl_Group *scopemode; #include extern void cb_mode(Fl_Check_Button*, long); extern Fl_Check_Button *sm_off; extern Fl_Check_Button *sm_input; extern Fl_Check_Button *sm_demod; extern Fl_Check_Button *sm_constell; extern Fl_Check_Button *sm_dcd; extern void cb_quit(Fl_Button*, long); extern Fl_Button *quit; #include extern Fl_Round_Button *st_dcd; extern Fl_Round_Button *st_ptt; #include extern Fl_Output *modename; extern Fl_Output *drivername; Fl_Window* create_the_forms(); #endif hdlcutil/fl/xfsmdiag_main.cxx000066400000000000000000000240421442776067000166400ustar00rootroot00000000000000/*****************************************************************************/ /* * xfsmdiag.c -- kernel soundcard radio modem driver diagnostics utility. * * Copyright (C) 1996,1997,2000 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 14.12.1996 Started * 0.2 11.05.1997 introduced hdrvcomm.h * 0.3 05.01.2000 upgraded to fltk 1.0.7 */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hdrvcomm.h" #include "xfsmdiag.h" #include #include #include #include /* ---------------------------------------------------------------------- */ static char *progname; static unsigned int drawflags; /* ---------------------------------------------------------------------- */ scope::scope(int x, int y, int w, int h, const char *l) : Fl_Box(x, y, w, h, l) { box(FL_DOWN_FRAME); X = x+3; Y = y+4; W = w-6; H = h-8; drawmode = HDRVC_DIAGMODE_OFF; pixmalloc = false; hide(); } scope::~scope() { if (pixmalloc) { XFreePixmap(fl_display, pixmap); pixmalloc = false; } } void scope::resize(int xx, int yy, int ww, int hh) { if (pixmalloc && (ww != w() || hh != h())) { XFreePixmap(fl_display, pixmap); pixmalloc = false; } X = xx+3; Y = yy+4; W = ww-6; H = hh-8; Fl_Box::resize(xx, yy, ww, hh); } void scope::draw() { GC gc; XGCValues gcv; /* cannot use draw_box(); as it clears the whole window -> flicker */ /* from fl_boxtype.C, fl_down_frame */ if (W > 0 && H > 0) { fl_color(FL_DARK1); fl_xyline(X-3, Y-4, X+W+1); fl_color(FL_DARK2); fl_yxline(X-3, Y+H+3, Y-3, X+W+1); fl_color(FL_DARK3); fl_yxline(X-2, Y+H+2, Y-2, X+W); fl_color(FL_LIGHT3); fl_xyline(X-1, Y+H+1, X+W+1, Y-2); fl_color(FL_LIGHT2); fl_xyline(X-2, Y+H+2, X+W+2, Y-4); fl_color(FL_LIGHT1); fl_xyline(X-3, Y+H+3, X+W+2); } draw_label(); if (pixmalloc) XCopyArea(fl_display, pixmap, fl_window, fl_gc, 0, 0, W, H, X, Y); else { gcv.line_width = 1; gcv.line_style = LineSolid; gcv.fill_style = FillSolid; gc = XCreateGC(fl_display, pixmap, GCLineWidth | GCLineStyle | GCFillStyle, &gcv); XSetState(fl_display, gc, col_background, col_background, GXcopy, AllPlanes); XFillRectangle(fl_display, fl_window, gc, X, Y, W, H); XFreeGC(fl_display, gc); } XSync(fl_display, 0); } /* ---------------------------------------------------------------------- */ int scope::mode(void) { mode(drawmode); return drawmode; } void scope::mode(int dmode) { Fl_Window *wnd; if (dmode != drawmode) { if (pixmalloc) { XFreePixmap(fl_display, pixmap); pixmalloc = false; } drawmode = dmode; switch (drawmode) { case HDRVC_DIAGMODE_OFF: default: hide(); break; case HDRVC_DIAGMODE_INPUT: case HDRVC_DIAGMODE_DEMOD: size(512+6, 256+8); show(); break; case HDRVC_DIAGMODE_CONSTELLATION: size(512+6, 512+8); show(); break; } } if (pixmalloc || !visible() || W < 2 || H < 2) return; if (!(wnd = window())) return; wnd->make_current(); if (!(pixmap = XCreatePixmap(fl_display, fl_window, W, H, fl_visual->depth))) { fprintf(stderr, "unable to open offscreen pixmap\n"); exit(1); } pixmalloc = true; col_zeroline = fl_xpixel(FL_RED); col_background = fl_xpixel(FL_WHITE); col_trace = fl_xpixel(FL_BLACK); clear(); } /* ---------------------------------------------------------------------- */ void scope::clear(void) { GC gc; XGCValues gcv; if (!pixmalloc) return; gcv.line_width = 1; gcv.line_style = LineSolid; gcv.fill_style = FillSolid; gc = XCreateGC(fl_display, pixmap, GCLineWidth | GCLineStyle | GCFillStyle, &gcv); XSetState(fl_display, gc, col_background, col_background, GXcopy, AllPlanes); XFillRectangle(fl_display, pixmap, gc, 0, 0, W, H); XFreeGC(fl_display, gc); redraw(); } /* ---------------------------------------------------------------------- */ #define WIDTH 512 /* ---------------------------------------------------------------------- */ void scope::drawdata(short data[], int len, int xm) { int cnt; GC gc; XGCValues gcv; mode(); if (!pixmalloc || (drawmode != HDRVC_DIAGMODE_CONSTELLATION && drawmode != HDRVC_DIAGMODE_INPUT && drawmode != HDRVC_DIAGMODE_DEMOD)) return; gcv.line_width = 1; gcv.line_style = LineSolid; gc = XCreateGC(fl_display, pixmap, GCLineWidth | GCLineStyle, &gcv); if (drawmode == HDRVC_DIAGMODE_CONSTELLATION) { #define XCOORD(x) ((SHRT_MAX - (int)(x)) * W / USHRT_MAX) #define YCOORD(y) ((SHRT_MAX - (int)(y)) * H / USHRT_MAX) XSetState(fl_display, gc, col_background, col_background, GXcopy, AllPlanes); XSetForeground(fl_display, gc, col_trace); for (cnt = 0; cnt < len-1; cnt += 2) XDrawPoint(fl_display, pixmap, gc, XCOORD(data[cnt]), YCOORD(data[cnt+1])); XSetForeground(fl_display, gc, col_zeroline); XDrawLine(fl_display, pixmap, gc, 0, YCOORD(0), W, YCOORD(0)); XDrawLine(fl_display, pixmap, gc, XCOORD(0), 0, XCOORD(0), H); #undef XCOORD #undef YCOORD } else { #define XCOORD(x) ((x) * xm) #define YCOORD(y) ((SHRT_MAX - (int)(y)) * H / USHRT_MAX) XSetState(fl_display, gc, col_background, col_background, GXcopy, AllPlanes); if (drawmode == HDRVC_DIAGMODE_INPUT) { XFillRectangle(fl_display, pixmap, gc, 0, 0, W, H); xm = 5; } XSetForeground(fl_display, gc, col_trace); for (cnt = 0; cnt < len-1; cnt++) XDrawLine(fl_display, pixmap, gc, XCOORD(cnt), YCOORD(data[cnt]), XCOORD(cnt+1), YCOORD(data[cnt+1])); XSetForeground(fl_display, gc, col_zeroline); XDrawLine(fl_display, pixmap, gc, 0, YCOORD(0), W, YCOORD(0)); #undef XCOORD #undef YCOORD } XFreeGC(fl_display, gc); redraw(); } /* ---------------------------------------------------------------------- */ void cb_cleargr(Fl_Button *, long) { scdisp->clear(); } /* ---------------------------------------------------------------------- */ void cb_mode(Fl_Check_Button *, long which) { struct sm_diag_data diag; short data; diag.mode = HDRVC_DIAGMODE_OFF; diag.flags = 0; diag.datalen = 1; diag.data = &data; hdrvc_diag(&diag); switch (which) { case 256: drawflags ^= HDRVC_DIAGFLAG_DCDGATE; break; case 0: scdisp->mode(HDRVC_DIAGMODE_OFF); drawflags = 0; break; case 1: scdisp->mode(HDRVC_DIAGMODE_INPUT); drawflags = 0; break; case 2: scdisp->mode(HDRVC_DIAGMODE_DEMOD); drawflags = HDRVC_DIAGFLAG_DCDGATE; break; case 3: scdisp->mode(HDRVC_DIAGMODE_CONSTELLATION); drawflags = HDRVC_DIAGFLAG_DCDGATE; break; } sm_dcd->value(!!(drawflags & HDRVC_DIAGFLAG_DCDGATE)); } /* ---------------------------------------------------------------------- */ void cb_quit(Fl_Button *, long) { struct sm_diag_data diag; short data; diag.mode = HDRVC_DIAGMODE_OFF; diag.flags = 0; diag.datalen = 1; diag.data = &data; hdrvc_diag(&diag); exit(0); } /* ---------------------------------------------------------------------- */ void cb_timer(void *) { struct hdrvc_channel_state cs; int ret; short data[256]; unsigned int samplesperbit; Fl::add_timeout(0.2, cb_timer); /* * display state */ ret = hdrvc_get_channel_state(&cs); if (ret < 0) { perror("hdrvc_get_channel_state"); } else { st_ptt->value(cs.ptt); st_dcd->value(cs.dcd); } /* * draw scope */ if ((ret = hdrvc_diag2(scdisp->mode(), drawflags, data, sizeof(data) / sizeof(short), &samplesperbit)) < 0) { perror("hdrvc_diag2"); exit(1); } if (ret > 0) scdisp->drawdata(data, ret, WIDTH / (2*(samplesperbit > 0 ? samplesperbit : 1))); } /* ---------------------------------------------------------------------- */ static const char *usage_str = "[-i smif]\n" " -i: specify the name of the baycom kernel driver interface\n\n"; /* ---------------------------------------------------------------------- */ int main(int argc, char *argv[]) { int c, i; int ret; unsigned int ifflags; char name[64]; progname = *argv; printf("%s: Version " FULL_VER "; (C) 1996,1997,2000 by Thomas Sailer HB9JNX/AE4WA\n", *argv); hdrvc_args(&argc, argv, "sm0"); for (i = 1; i < argc; ) { c = i; Fl::arg(argc, argv, c); if (c <= 0) { i++; continue; } memmove(&argv[i], &argv[i+c], (argc-i) * sizeof(char *)); argc -= c; } #if 0 while ((ret = getopt(argc, argv, "")) != -1) { switch (ret) { default: printf("usage: %s %s", *argv, usage_str); exit(-1); } } #endif hdrvc_init(); ifflags = hdrvc_get_ifflags(); if (!(ifflags & IFF_UP)) { fprintf(stderr, "interface %s down\n", hdrvc_ifname()); exit(1); } if (!(ifflags & IFF_RUNNING)) { fprintf(stderr, "interface %s not running\n", hdrvc_ifname()); exit(1); } create_the_forms(); scdisp->hide(); /* * set driver and modem name */ ret = hdrvc_get_mode_name(name, sizeof(name)); if (ret < 0) { perror("hdrvc_get_mode_name"); modename->hide(); } else modename->value(name); ret = hdrvc_get_driver_name(name, sizeof(name)); if (ret < 0) { perror("hdrvc_get_driver_name"); drivername->hide(); } else drivername->value(name); Fl::add_timeout(0.1, cb_timer); scopewindow->show(); Fl::run(); cb_quit(quit, 0); exit(0); } hdlcutil/fl/xfsmdiag_main.h000066400000000000000000000041671442776067000162730ustar00rootroot00000000000000/*****************************************************************************/ /* * xfsmdiag_main.h -- kernel soundcard radio modem driver diagnostics utility. * * Copyright (C) 1996,1997,2000 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 14.12.1996 Started * 0.2 11.05.1997 introduced hdrvcomm.h * 0.3 05.01.2000 upgraded to fltk 1.0.7 */ /*****************************************************************************/ #ifndef _XFSMDIAG_MAIN_H #define _XFSMDIAG_MAIN_H /* ---------------------------------------------------------------------- */ #include #include #include /* ---------------------------------------------------------------------- */ class scope : public Fl_Box { Pixmap pixmap; bool pixmalloc; int X, Y, W, H; unsigned long col_zeroline; unsigned long col_background; unsigned long col_trace; int drawmode; protected: void draw(); void resize(int, int, int, int); public: scope(int, int, int, int, const char * = 0); ~scope(); void drawdata(short data[], int len, int xm); void mode(int dmode); int mode(void); void clear(void); }; /* ---------------------------------------------------------------------- */ #endif /* _XFSMDIAG_MAIN_H */ hdlcutil/fl/xfsmmixer.cxx000066400000000000000000000334561442776067000160650ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #include "xfsmmixer.h" Fl_Window *mixer_ad1848=(Fl_Window *)0; Fl_Value_Slider *ad1848_inl=(Fl_Value_Slider *)0; Fl_Value_Slider *ad1848_inr=(Fl_Value_Slider *)0; Fl_Value_Slider *ad1848_outl=(Fl_Value_Slider *)0; Fl_Value_Slider *ad1848_outr=(Fl_Value_Slider *)0; Fl_Button *ad1848_quit=(Fl_Button *)0; Fl_Group *ad1848_srcr=(Fl_Group *)0; Fl_Check_Button *ad1848_srcr_line=(Fl_Check_Button *)0; Fl_Check_Button *ad1848_srcr_aux1=(Fl_Check_Button *)0; Fl_Check_Button *ad1848_srcr_mic=(Fl_Check_Button *)0; Fl_Check_Button *ad1848_srcr_dac=(Fl_Check_Button *)0; Fl_Group *ad1848_srcl=(Fl_Group *)0; Fl_Check_Button *ad1848_srcl_line=(Fl_Check_Button *)0; Fl_Check_Button *ad1848_srcl_aux1=(Fl_Check_Button *)0; Fl_Check_Button *ad1848_srcl_mic=(Fl_Check_Button *)0; Fl_Check_Button *ad1848_srcl_dac=(Fl_Check_Button *)0; Fl_Window* create_form_ad1848() { Fl_Window* w; { Fl_Window* o = mixer_ad1848 = new Fl_Window(300, 330, "SoundModem Mixer"); w = o; o->box(FL_NO_BOX); { Fl_Box* o = new Fl_Box(0, 0, 300, 330); o->box(FL_UP_BOX); } { Fl_Box* o = new Fl_Box(10, 10, 280, 310); o->box(FL_DOWN_BOX); } { Fl_Value_Slider* o = ad1848_inl = new Fl_Value_Slider(20, 20, 30, 270, "Input\nleft"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ad1848); } { Fl_Value_Slider* o = ad1848_inr = new Fl_Value_Slider(60, 20, 30, 270, "Input\nright"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ad1848); } { Fl_Value_Slider* o = ad1848_outl = new Fl_Value_Slider(100, 20, 30, 270, "Output\nleft"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ad1848); } { Fl_Value_Slider* o = ad1848_outr = new Fl_Value_Slider(140, 20, 30, 270, "Output\nright"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ad1848); } { Fl_Button* o = ad1848_quit = new Fl_Button(180, 280, 100, 30, "Quit"); o->callback((Fl_Callback*)cb_quit, (void*)(0)); } { Fl_Group* o = ad1848_srcr = new Fl_Group(180, 150, 100, 120); o->align(FL_ALIGN_CENTER); { Fl_Box* o = new Fl_Box(180, 150, 100, 120); o->box(FL_DOWN_BOX); } { Fl_Check_Button* o = ad1848_srcr_line = new Fl_Check_Button(190, 180, 60, 20, "Line"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } { Fl_Check_Button* o = ad1848_srcr_aux1 = new Fl_Check_Button(190, 200, 60, 20, "Aux1"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } { Fl_Check_Button* o = ad1848_srcr_mic = new Fl_Check_Button(190, 220, 60, 20, "Mic"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } { Fl_Check_Button* o = ad1848_srcr_dac = new Fl_Check_Button(190, 240, 60, 20, "DAC"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } { Fl_Box* o = new Fl_Box(190, 160, 60, 20, "Right source"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } o->end(); } { Fl_Group* o = ad1848_srcl = new Fl_Group(180, 20, 100, 120); o->align(FL_ALIGN_CENTER); { Fl_Box* o = new Fl_Box(180, 20, 100, 120); o->box(FL_DOWN_BOX); } { Fl_Box* o = new Fl_Box(190, 30, 60, 20, "Left source"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Check_Button* o = ad1848_srcl_line = new Fl_Check_Button(190, 50, 60, 20, "Line"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } { Fl_Check_Button* o = ad1848_srcl_aux1 = new Fl_Check_Button(190, 70, 60, 20, "Aux1"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } { Fl_Check_Button* o = ad1848_srcl_mic = new Fl_Check_Button(190, 90, 60, 20, "Mic"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } { Fl_Check_Button* o = ad1848_srcl_dac = new Fl_Check_Button(190, 110, 60, 20, "DAC"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ad1848); } o->end(); } o->end(); } return w; } Fl_Window *mixer_ct1335=(Fl_Window *)0; Fl_Value_Slider *ct1335_out=(Fl_Value_Slider *)0; Fl_Button *ct1335_quit=(Fl_Button *)0; Fl_Window* create_form_ct1335() { Fl_Window* w; { Fl_Window* o = mixer_ct1335 = new Fl_Window(100, 360, "SoundModem Mixer"); w = o; o->box(FL_NO_BOX); { Fl_Box* o = new Fl_Box(0, 0, 100, 360); o->box(FL_UP_BOX); } { Fl_Box* o = new Fl_Box(10, 10, 80, 340); o->box(FL_DOWN_BOX); } { Fl_Value_Slider* o = ct1335_out = new Fl_Value_Slider(30, 20, 40, 270, "Output"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ct1335); } { Fl_Button* o = ct1335_quit = new Fl_Button(20, 310, 60, 30, "Quit"); o->callback((Fl_Callback*)cb_quit, (void*)(0)); } o->end(); } return w; } Fl_Window *mixer_ct1345=(Fl_Window *)0; Fl_Value_Slider *ct1345_outl=(Fl_Value_Slider *)0; Fl_Value_Slider *ct1345_outr=(Fl_Value_Slider *)0; Fl_Button *ct1345_quit=(Fl_Button *)0; Fl_Group *ct1345_src=(Fl_Group *)0; Fl_Check_Button *ct1345_src_mic=(Fl_Check_Button *)0; Fl_Check_Button *ct1345_src_cd=(Fl_Check_Button *)0; Fl_Check_Button *ct1345_src_line=(Fl_Check_Button *)0; Fl_Window* create_form_ct1345() { Fl_Window* w; { Fl_Window* o = mixer_ct1345 = new Fl_Window(190, 330, "SoundModem Mixer"); w = o; o->box(FL_NO_BOX); { Fl_Box* o = new Fl_Box(0, 0, 190, 330); o->box(FL_UP_BOX); } { Fl_Box* o = new Fl_Box(10, 10, 170, 310); o->box(FL_DOWN_BOX); } { Fl_Value_Slider* o = ct1345_outl = new Fl_Value_Slider(20, 20, 30, 270, "Output\nleft"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ct1345); } { Fl_Value_Slider* o = ct1345_outr = new Fl_Value_Slider(60, 20, 30, 270, "Output\nright"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ct1345); } { Fl_Button* o = ct1345_quit = new Fl_Button(100, 280, 70, 30, "Quit"); o->callback((Fl_Callback*)cb_quit, (void*)(0)); } { Fl_Group* o = ct1345_src = new Fl_Group(100, 20, 70, 100); o->align(FL_ALIGN_CENTER); { Fl_Box* o = new Fl_Box(100, 20, 70, 100); o->box(FL_DOWN_BOX); } { Fl_Check_Button* o = ct1345_src_mic = new Fl_Check_Button(110, 50, 50, 20, "Mic"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1345); } { Fl_Check_Button* o = ct1345_src_cd = new Fl_Check_Button(110, 70, 50, 20, "CD"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1345); } { Fl_Check_Button* o = ct1345_src_line = new Fl_Check_Button(110, 90, 50, 20, "Line"); o->type(102); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1345); } { Fl_Box* o = new Fl_Box(110, 30, 50, 20, "Source"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } o->end(); } o->end(); } return w; } Fl_Window *mixer_ct1745=(Fl_Window *)0; Fl_Value_Slider *ct1745_inl=(Fl_Value_Slider *)0; Fl_Value_Slider *ct1745_inr=(Fl_Value_Slider *)0; Fl_Value_Slider *ct1745_outl=(Fl_Value_Slider *)0; Fl_Value_Slider *ct1745_outr=(Fl_Value_Slider *)0; Fl_Button *ct1745_quit=(Fl_Button *)0; Fl_Value_Slider *ct1745_treble=(Fl_Value_Slider *)0; Fl_Value_Slider *ct1745_bass=(Fl_Value_Slider *)0; Fl_Group *ct1745_srcl=(Fl_Group *)0; Fl_Check_Button *ct1745_srcl_mic=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcl_cdl=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcl_cdr=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcl_linel=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcl_midil=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcl_midir=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcl_liner=(Fl_Check_Button *)0; Fl_Group *ct1745_srcr=(Fl_Group *)0; Fl_Check_Button *ct1745_srcr_mic=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcr_cdl=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcr_cdr=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcr_linel=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcr_midil=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcr_midir=(Fl_Check_Button *)0; Fl_Check_Button *ct1745_srcr_liner=(Fl_Check_Button *)0; Fl_Window* create_form_ct1745() { Fl_Window* w; { Fl_Window* o = mixer_ct1745 = new Fl_Window(430, 330, "SoundModem Mixer"); w = o; o->box(FL_NO_BOX); { Fl_Box* o = new Fl_Box(0, 0, 430, 330); o->box(FL_UP_BOX); } { Fl_Box* o = new Fl_Box(10, 10, 410, 310); o->box(FL_DOWN_BOX); } { Fl_Value_Slider* o = ct1745_inl = new Fl_Value_Slider(20, 20, 30, 270, "Input\nleft"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ct1745); } { Fl_Value_Slider* o = ct1745_inr = new Fl_Value_Slider(60, 20, 30, 270, "Input\nright"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ct1745); } { Fl_Value_Slider* o = ct1745_outl = new Fl_Value_Slider(100, 20, 30, 270, "Output\nleft"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ct1745); } { Fl_Value_Slider* o = ct1745_outr = new Fl_Value_Slider(140, 20, 30, 270, "Output\nright"); o->type(2); o->labelsize(8); o->callback((Fl_Callback*)update_ct1745); } { Fl_Button* o = ct1745_quit = new Fl_Button(180, 280, 230, 30, "Quit"); o->callback((Fl_Callback*)cb_quit, (void*)(0)); } { Fl_Value_Slider* o = ct1745_treble = new Fl_Value_Slider(180, 210, 230, 20, "Treble"); o->type(3); o->labelsize(8); o->callback((Fl_Callback*)update_ct1745); } { Fl_Value_Slider* o = ct1745_bass = new Fl_Value_Slider(180, 250, 230, 20, "Bass"); o->type(3); o->labelsize(8); o->callback((Fl_Callback*)update_ct1745); } { Fl_Group* o = ct1745_srcl = new Fl_Group(180, 20, 110, 180); o->align(FL_ALIGN_CENTER); { Fl_Box* o = new Fl_Box(180, 20, 110, 180); o->box(FL_DOWN_BOX); } { Fl_Box* o = new Fl_Box(191, 29, 89, 18, "Left source"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Check_Button* o = ct1745_srcl_mic = new Fl_Check_Button(190, 50, 90, 20, "Mic"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcl_cdl = new Fl_Check_Button(190, 70, 90, 20, "CD left"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcl_cdr = new Fl_Check_Button(190, 90, 90, 20, "CD right"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcl_linel = new Fl_Check_Button(190, 110, 90, 20, "Line left"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcl_midil = new Fl_Check_Button(190, 150, 90, 20, "MIDI left"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcl_midir = new Fl_Check_Button(190, 170, 90, 20, "MIDI right"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcl_liner = new Fl_Check_Button(190, 130, 90, 20, "Line right"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } o->end(); } { Fl_Group* o = ct1745_srcr = new Fl_Group(300, 20, 110, 180); o->align(FL_ALIGN_CENTER); { Fl_Box* o = new Fl_Box(300, 20, 110, 180); o->box(FL_DOWN_BOX); } { Fl_Box* o = new Fl_Box(311, 29, 89, 18, "Right source"); o->box(FL_FLAT_BOX); o->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } { Fl_Check_Button* o = ct1745_srcr_mic = new Fl_Check_Button(310, 50, 90, 20, "Mic"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcr_cdl = new Fl_Check_Button(310, 70, 90, 20, "CD left"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcr_cdr = new Fl_Check_Button(310, 90, 90, 20, "CD right"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcr_linel = new Fl_Check_Button(310, 110, 90, 20, "Line left"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcr_midil = new Fl_Check_Button(310, 150, 90, 20, "MIDI left"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcr_midir = new Fl_Check_Button(310, 170, 90, 20, "MIDI right"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } { Fl_Check_Button* o = ct1745_srcr_liner = new Fl_Check_Button(310, 130, 90, 20, "Line right"); o->down_box(FL_DIAMOND_DOWN_BOX); o->selection_color(3); o->callback((Fl_Callback*)update_ct1745); } o->end(); } o->end(); } return w; } hdlcutil/fl/xfsmmixer.h000066400000000000000000000055771442776067000155150ustar00rootroot00000000000000// generated by Fast Light User Interface Designer (fluid) version 1.00 #ifndef xfsmmixer_h #define xfsmmixer_h #include #include extern Fl_Window *mixer_ad1848; #include #include extern void update_ad1848(Fl_Widget*, void*); extern void update_ad1848(Fl_Value_Slider*, void*); extern Fl_Value_Slider *ad1848_inl; extern Fl_Value_Slider *ad1848_inr; extern Fl_Value_Slider *ad1848_outl; extern Fl_Value_Slider *ad1848_outr; #include extern void cb_quit(Fl_Button*, long); extern Fl_Button *ad1848_quit; #include extern Fl_Group *ad1848_srcr; #include extern void update_ad1848(Fl_Check_Button*, void*); extern Fl_Check_Button *ad1848_srcr_line; extern Fl_Check_Button *ad1848_srcr_aux1; extern Fl_Check_Button *ad1848_srcr_mic; extern Fl_Check_Button *ad1848_srcr_dac; extern Fl_Group *ad1848_srcl; extern Fl_Check_Button *ad1848_srcl_line; extern Fl_Check_Button *ad1848_srcl_aux1; extern Fl_Check_Button *ad1848_srcl_mic; extern Fl_Check_Button *ad1848_srcl_dac; Fl_Window* create_form_ad1848(); extern Fl_Window *mixer_ct1335; extern void update_ct1335(Fl_Widget*, void*); extern void update_ct1335(Fl_Value_Slider*, void*); extern Fl_Value_Slider *ct1335_out; extern Fl_Button *ct1335_quit; Fl_Window* create_form_ct1335(); extern Fl_Window *mixer_ct1345; extern void update_ct1345(Fl_Widget*, void*); extern void update_ct1345(Fl_Value_Slider*, void*); extern Fl_Value_Slider *ct1345_outl; extern Fl_Value_Slider *ct1345_outr; extern Fl_Button *ct1345_quit; extern Fl_Group *ct1345_src; extern void update_ct1345(Fl_Check_Button*, void*); extern Fl_Check_Button *ct1345_src_mic; extern Fl_Check_Button *ct1345_src_cd; extern Fl_Check_Button *ct1345_src_line; Fl_Window* create_form_ct1345(); extern Fl_Window *mixer_ct1745; extern void update_ct1745(Fl_Widget*, void*); extern void update_ct1745(Fl_Value_Slider*, void*); extern Fl_Value_Slider *ct1745_inl; extern Fl_Value_Slider *ct1745_inr; extern Fl_Value_Slider *ct1745_outl; extern Fl_Value_Slider *ct1745_outr; extern Fl_Button *ct1745_quit; extern Fl_Value_Slider *ct1745_treble; extern Fl_Value_Slider *ct1745_bass; extern Fl_Group *ct1745_srcl; extern void update_ct1745(Fl_Check_Button*, void*); extern Fl_Check_Button *ct1745_srcl_mic; extern Fl_Check_Button *ct1745_srcl_cdl; extern Fl_Check_Button *ct1745_srcl_cdr; extern Fl_Check_Button *ct1745_srcl_linel; extern Fl_Check_Button *ct1745_srcl_midil; extern Fl_Check_Button *ct1745_srcl_midir; extern Fl_Check_Button *ct1745_srcl_liner; extern Fl_Group *ct1745_srcr; extern Fl_Check_Button *ct1745_srcr_mic; extern Fl_Check_Button *ct1745_srcr_cdl; extern Fl_Check_Button *ct1745_srcr_cdr; extern Fl_Check_Button *ct1745_srcr_linel; extern Fl_Check_Button *ct1745_srcr_midil; extern Fl_Check_Button *ct1745_srcr_midir; extern Fl_Check_Button *ct1745_srcr_liner; Fl_Window* create_form_ct1745(); #endif hdlcutil/fl/xfsmmixer_main.cxx000066400000000000000000000316511442776067000170640ustar00rootroot00000000000000/*****************************************************************************/ /* * xfsmmixer_main.c -- kernel soundcard radio modem driver mixer utility. * * Copyright (C) 1996,1997,2000 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 14.12.1996 Started * 0.2 11.05.1997 introduced hdrvcomm.h * 0.3 05.01.2000 upgraded to fltk 1.0.7 */ /*****************************************************************************/ #include #include #include #include #include //#include #include "hdrvcomm.h" #include "xfsmmixer.h" #include #include /* ---------------------------------------------------------------------- */ static char *progname; static unsigned int mixdevice; /* ---------------------------------------------------------------------- */ static int do_mix_ioctl(int cmd, struct sm_mixer_data *mixdat) { struct sm_ioctl par; int i; par.cmd = cmd; par.data.mix = *mixdat; i = hdrvc_sm_ioctl(cmd, &par); *mixdat = par.data.mix; return i; } /* ---------------------------------------------------------------------- */ static unsigned char get_mixer_reg(unsigned char addr) { int i; struct sm_mixer_data mixdat; mixdat.reg = addr; if ((i = do_mix_ioctl(SMCTL_GETMIXER, &mixdat)) < 0) { perror("do_mix_ioctl: SMCTL_GETMIXER"); exit(1); } if (!i) fprintf(stderr, "warning: mixer device %u register %u not " "accessible for reading!\n", mixdat.mixer_type, addr); if (mixdat.mixer_type != mixdevice) { fprintf(stderr, "error: mixer type changed !?\n"); exit(2); } return mixdat.data; } /* ---------------------------------------------------------------------- */ static void set_mixer_reg(unsigned char addr, unsigned char data) { struct sm_mixer_data mixdat; mixdat.reg = addr; mixdat.data = data; mixdat.mixer_type = mixdevice; if (do_mix_ioctl(SMCTL_SETMIXER, &mixdat) < 0) { perror("do_mix_ioctl: SMCTL_SETMIXER"); exit(1); } } /* ---------------------------------------------------------------------- */ void update_ad1848(Fl_Widget *widget, void *udata) { unsigned char mdata; float mval; mdata = 0; if (ad1848_srcl_line->value()) mdata = 0x00; else if (ad1848_srcl_aux1->value()) mdata = 0x40; else if (ad1848_srcl_mic->value()) mdata = 0x80; else if (ad1848_srcl_dac->value()) mdata = 0xc0; mval = ad1848_inl->value(); if (mdata == 0x80 && mval >= 20) { mval -= 20; mdata |= 0x20; } mval *= 0.666666; if (mval > 15) mval = 15; mdata |= (int)mval; set_mixer_reg(0x00, mdata); mdata = 0; if (ad1848_srcr_line->value()) mdata = 0x00; else if (ad1848_srcr_aux1->value()) mdata = 0x40; else if (ad1848_srcr_mic->value()) mdata = 0x80; else if (ad1848_srcr_dac->value()) mdata = 0xc0; mval = ad1848_inr->value(); if (mdata == 0x80 && mval >= 20) { mval -= 20; mdata |= 0x20; } mval *= 0.666666; if (mval > 15) mval = 15; mdata |= (int)mval; set_mixer_reg(0x01, mdata); set_mixer_reg(0x02, 0x80); set_mixer_reg(0x03, 0x80); set_mixer_reg(0x04, 0x80); set_mixer_reg(0x05, 0x80); mval = ad1848_outl->value(); if (mval < -95) set_mixer_reg(0x06, 0x80); else set_mixer_reg(0x06, (unsigned char)(mval * (-0.66666666))); mval = ad1848_outr->value(); if (mval < -95) set_mixer_reg(0x07, 0x80); else set_mixer_reg(0x07, (unsigned char)(mval * (-0.66666666))); set_mixer_reg(0x0d, 0x00); } /* ---------------------------------------------------------------------- */ void update_ct1335(Fl_Widget *widget, void *udata) { float mval; mval = ct1335_out->value() * (7.0 / 46.0); set_mixer_reg(0x02, ((((int)(mval)) + 7) << 1)); set_mixer_reg(0x06, 0x00); set_mixer_reg(0x08, 0x00); set_mixer_reg(0x0a, 0x06); } /* ---------------------------------------------------------------------- */ void update_ct1345(Fl_Widget *widget, void *udata) { unsigned char mdata; double mval; set_mixer_reg(0x04, 0xee); set_mixer_reg(0x0a, 0x00); mdata = 0x20; if (ct1345_src_mic->value()) mdata = 0x20; else if (ct1345_src_cd->value()) mdata = 0x22; else if (ct1345_src_line->value()) mdata = 0x26; set_mixer_reg(0x0c, mdata); set_mixer_reg(0x0e, 0x20); mval = ct1345_outl->value() * (7.0 / 46.0); mdata = (((int)(mval))+7) << 5; mval = ct1345_outr->value() * (7.0 / 46.0); mdata |= (((int)(mval))+7) << 1; set_mixer_reg(0x22, mdata); set_mixer_reg(0x26, 0x00); set_mixer_reg(0x28, 0x00); set_mixer_reg(0x2e, 0x00); } /* ---------------------------------------------------------------------- */ void update_ct1745(Fl_Widget *widget, void *udata) { unsigned char mdata; float mval; set_mixer_reg(0x3c, 0); /* output src: only voice and pcspk */ mval = ct1745_treble->value(); set_mixer_reg(0x44, ((int)mval)<<4); /* treble.l */ set_mixer_reg(0x45, ((int)mval)<<4); /* treble.r */ mval = ct1745_bass->value(); set_mixer_reg(0x46, ((int)mval)<<4); /* bass.l */ set_mixer_reg(0x47, ((int)mval)<<4); /* bass.r */ set_mixer_reg(0x3b, 0); /* PC speaker vol -18dB */ set_mixer_reg(0x43, 1); /* mic: agc off, fixed 20dB gain */ mval = ct1745_outl->value(); mdata = 0; while (mval > 0) { mval -= 6; mdata += 0x40; } set_mixer_reg(0x41, mdata); /* output gain */ set_mixer_reg(0x30, 0xf8); /* master vol */ set_mixer_reg(0x32, ((int)(mval * 0.5) + 31) << 3); /* voice vol */ mval = ct1745_outr->value(); mdata = 0; while (mval > 0) { mval -= 6; mdata += 0x40; } set_mixer_reg(0x42, mdata); /* output gain */ set_mixer_reg(0x31, 0xf8); /* master vol */ set_mixer_reg(0x33, ((int)(mval * 0.5) + 31) << 3); /* voice vol */ mval = ct1745_inl->value(); mdata = 0; while (mval > 0) { mval -= 6; mdata += 0x40; } set_mixer_reg(0x3f, mdata); /* input gain */ mdata = ((int)(mval * 0.5) + 31) << 3; set_mixer_reg(0x34, mdata); /* midi vol */ set_mixer_reg(0x36, mdata); /* cd vol */ set_mixer_reg(0x38, mdata); /* line vol */ set_mixer_reg(0x3a, mdata); /* mic vol */ mval = ct1745_inr->value(); mdata = 0; while (mval > 0) { mval -= 6; mdata += 0x40; } set_mixer_reg(0x40, mdata); /* input gain */ mdata = ((int)(mval * 0.5) + 31) << 3; set_mixer_reg(0x35, mdata); /* midi vol */ set_mixer_reg(0x37, mdata); /* cd vol */ set_mixer_reg(0x39, mdata); /* line vol */ mdata = 0; if (ct1745_srcl_mic->value()) mdata |= 0x01; if (ct1745_srcl_cdl->value()) mdata |= 0x04; if (ct1745_srcl_cdr->value()) mdata |= 0x02; if (ct1745_srcl_linel->value()) mdata |= 0x10; if (ct1745_srcl_liner->value()) mdata |= 0x08; if (ct1745_srcl_midil->value()) mdata |= 0x40; if (ct1745_srcl_midir->value()) mdata |= 0x20; set_mixer_reg(0x3d, mdata); /* input sources left */ mdata = 0; if (ct1745_srcr_mic->value()) mdata |= 0x01; if (ct1745_srcr_cdl->value()) mdata |= 0x04; if (ct1745_srcr_cdr->value()) mdata |= 0x02; if (ct1745_srcr_linel->value()) mdata |= 0x10; if (ct1745_srcr_liner->value()) mdata |= 0x08; if (ct1745_srcr_midil->value()) mdata |= 0x40; if (ct1745_srcr_midir->value()) mdata |= 0x20; set_mixer_reg(0x3e, mdata); /* input sources right*/ } /* ---------------------------------------------------------------------- */ void cb_quit(Fl_Button *, long) { exit (0); } /* ---------------------------------------------------------------------- */ static const char *usage_str = "[-i smif]\n" " -i: specify the name of the soundmodem kernel driver interface\n\n"; /* ---------------------------------------------------------------------- */ int main(int argc, char *argv[]) { int c, i; struct sm_mixer_data mixdat; unsigned char mdata; progname = *argv; printf("%s: Version " FULL_VER "; (C) 1996,1997,2000 by Thomas Sailer HB9JNX/AE4WA\n", progname); hdrvc_args(&argc, argv, "sm0"); for (i = 1; i < argc; ) { c = i; Fl::arg(argc, argv, c); if (c <= 0) { i++; continue; } memmove(&argv[i], &argv[i+c], (argc-i) * sizeof(char *)); argc -= c; } #if 0 while ((ret = getopt(argc, argv, "")) != -1) { switch (ret) { default: printf("usage: %s %s", progname, usage_str); exit(-1); } } #endif hdrvc_init(); /* * set mixer */ mixdat.reg = 0; if (do_mix_ioctl(SMCTL_GETMIXER, &mixdat) < 0) { perror("do_mix_ioctl: SMCTL_GETMIXER"); mixdevice = SM_MIXER_INVALID; } else mixdevice = mixdat.mixer_type; switch (mixdevice) { case SM_MIXER_INVALID: printf("invalid mixer device\n"); exit(1); case SM_MIXER_AD1848: case SM_MIXER_CRYSTAL: printf("Mixer device: %s\n", mixdevice == SM_MIXER_CRYSTAL ? "CS423x" : "AD1848"); create_form_ad1848(); mdata = get_mixer_reg(0); ad1848_inl->step(1.5); ad1848_inl->bounds(42.5, 0); ad1848_inl->value((((mdata & 0xe0) == 0xa0) ? 20 : 0) + (mdata & 0xf) * 1.5); ad1848_srcl_line->value((mdata & 0xc0) == 0x00); ad1848_srcl_aux1->value((mdata & 0xc0) == 0x40); ad1848_srcl_mic->value((mdata & 0xc0) == 0x80); ad1848_srcl_dac->value((mdata & 0xc0) == 0xc0); mdata = get_mixer_reg(1); ad1848_inr->step(1.5); ad1848_inr->bounds(42.5, 0); ad1848_inr->value((((mdata & 0xe0) == 0xa0) ? 20 : 0) + (mdata & 0xf) * 1.5); ad1848_srcr_line->value((mdata & 0xc0) == 0x00); ad1848_srcr_aux1->value((mdata & 0xc0) == 0x40); ad1848_srcr_mic->value((mdata & 0xc0) == 0x80); ad1848_srcr_dac->value((mdata & 0xc0) == 0xc0); mdata = get_mixer_reg(6); ad1848_outl->step(1.5); ad1848_outl->bounds(0, -100); ad1848_outl->value((mdata & 0x80) ? -100 : (mdata & 0x3f) * -1.5); mdata = get_mixer_reg(7); ad1848_outr->step(1.5); ad1848_outr->bounds(0, -100); ad1848_outr->value((mdata & 0x80) ? -100 : (mdata & 0x3f) * -1.5); mixer_ad1848->show(); break; case SM_MIXER_CT1335: printf("Mixer device: CT1335\n"); create_form_ct1335(); mdata = get_mixer_reg(0x2); ct1335_out->step(46.0/7.0); ct1335_out->bounds(0, -46); ct1335_out->value((((int)((mdata >> 1) & 7)) - 7) * (46.0/7.0)); mixer_ct1335->show(); break; case SM_MIXER_CT1345: printf("Mixer device: CT1345\n"); create_form_ct1345(); mdata = get_mixer_reg(0x22); ct1345_outl->step(46.0/7.0); ct1345_outl->bounds(0, -46); ct1345_outl->value((((int)((mdata >> 5) & 7)) - 7) * (46.0/7.0)); ct1345_outr->step(46.0/7.0); ct1345_outr->bounds(0, -46); ct1345_outr->value((((int)((mdata >> 1) & 7)) - 7) * (46.0/7.0)); mdata = get_mixer_reg(0xc); ct1345_src_mic->value((mdata & 6) == 0 || (mdata & 6) == 4); ct1345_src_cd->value((mdata & 6) == 2); ct1345_src_line->value((mdata & 6) == 6); mixer_ct1345->show(); break; case SM_MIXER_CT1745: printf("Mixer device: CT1745\n"); create_form_ct1745(); ct1745_outl->step(2); ct1745_outl->bounds(0, -62); ct1745_outl->value(((int)((get_mixer_reg(0x32) >> 3) & 0x1f) - 31) * 2 + ((get_mixer_reg(0x41) >> 6) & 0x3) * 6); ct1745_outr->step(2); ct1745_outr->bounds(0, -62); ct1745_outr->value(((int)((get_mixer_reg(0x33) >> 3) & 0x1f) - 31) * 2 + ((get_mixer_reg(0x42) >> 6) & 0x3) * 6); ct1745_inl->step(2); ct1745_inl->bounds(0, -62); ct1745_inl->value(((int)((get_mixer_reg(0x38) >> 3) & 0x1f) - 31) * 2 + ((get_mixer_reg(0x3f) >> 6) & 0x3) * 6); ct1745_inr->step(2); ct1745_inr->bounds(0, -62); ct1745_inr->value(((int)((get_mixer_reg(0x39) >> 3) & 0x1f) - 31) * 2 + ((get_mixer_reg(0x40) >> 6) & 0x3) * 6); mdata = get_mixer_reg(0x3d); ct1745_srcl_mic->value(!!(mdata & 1)); ct1745_srcl_cdl->value(!!(mdata & 4)); ct1745_srcl_cdr->value(!!(mdata & 2)); ct1745_srcl_linel->value(!!(mdata & 0x10)); ct1745_srcl_liner->value(!!(mdata & 8)); ct1745_srcl_midil->value(!!(mdata & 0x40)); ct1745_srcl_midir->value(!!(mdata & 0x20)); mdata = get_mixer_reg(0x3e); ct1745_srcr_mic->value(!!(mdata & 1)); ct1745_srcr_cdl->value(!!(mdata & 4)); ct1745_srcr_cdr->value(!!(mdata & 2)); ct1745_srcr_linel->value(!!(mdata & 0x10)); ct1745_srcr_liner->value(!!(mdata & 8)); ct1745_srcr_midil->value(!!(mdata & 0x40)); ct1745_srcr_midir->value(!!(mdata & 0x20)); ct1745_treble->step(1); ct1745_treble->bounds(7, -8); ct1745_treble->value((get_mixer_reg(0x44) >> 4) & 0xf); ct1745_bass->step(1); ct1745_bass->bounds(7, -8); ct1745_bass->value((get_mixer_reg(0x46) >> 4) & 0xf); mixer_ct1745->show(); break; default: printf("unknown mixer device %d\n", mixdevice); exit(1); } Fl::run(); exit(0); } hdlcutil/hdlcdrv.9000066400000000000000000000175061442776067000144340ustar00rootroot00000000000000.\" Copyright 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) .\" May be distributed under the GNU General Public License .\" " .TH HDLCDRV 9 "27 April 2008" "Linux 2.1.x" "Kernel Reference Guide" .SH NAME hdlcdrv \- HDLC amateur (AX.25) packet radio network driver .SH SYNOPSIS .B #include .B linux/drivers/net/hdlcdrv.c .BI "extern inline void hdlcdrv_putbits(struct hdlcdrv_state * " s ", unsigned int " bits ");" .BI "extern inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state * " s ");" .BI "extern inline void hdlcdrv_channelbit(struct hdlcdrv_state * " s ", unsigned int " bit ");" .BI "extern inline void hdlcdrv_setdcd(struct hdlcdrv_state * " s " , int " dcd ");" .BI "extern inline int hdlcdrv_ptt(struct hdlcdrv_state * " s ");" .BI "void hdlcdrv_receiver(struct device *, struct hdlcdrv_state *);" .BI "void hdlcdrv_transmitter(struct device *, struct hdlcdrv_state *);" .BI "void hdlcdrv_arbitrate(struct device *, struct hdlcdrv_state *);" .BI "int hdlcdrv_register_hdlcdrv(struct device * " dev ", struct hdlcdrv_ops * " ops ", unsigned int " privsize ", char * " ifname ", unsigned int " baseaddr " , unsigned int " irq ", unsigned int " dma "); .BI "int hdlcdrv_unregister_hdlcdrv(struct device * " dev ");" .SH DESCRIPTION This driver should ease the implementation of simple AX.25 packet radio modems where the software is responsible for the HDLC encoding and decoding. Examples of such modems include the \fIbaycom\fP family and the \fIsoundcard\fP modems. This driver provides a standard Linux network driver interface. It can even be compiled if Kernel AX.25 is not enabled in the Linux configuration. This allows this driver to be used even for userland AX.25 stacks such as \fIWampes\fP or \fITNOS\fP, with the help of the \fInet2kiss\fP utility. This driver does not access any hardware; it is the responsibility of an additional hardware driver such as \fIbaycom\fP or \fIsoundmodem\fP to access the hardware and derive the bitstream to feed into this driver. The hardware driver should store its state in a structure of the following form: .nf struct hwdrv_state { struct hdlc_state \fIhdrv\fP; ... the drivers private state }; .fi A pointer to this structure will be stored in \fIdev->priv\fP. \fBhdlcdrv_register_hdlcdrv\fP registers a hardware driver to the hdlc driver. \fIdev\fP points to storage for the \fIdevice\fP structure, which must be provided by the hardware driver, but gets initialized by this function call. \fIops\fP provides information about the hardware driver and its calls. \fIprivsize\fP should be \fIsizeof(struct\ hwdrv_state)\fP. \fIifname\fP specifies the name the interface should get. \fIbaseaddr\fP, \fIirq\fP and \fIdma\fP are simply stored in the \fIdevice\fP structure. After this function succeeds, the interface is registered with the kernel. It is not running, however, this must be done with \fBifconfig\ \fP\fIifname\fP\fB\ up\fP. \fBhdlcdrv_unregister_hdlcdrv\fP shuts the interface down and unregisters it with the kernel. \fBhdlcdrv_putbits\fP delivers 16 received bits for processing to the HDLC driver. This routine merely stores them in a buffer and does not process them. It is thus fast and can be called with interrupts off. The least significant bit should be the first one received. \fBhdlcdrv_getbits\fP requests 16 bits from the driver for transmission. The least significant bit should be transmitted first. This routine takes them from a buffer and is therefore fast. It can be called with interrupts off. \fBhdlcdrv_channelbit\fP puts a single bit into a buffer, which can be displayed with \fBsethdlc\ \-s\fP. It is intended for driver debugging purposes. \fBhdlcdrv_setdcd\fP informs the HDLC driver about the channel state (i.e. if the hardware driver detected a data carrier). This information is used in the channel access algorithm, i.e. it prevents the driver from transmitting on a half duplex channel if there is already a transmitter on air. \fBhdlcdrv_ptt\fP should be called by the hardware driver to determine if it should start or stop transmitting. The hardware driver does not need to worry about keyup delays. This is done by the HDLC driver. \fBhdlcdrv_receiver\fP actually processes the received bits delivered by \fBhdlcdrv_putbits\fP. It should be called with interrupts on. It guards itself against reentrance problems. \fBhdlcdrv_transmitter\fP actually prepares the bits to be transmitted. It should be called with interrupts on. It guards itself against reentrance problems. \fBhdlcdrv_arbitrate\fP does the channel access algorithm (p-persistent CSMA). It should be called once every 10ms. Note that the hardware driver \fBmust\fP set the \fIhdrv.par.bitrate\fP field prior to starting operation so that \fBhdlcdrv\fP can calculate the transmitter keyup delay correctly. .SH "HARDWARE DRIVER ENTRY POINTS" The hardware driver should provide the following information to the HDLC driver: .nf struct hdlcdrv_ops { const char *\fIdrvname\fP; const char *\fIdrvinfo\fP; int (*\fIopen\fP)(struct device *); int (*\fIclose\fP)(struct device *); int (*\fIioctl\fP)(struct device *, struct ifreq *, int); }; .fi \fBdrvname\fP and \fBdrvinfo\fP are just for informational purposes. The following routines receive a pointer to the \fIdevice\fP structure, where they may find the io address, irq and dma channels. \fBopen\fP must be provided. It is called during \fBifconfig\ \fP\fIifname\fP\fB\ up\fP and should check for the hardware, grab it and initialize it. It usually installs an interrupt handler which then gets invoked by the hardware. \fBclose\fP must be provided. It is called during \fBifconfig\ \fP\fIifname\fP\fB\ down\fP and should undo all actions done by \fBopen\fP, i.e. release io regions and irqs. \fBioctl\fP may be provided to implement device specific ioctl's. .SH "IOCTL CALLS" The driver only responds to \fISIOCDEVPRIVATE\fP. Parameters are passed from and to the driver using the following struct: .nf struct hdlcdrv_ioctl { int cmd; union { struct hdlcdrv_params mp; struct hdlcdrv_channel_params cp; struct hdlcdrv_channel_state cs; unsigned int calibrate; unsigned char bits; } data; }; .fi Since the 16 private \fIioctl\ request\fP numbers for network drivers were not enough, the driver implements its own \fIsub\ request\fP number with \fIcmd\fP. The following numbers are implemented: .TP .B HDLCDRVCTL_GETMODEMPAR returns the IO parameters of the modem in \fIdata.mp\fP. This includes the io address, irq, eventually dma, and ports to output a PTT signal. .TP .B HDLCDRVCTL_SETMODEMPAR sets the modem parameters. Only superuser can do this. Parameters can only be changed if the interface is not running (i.e. down). .TP .B HDLCDRVCTL_GETCHANNELPAR returns the channel access parameters. .TP .B HDLCDRVCTL_SETCHANNELPAR sets the channel access parameters. Only superuser can do this. They may also be changed using the \fBkissparms\fP command if using kernel AX.25 or the \fBparam\fP command of \fB*NOS\fP. .TP .B HDLCDRVCTL_GETSTAT statistics and status information, such as if a carrier is detected on the channel and if the interface is currently transmitting. .TP .B HDLCDRVCTL_CALIBRATE instructs the driver to transmit a calibration pattern for the specified number of seconds. .TP .B HDLCDRVCTL_GETSAMPLES returns the bits delivered by the hardware driver with \fIhdlcdrv_channelbit\fP. The bits are returned 8 at a time with the least significant bit the first one. This command may not be available, depending on debugging settings. .TP .B HDLCDRVCTL_GETBITS returns the bits delivered by the hardware driver to the HDLC decoder. The bits are returned 8 at a time with the least significant bit the first one. This command may not be available, depending on debugging settings. .SH "SEE ALSO" .BR baycom "\ (9), " soundmodem "\ (9), " sethdlc "\ (8), " linux/drivers/net/hdlcdrv.c, .SH AUTHOR hdlcdrv was written by Thomas Sailer, HB9JNX/AE4WA, (t.sailer@alumni.ethz.ch). hdlcutil/hdrvcomm.c000066400000000000000000000404551442776067000146760ustar00rootroot00000000000000/*****************************************************************************/ /* * hdrvcomm.c -- HDLC driver communications. * * Copyright (C) 1996-1998 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 10.05.97 Started * 0.2 14.04.98 Tried to implement AF_PACKET */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hdrvcomm.h" #include "usersmdiag.h" #include #include #include #ifndef SOL_PACKET #define SOL_PACKET 263 #endif /* ---------------------------------------------------------------------- */ static const char *if_name = "bcsf0"; static char *prg_name; static int fd = -1; static struct ifreq ifr_h; static int promisc; static int afpacket = 1; static int msqid = -1; /* ---------------------------------------------------------------------- */ static void terminate(void) { if (ioctl(fd, SIOCSIFFLAGS, &ifr_h) < 0) { perror("ioctl (SIOCSIFFLAGS)"); exit(1); } exit(0); } /* ---------------------------------------------------------------------- */ static void terminate_sig(int signal) { printf("signal %i caught\n", signal); terminate(); } /* ---------------------------------------------------------------------- */ int hdrvc_recvpacket(char *pkt, int maxlen) { struct ifreq ifr_new; struct sockaddr_ll from; socklen_t from_len = sizeof(from); if (!promisc) { if (afpacket) { struct sockaddr_ll sll; struct packet_mreq mr; memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifr_h.ifr_ifindex; sll.sll_protocol = htons(ETH_P_AX25); if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)) < 0) { fprintf(stderr, "%s: Error %s (%i) bind failed\n", prg_name, strerror(errno), errno); exit(-2); } memset(&mr, 0, sizeof(mr)); mr.mr_ifindex = sll.sll_ifindex; mr.mr_type = PACKET_MR_PROMISC; if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (char *)&mr, sizeof(mr)) < 0) { fprintf(stderr, "%s: Error %s (%i) setsockopt SOL_PACKET, PACKET_ADD_MEMBERSHIP failed\n", prg_name, strerror(errno), errno); exit(-2); } } else { struct sockaddr sa; strcpy(sa.sa_data, if_name); sa.sa_family = AF_INET; if (bind(fd, &sa, sizeof(sa)) < 0) { fprintf(stderr, "%s: Error %s (%i) bind failed\n", prg_name, strerror(errno), errno); exit(-2); } ifr_new = ifr_h; ifr_new.ifr_flags |= IFF_PROMISC; if (ioctl(fd, SIOCSIFFLAGS, &ifr_new) < 0) { perror("ioctl (SIOCSIFFLAGS)"); exit(1); } signal(SIGTERM, terminate_sig); signal(SIGQUIT, terminate_sig); if (atexit((void (*)(void))terminate)) { perror("atexit"); terminate(); } } promisc = 1; fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); } if (!pkt || maxlen < 2) return 0; return recvfrom(fd, pkt, maxlen, 0, (struct sockaddr *)&from, &from_len); return -1; } /* ---------------------------------------------------------------------- */ int hdrvc_getfd(void) { return fd; } /* ---------------------------------------------------------------------- */ const char *hdrvc_ifname(void) { return if_name; } /* ---------------------------------------------------------------------- */ void hdrvc_args(int *argc, char *argv[], const char *def_if) { int ac, i; if (def_if) if_name = def_if; if (!argc || !argv || (ac = *argc) < 1) return; prg_name = argv[0]; for (i = 1; i < ac-1; i++) { if (!strcmp(argv[i], "-i")) { if_name = argv[i+1]; ac -= 2; if (i < ac) memmove(argv+i, argv+i+2, (ac-i) * sizeof(void *)); i--; } } *argc = ac; } /* ---------------------------------------------------------------------- */ void hdrvc_init(void) { key_t k; static struct ifreq ifr; strcpy(ifr_h.ifr_name, if_name); /* first try to use AF_PACKET */ if ((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_AX25))) < 0 || ioctl(fd, SIOCGIFINDEX, &ifr_h) < 0) { if (fd >= 0) close(fd); afpacket = 0; fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_AX25)); if (fd < 0) { fprintf(stderr, "%s: Error %s (%i), cannot open %s\n", prg_name, strerror(errno), errno, if_name); exit(-1); } if (ioctl(fd, SIOCGIFFLAGS, &ifr_h) < 0) { fprintf(stderr, "%s: Error %s (%i), cannot ioctl SIOCGIFFLAGS %s\n", prg_name, strerror(errno), errno, if_name); exit(-1); } } strcpy(ifr.ifr_name, if_name); if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0 ) { fprintf(stderr, "%s: Error %s (%i), cannot ioctl SIOCGIFHWADDR %s\n", prg_name, strerror(errno), errno, if_name); exit(-1); } if (ifr.ifr_hwaddr.sa_family != ARPHRD_AX25) { fprintf(stderr, "%s: Error, interface %s not AX25 (%i)\n", prg_name, if_name, ifr.ifr_hwaddr.sa_family); exit(-1); } return; k = ftok(if_name, USERSM_KEY_PROJ); if (k == (key_t)-1) { fprintf(stderr, "%s: Error %s (%i), cannot ftok on %s\n", prg_name, strerror(errno), errno, if_name); exit(-1); } msqid = msgget(k, 0700); if (msqid < 0) { fprintf(stderr, "%s: Error %s (%i), cannot msgget %d\n", prg_name, strerror(errno), errno, k); exit(-1); } } /* ---------------------------------------------------------------------- */ static void hdrvc_sendmsg(struct usersmmsg *msg, int len) { if (msgsnd(msqid, (struct msgbuf *)msg, len+sizeof(msg->hdr)-sizeof(long), 0) < 0) { perror("msgsnd"); exit(1); } } static int hdrvc_recvmsg(struct usersmmsg *msg, int maxlen, long type) { int len; len = msgrcv(msqid, (struct msgbuf *)msg, maxlen - sizeof(long), type, MSG_NOERROR); if (len < 0) { perror("msgrcv"); exit(1); } #if 0 for (;;) { if ((len = msgrcv(msqid, (struct msgbuf *)msg, maxlen-sizeof(long), type, IPC_NOWAIT|MSG_NOERROR)) >= 0) return len+sizeof(long); if (errno != ENOMSG) { perror("msgrcv"); exit(1); } usleep(250000); } #endif return len+sizeof(long); } /* ---------------------------------------------------------------------- */ int hdrvc_hdlcdrv_ioctl(int cmd, struct hdlcdrv_ioctl *par) { struct ifreq ifr = ifr_h; errno = EINVAL; return -1; ifr.ifr_data = (caddr_t)par; par->cmd = cmd; return ioctl(fd, SIOCDEVPRIVATE, &ifr); } /* ---------------------------------------------------------------------- */ int hdrvc_sm_ioctl(int cmd, struct sm_ioctl *par) { struct ifreq ifr = ifr_h; errno = EINVAL; return -1; ifr.ifr_data = (caddr_t)par; par->cmd = cmd; return ioctl(fd, SIOCDEVPRIVATE, &ifr); } /* ---------------------------------------------------------------------- */ int hdrvc_baycom_ioctl(int cmd, struct baycom_ioctl *par) { struct ifreq ifr = ifr_h; errno = EINVAL; return -1; ifr.ifr_data = (caddr_t)par; par->cmd = cmd; return ioctl(fd, SIOCDEVPRIVATE, &ifr); } /* ---------------------------------------------------------------------- */ unsigned int hdrvc_get_ifflags(void) { struct ifreq ifr; memcpy(&ifr, &ifr_h, sizeof(ifr)); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { perror("ioctl: SIOCGIFFLAGS"); exit(-1); } return ifr.ifr_flags; return IFF_UP | IFF_RUNNING; } /* ---------------------------------------------------------------------- */ int hdrvc_diag(struct sm_diag_data *diag) { struct sm_ioctl smi; int ret; struct usersmmsg msg; if (!diag) { errno = EINVAL; return -1; } memcpy(&smi.data.diag, diag, sizeof(smi.data.diag)); ret = hdrvc_sm_ioctl(SMCTL_DIAGNOSE, &smi); memcpy(diag, &smi.data.diag, sizeof(smi.data.diag)); return ret; msg.hdr.type = USERSM_CMD_REQ_DIAG; msg.hdr.channel = 0; msg.data.diag.mode = diag->mode; msg.data.diag.flags = diag->flags; msg.data.diag.samplesperbit = diag->samplesperbit; msg.data.diag.datalen = diag->datalen; hdrvc_sendmsg(&msg, sizeof(msg.data.diag)); ret = hdrvc_recvmsg(&msg, sizeof(msg), USERSM_CMD_ACK_DIAG); if (ret < 0) return ret; if (ret < sizeof(msg.data.diag) || ret < sizeof(msg.data.diag)+msg.data.diag.datalen*sizeof(short)) { errno = EIO; return -1; } diag->mode = msg.data.diag.mode; diag->flags = msg.data.diag.flags; diag->samplesperbit = msg.data.diag.samplesperbit; diag->datalen = msg.data.diag.datalen; memcpy(diag->data, msg.data.diag_out.samples, msg.data.diag.datalen*sizeof(short)); return 0; } /* ---------------------------------------------------------------------- */ int hdrvc_get_samples(void) { int ret; struct hdlcdrv_ioctl bi; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_GETSAMPLES, &bi); if (ret < 0) return ret; return bi.data.bits & 0xff; errno = EAGAIN; return -1; } /* ---------------------------------------------------------------------- */ int hdrvc_get_bits(void) { int ret; struct hdlcdrv_ioctl bi; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_GETBITS, &bi); if (ret < 0) return ret; return bi.data.bits & 0xff; errno = EAGAIN; return -1; } /* ---------------------------------------------------------------------- */ int hdrvc_get_channel_access_param(struct hdrvc_channel_params *par) { struct hdlcdrv_ioctl hi; int ret; struct usersmmsg msg; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_GETCHANNELPAR, &hi); if (ret < 0) return ret; if (!par) { errno = EINVAL; return -1; } par->tx_delay = hi.data.cp.tx_delay; par->tx_tail = hi.data.cp.tx_tail; par->slottime = hi.data.cp.slottime; par->ppersist = hi.data.cp.ppersist; par->fulldup = hi.data.cp.fulldup; return 0; msg.hdr.type = USERSM_CMD_REQ_CHACCESS_PAR; msg.hdr.channel = 0; hdrvc_sendmsg(&msg, 0); ret = hdrvc_recvmsg(&msg, sizeof(msg), USERSM_CMD_ACK_CHACCESS_PAR); if (ret < 0) return ret; if (ret < sizeof(msg.data.cp)) { errno = EIO; return -1; } par->tx_delay = msg.data.cp.tx_delay; par->tx_tail = msg.data.cp.tx_tail; par->slottime = msg.data.cp.slottime; par->ppersist = msg.data.cp.ppersist; par->fulldup = msg.data.cp.fulldup; return 0; } /* ---------------------------------------------------------------------- */ int hdrvc_set_channel_access_param(struct hdrvc_channel_params par) { int ret; struct usersmmsg msg; struct hdlcdrv_ioctl hi; hi.data.cp.tx_delay = par.tx_delay; hi.data.cp.tx_tail = par.tx_tail; hi.data.cp.slottime = par.slottime; hi.data.cp.ppersist = par.ppersist; hi.data.cp.fulldup = par.fulldup; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_SETCHANNELPAR, &hi); if (ret < 0) return ret; return 0; msg.hdr.type = USERSM_CMD_SET_CHACCESS_PAR; msg.hdr.channel = 0; msg.data.cp.tx_delay = par.tx_delay; msg.data.cp.tx_tail = par.tx_tail; msg.data.cp.slottime = par.slottime; msg.data.cp.ppersist = par.ppersist; msg.data.cp.fulldup = par.fulldup; hdrvc_sendmsg(&msg, sizeof(msg.data.cp)); return 0; } /* ---------------------------------------------------------------------- */ int hdrvc_calibrate(int calib) { struct hdlcdrv_ioctl bhi; struct usersmmsg msg; bhi.data.calibrate = calib; return hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_CALIBRATE, &bhi); msg.hdr.type = USERSM_CMD_CALIBRATE; msg.hdr.channel = 0; msg.data.calib = calib; hdrvc_sendmsg(&msg, sizeof(msg.data.calib)); return 0; } /* ---------------------------------------------------------------------- */ int hdrvc_get_channel_state(struct hdrvc_channel_state *st) { struct hdlcdrv_ioctl bhi; int ret; struct usersmmsg msg; if (!st) { errno = EINVAL; return -1; } ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_GETSTAT, &bhi); if (ret >= 0) { st->ptt = bhi.data.cs.ptt; st->dcd = bhi.data.cs.dcd; st->ptt_keyed = bhi.data.cs.ptt_keyed; st->tx_packets = bhi.data.cs.tx_packets; st->tx_errors = bhi.data.cs.tx_errors; st->rx_packets = bhi.data.cs.rx_packets; st->rx_errors = bhi.data.cs.rx_errors; } return ret; msg.hdr.type = USERSM_CMD_REQ_CHANNELSTATE; msg.hdr.channel = 0; hdrvc_sendmsg(&msg, 0); ret = hdrvc_recvmsg(&msg, sizeof(msg), USERSM_CMD_ACK_CHANNELSTATE); if (ret < 0) return ret; if (ret < sizeof(msg.data.cs)) { errno = EIO; return -1; } st->ptt = msg.data.cs.ptt; st->dcd = msg.data.cs.dcd; st->ptt_keyed = msg.data.cs.ptt_keyed; st->tx_packets = msg.data.cs.tx_packets; st->tx_errors = msg.data.cs.tx_errors; st->rx_packets = msg.data.cs.rx_packets; st->rx_errors = msg.data.cs.rx_errors; return 0; } /* ---------------------------------------------------------------------- */ int hdrvc_diag2(unsigned int mode, unsigned int flags, short *data, unsigned int maxdatalen, unsigned int *samplesperbit) { int ret; struct usersmmsg msg; static unsigned int modeconvusersm[4] = { USERSM_DIAGMODE_OFF, USERSM_DIAGMODE_INPUT, USERSM_DIAGMODE_DEMOD, USERSM_DIAGMODE_CONSTELLATION }; if (mode > HDRVC_DIAGMODE_CONSTELLATION) { errno = EINVAL; return -1; } struct sm_ioctl smi; static unsigned int modeconvsm[4] = { SM_DIAGMODE_OFF, SM_DIAGMODE_INPUT, SM_DIAGMODE_DEMOD, SM_DIAGMODE_CONSTELLATION }; smi.data.diag.mode = modeconvsm[mode]; smi.data.diag.flags = (flags & HDRVC_DIAGFLAG_DCDGATE) ? SM_DIAGFLAG_DCDGATE : 0; smi.data.diag.samplesperbit = 0; smi.data.diag.datalen = maxdatalen; smi.data.diag.data = data; ret = hdrvc_sm_ioctl(SMCTL_DIAGNOSE, &smi); if (ret < 0) return ret; if (samplesperbit) *samplesperbit = smi.data.diag.samplesperbit; if (smi.data.diag.mode != modeconvsm[mode] || !(smi.data.diag.flags & SM_DIAGFLAG_VALID)) return 0; return smi.data.diag.datalen; msg.hdr.type = USERSM_CMD_REQ_DIAG; msg.hdr.channel = 0; msg.data.diag.mode = modeconvusersm[mode]; msg.data.diag.flags = (flags & HDRVC_DIAGFLAG_DCDGATE) ? USERSM_DIAGFLAG_DCDGATE : 0; msg.data.diag.samplesperbit = 0; msg.data.diag.datalen = maxdatalen; hdrvc_sendmsg(&msg, sizeof(msg.data.diag)); ret = hdrvc_recvmsg(&msg, sizeof(msg), USERSM_CMD_ACK_DIAG); if (ret < 0) return ret; if (ret < sizeof(msg.data.diag) || ret < sizeof(msg.data.diag)+msg.data.diag.datalen*sizeof(short)) { errno = EIO; return -1; } if (samplesperbit) *samplesperbit = msg.data.diag.samplesperbit; if (msg.data.diag.mode != modeconvusersm[mode] || !(msg.data.diag.flags & USERSM_DIAGFLAG_VALID)) return 0; if (!data || msg.data.diag.datalen <= 0) return 0; memcpy(data, msg.data.diag_out.samples, msg.data.diag.datalen*sizeof(short)); return msg.data.diag.datalen; } /* ---------------------------------------------------------------------- */ int hdrvc_get_driver_name(char *buf, int bufsz) { int ret; struct usersmmsg msg; struct hdlcdrv_ioctl bhi; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_DRIVERNAME, &bhi); if (ret < 0) return ret; strncpy(buf, bhi.data.modename, bufsz); return 0; msg.hdr.type = USERSM_CMD_REQ_DRVNAME; msg.hdr.channel = 0; hdrvc_sendmsg(&msg, 0); ret = hdrvc_recvmsg(&msg, sizeof(msg), USERSM_CMD_ACK_DRVNAME); if (ret < 0) return ret; if (ret < 1) { errno = EIO; return -1; } if (bufsz < ret) ret = bufsz; strncpy(buf, msg.data.by, ret); buf[ret-1] = 0; return 0; } /* ---------------------------------------------------------------------- */ int hdrvc_get_mode_name(char *buf, int bufsz) { int ret; struct usersmmsg msg; struct hdlcdrv_ioctl bhi; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_GETMODE, &bhi); if (ret < 0) return ret; strncpy(buf, bhi.data.modename, bufsz); return 0; msg.hdr.type = USERSM_CMD_REQ_DRVMODE; msg.hdr.channel = 0; hdrvc_sendmsg(&msg, 0); ret = hdrvc_recvmsg(&msg, sizeof(msg), USERSM_CMD_ACK_DRVMODE); if (ret < 0) return ret; if (ret < 1) { errno = EIO; return -1; } if (bufsz < ret) ret = bufsz; strncpy(buf, msg.data.by, ret); buf[ret-1] = 0; return 0; } /* ---------------------------------------------------------------------- */ hdlcutil/hdrvcomm.h000066400000000000000000000072071442776067000147010ustar00rootroot00000000000000/*****************************************************************************/ /* * hdrvcomm.h -- HDLC driver communications. * * 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. * */ /*****************************************************************************/ #ifndef _HDRVCOMM_H #define _HDRVCOMM_H #ifdef __cplusplus extern "C" { #endif /* ---------------------------------------------------------------------- */ #include #include "soundmodem.h" #include /* ---------------------------------------------------------------------- */ struct hdrvc_channel_params { int tx_delay; /* the transmitter keyup delay in 10ms units */ int tx_tail; /* the transmitter keyoff delay in 10ms units */ int slottime; /* the slottime in 10ms; usually 10 = 100ms */ int ppersist; /* the p-persistence 0..255 */ int fulldup; /* some driver do not support full duplex, setting */ /* this just makes them send even if DCD is on */ }; struct hdrvc_channel_state { int ptt; int dcd; int ptt_keyed; unsigned long tx_packets; unsigned long tx_errors; unsigned long rx_packets; unsigned long rx_errors; }; /* ---------------------------------------------------------------------- */ /* * diagnose modes */ #define HDRVC_DIAGMODE_OFF 0 #define HDRVC_DIAGMODE_INPUT 1 #define HDRVC_DIAGMODE_DEMOD 2 #define HDRVC_DIAGMODE_CONSTELLATION 3 /* * diagnose flags */ #define HDRVC_DIAGFLAG_DCDGATE (1<<0) /* ---------------------------------------------------------------------- */ extern int hdrvc_recvpacket(char *pkt, int maxlen); extern int hdrvc_getfd(void); extern const char *hdrvc_ifname(void); extern void hdrvc_args(int *argc, char *argv[], const char *def_if); extern void hdrvc_init(void); extern int hdrvc_get_samples(void); extern int hdrvc_get_bits(void); extern int hdrvc_get_channel_access_param(struct hdrvc_channel_params *par); extern int hdrvc_set_channel_access_param(struct hdrvc_channel_params par); extern int hdrvc_calibrate(int calib); extern int hdrvc_get_channel_state(struct hdrvc_channel_state *st); extern unsigned int hdrvc_get_ifflags(void); extern int hdrvc_diag2(unsigned int mode, unsigned int flags, short *data, unsigned int maxdatalen, unsigned int *samplesperbit); extern int hdrvc_get_driver_name(char *buf, int bufsz); extern int hdrvc_get_mode_name(char *buf, int bufsz); extern int hdrvc_hdlcdrv_ioctl(int cmd, struct hdlcdrv_ioctl *par); extern int hdrvc_sm_ioctl(int cmd, struct sm_ioctl *par); extern int hdrvc_baycom_ioctl(int cmd, struct baycom_ioctl *par); extern int hdrvc_diag(struct sm_diag_data *diag); /* ---------------------------------------------------------------------- */ #ifdef __cplusplus } #endif #endif /* _HDRVCOMM_H */ hdlcutil/sethdlc.8000066400000000000000000000231671442776067000144330ustar00rootroot00000000000000.\" Copyright 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) .\" May be distributed under the GNU General Public License .\" .\" portions from setserial.8 by Rickard E. Faith (faith@cs.unc.edu) .\" " .TH SETHDLC 8 "27 April 2008" "Sethdlc 0.1" "Linux Programmer's Manual" .SH NAME sethdlc \- get/set Linux HDLC packet radio modem driver port information .SH SYNOPSIS .B sethdlc .B "[ \-bdhs ]" .B "[\-i device]" .B "sethdlc [\-i device] -c" cal .B "sethdlc\ -p" .B "[\-i\ device]" .BR "[\ mode\ " mode "\ ]" .BR "[\ io\ " iobase "\ ]" .BR "[\ irq\ " irq "\ ]" .BR "[\ dma\ " dma "\ ]" .BR "[\ dma2\ " dma2 "\ ]" .BR "[\ serio\ " seriobase "\ ]" .BR "[\ pario\ " pariobase "\ ]" .BR "[\ midiio\ " midiiobase "\ ]" .BR "[\ options\ " options "\ ]" .B "sethdlc\ -a" .B "[\-i\ device]" .BR "[\ txd\ " txdelay "\ ]" .BR "[\ txtail\ " txtail "\ ]" .BR "[\ slot\ " slottime "\ ]" .BR "[\ ppersist\ " ppersistence "\ ]" .BR "[\ full\ ]" .BR "[\ half\ ]" .SH DESCRIPTION .B sethdlc is a program designed to set and/or report the configuration information associated with a soundcard radio modem port. This information includes the modem type, what I/O port, IRQ and DMA channel a particular modem port is using, and where to output a transmitter keying (PTT) signal. With the .B \-p option, .B sethdlc sets and/or reports the port configuration. With the .B \-a option, .B sethdlc sets and/or reports the AX.25 channel access parameters. These parameters can also be set with the .I kissparms utility. With the .B \-c option, .B sethdlc instructs the driver to send a calibration pattern for .I cal seconds. Without the .B \-p, .B \-a and .B \-c option, .B sethdlc will stay in the foreground and display received packets. The AX.25 header and eventually a FlexNet compressed header are decoded. CTRL-C terminates .B sethdlc. Specifying additional options, .B sethdlc may display additional information. .SH OPTIONS .B sethdlc accepts the following options: .TP .B \-b Trace the bits at the output of the demodulator, after RX clock recovery. This option is only available if \fBsethdlc\fP and the soundcard modem kernel driver is compiled with debugging support on. This is useful for driver debugging. .TP .B \-d Trace DCD, PTT and other status information on stdout. \fBsethdlc\fP displays two times per second a line containing this informations. .TP .B \-h Display an overview of the available command line parameters and exit. .TP .B \-i The .I device argument specifies the HDLC modem device which should be configured or interrogated. It will usually have the following form: \fIbc[0-3]\fP for the baycom driver and \fIsm[0-3]\fP for the soundcard modem driver. .TP .B \-s Trace the bits at the demodulator output, \fIbefore\fP the RX clock recovery, to stdout. This option is only available the modem driver is compiled with debugging support on. It may not be available on some modem, such as the \fIpar96\fP. .SH PARAMETERS The following parameters can be assigned to a soundcard radio modem port. All numerical parameter values are assumed to be in decimal unless preceded by "0x". The .B mode parameter sets the type of hardware and the operating mode of the driver. \fIser12\fP and \fIpar96\fP are valid modes for the \fBbaycom\fP driver. A star "\fI*\fP" may be added to enable software DCD. The \fBmode\fP string format of the \fBsoundmodem\fP driver is as follows: \fIhw:mode\fP. \fIhw\fP may be either \fIsbc\fP, \fIwss\fP or \fIwssfdx\fP. The first one specifies SoundBlaster compatible soundcards, the second one WindowsSoundSystem compatible hardware, and the third one WSS fullduplex operation (which currently works with Crystal Semiconductor Chipsets CS423[126]). The \fImode\fP portion may be \fIafsk1200\fP or \fIfsk9600\fP. Optionally, the receive and transmit modes may be different (\fIhw:txmode.rxmode\fP). The .B ioport parameter sets the I/O port address. Typical values for the \fIser12\fP modem are 0x3f8, 0x2f8, 0x3e8 or 0x2e8, for the \fIpar96\fP modem 0x378, 0x278 or 0x3bc, for the \fIsbc\fP modems 0x220 and for the \fIwss\fP modems 0x530, 0x608, 0xe80 or 0xf40. The .B irq parameter sets the hardware IRQ number. Typical values for the \fIser12\fP modem are 4 and 3, for the \fIpar96\fP modem 7 or 5, for the \fIsbc\fP modems are 7 or 5 and for the \fIwss\fP modems, any free IRQ from the set 2, 7, 9, 10, 11 will do. The driver automatically configures the WSS soundcard to the correct IRQ. The .B dma parameter sets the hardware DMA number. Typical values for the \fIsbc\fP modems are 1 or 0 and for the \fIwss\fP modems, any free DMA from 0 to 3 (except 2) will do. The driver automatically configures the WSS soundcard to the correct DMA. The Baycom modems do not need DMA. The .B dma2 parameter sets the second hardware DMA number. This is only needed for full duplex operation with the \fBsoundmodem\fP driver. The .B seriobase parameter optionally sets the address of a serial port, where the driver will output a PTT signal at the TxD and RTS pins, and a DCD signal at the DTR pin. As Baycom modems do have their own PTT pin, this parameter is not used by the Baycom modem driver. The .B pariobase parameter optionally sets the address of a LPT port where the driver will output a PTT signal on the DATA0 line and a DCD signal on the DATA1 line. As Baycom modems do have their own PTT pin, this parameter is not used by the Baycom modem driver. The .B midiiobase parameter optionally sets the address of a MPU401 compatible MIDI port, where the driver will output a PTT signal. Since the MIDI port is effectively an UART and therefore cannot output a DC signal, the output must be fed through a retriggerable monoflop with a period of about 15ms. See .I http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html for a sample schematic diagram. As Baycom modems do have their own PTT pin, this parameter is not used by the Baycom modem driver. The .B txdelay sets the transmitter keyup delay time. Unlike \fIkissparms\fP, the unit is \fItens of ms\fP. This is the time the transmitter needs to switch its PA on and for its frequency synthesizer to settle. Typical values for a handheld transceiver are 200ms (i.e. 20), and for a good crystal driven transceiver 20ms (i.e. 2). The .B txtail sets the time PTT is held after the last packet. Unlike \fIkissparms\fP, the unit is \fItens of ms\fP. Do not set this value to 0. Most modems need some extra time to actually clock the last bits out to the transmitter. The .B slottime parameter specifies how often the channel access algorithm is executed. Unlike \fIkissparms\fP, the unit is \fItens of ms\fP. Unless you have very specific requirements, set this to 100ms (i.e. 10). The .B ppersist sets how "eagerly" the station starts to transmit as soon as the channel gets free. The optimum value is 256 divided by the number of stations on the channel. (This should really be done automatically by the L2) .B full sets the modem to full duplex mode. Note that some modems do not actually support full duplex mode, in this case this parameter makes the modem start its transmission as soon as it gets packets from the upper layer, without waiting for the channel to become free. This is needed by some implementations of alternative channel access algorithms, e.g. \fIDAMA\fP. .B half sets the modem to half duplex mode. .SH CONSIDERATIONS OF CONFIGURING BAYCOM PORTS It is important to note that sethdlc merely tells the Linux kernel where it should expect to find the I/O port and IRQ lines of a particular serial port. It does .I not configure the hardware to use a particular I/O port. In order to do that, you will need to physically program the serial board, usually by setting some jumpers or by switching some DIP switches. This section will provide some pointers in helping you decide how you would like to configure your baycom ports. The "standard MS-DOS" port associations are given below: .nf .RS COM1, port 0x3f8, irq 4 COM2, port 0x2f8, irq 3 COM3, port 0x3e8, irq 4 COM4, port 0x2e8, irq 3 LPT1, port 0x378, irq 7 LPT1 (on hercules graphics adapter), port 0x3bc, irq 7 LPT1, port 0x278, irq 5 .RE .fi .SH CONSIDERATIONS OF CONFIGURING SOUNDCARD RADIO MODEM PORTS Some cards need to be initialized before they act as a WSS or SoundBlaster compatible card. This driver does \fInot\fP do this. You can use the standard linux sound driver, if compiled as a module. Just load the sound driver (insmod sound) and remove it again (rmmod sound). The card should then be configured for either soundblaster or WSS compatibility. If this does not work for some reason, you'll have to write your own soundcard configuration utility. This is not as complicated as it sounds; it can be done from user space (but requiring root privileges) using \fIioperm\fP and/or \fIiopl\fP. It is important that the audio levels of your radio match those of the soundcard. To help achieve this, there are two utilities: \fIsmdiag\fP and \fIsmmixer\fP. See their respective manpage. The sound driver and the soundcard modem driver are mutually exclusive, i.e. they cannot both access the same soundcard at the same time. Even worse, the sound driver reserves the soundcard as soon as it gets loaded. The souncard modem driver however reserves the card only when the interface is started, i.e. during ifconfig sm[0-3] up. 9600 baud may not currently work on SoundBlaster cards with DSP revision 4.x, i.e. SB16 and SB32 AWE. This is because they seem to not be fully backwards compatible. .SH CAUTION CAUTION: Using an invalid port can lock up your machine. .SH "SEE ALSO" .nf .BR smdiag "\ (8), " smmixer "\ (8), " kissparms "\ (8)," linux/drivers/net/hdlcdrv.c, linux/drivers/net/baycom.c, linux/drivers/net/soundmodem.c .fi .SH AUTHOR sethdlc was written by Thomas Sailer, HB9JNX/AE4WA (t.sailer@alumni.ethz.ch). Inspired by setserial. hdlcutil/sethdlc.c000066400000000000000000000357661442776067000145160ustar00rootroot00000000000000/*****************************************************************************/ /* * sethdlc.c -- kernel HDLC radio modem driver setup utility. * * Copyright (C) 1996,1997,2000 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 16.10.1996 Adapted from setbaycom.c and setsm.c * 0.2 20.11.1996 New mode set/query code * 0.5 11.05.1997 introduced hdrvcomm.h * 0.6 05.01.2000 glibc compile fixes */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "hdrvcomm.h" #include #include /* ---------------------------------------------------------------------- */ static unsigned char trace_stat; static char *progname; /* ---------------------------------------------------------------------- */ static void display_packet(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 print_bits(int (*bitproc)(void)) { int ret; int i; char str[9]; char *cp; for (;;) { ret = bitproc(); if (ret < 0) { if (errno == EAGAIN) return; fprintf(stderr, "%s: Error %s (%i), cannot ioctl\n", progname, strerror(errno), errno); return; } strcpy(cp = str, "00000000"); for (i = 0; i < 8; i++, cp++, ret >>= 1) *cp += (ret & 1); printf("%s", str); } } /* ---------------------------------------------------------------------- */ static void do_set_params(int argc, char **argv) { struct hdlcdrv_ioctl drvname, curm, newm, mlist, curp, newp, mpmask; char set = 0; int ret; int mask; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_DRIVERNAME, &drvname); if (ret < 0) { perror("ioctl (HDLCDRVCTL_DRIVERNAME)"); exit(1); } printf("driver name: %s\n", drvname.data.drivername); mask = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_MODEMPARMASK, &mpmask); if (mask < 0) mask = ~0; ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_GETMODEMPAR, &curp); if (ret < 0) { perror("ioctl (HDLCDRVCTL_GETMODEMPAR)"); exit(1); } ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_GETMODE, &curm); if (ret < 0) { perror("ioctl (HDLCDRVCTL_GETMODE)"); exit(1); } newm = curm; newp = curp; while (argc >= 2) { if (!strcasecmp(argv[0], "mode")) { strncpy(newm.data.modename, argv[1], sizeof(newm.data.modename)); set |= 1; } else if (!strcasecmp(argv[0], "io") && mask & HDLCDRV_PARMASK_IOBASE) { newp.data.mp.iobase = strtoul(argv[1], NULL, 0); set |= 2; } else if (!strcasecmp(argv[0], "irq") && mask & HDLCDRV_PARMASK_IRQ) { newp.data.mp.irq = strtoul(argv[1], NULL, 0); set |= 2; } else if (!strcasecmp(argv[0], "dma") && mask & HDLCDRV_PARMASK_DMA) { newp.data.mp.dma = strtoul(argv[1], NULL, 0); set |= 2; } else if (!strcasecmp(argv[0], "dma2") && mask & HDLCDRV_PARMASK_DMA2) { newp.data.mp.dma2 = strtoul(argv[1], NULL, 0); set |= 2; } else if (!strcasecmp(argv[0], "serio") && mask & HDLCDRV_PARMASK_SERIOBASE) { newp.data.mp.seriobase = strtol(argv[1], NULL, 0); set |= 2; } else if (!strcasecmp(argv[0], "pario") && mask & HDLCDRV_PARMASK_PARIOBASE) { newp.data.mp.pariobase = strtol(argv[1], NULL, 0); set |= 2; } else if (!strcasecmp(argv[0], "midiio") && mask & HDLCDRV_PARMASK_MIDIIOBASE) { newp.data.mp.midiiobase = strtol(argv[1], NULL, 0); set |= 2; } else { fprintf(stderr, "%s: invalid parameter type '%s', " "valid: mode%s%s%s%s%s%s%s\n", progname, argv[0], (mask & HDLCDRV_PARMASK_IOBASE) ? " io" : "", (mask & HDLCDRV_PARMASK_IRQ) ? " irq" : "", (mask & HDLCDRV_PARMASK_DMA) ? " dma" : "", (mask & HDLCDRV_PARMASK_DMA2) ? " dma2" : "", (mask & HDLCDRV_PARMASK_SERIOBASE) ? " serio" : "", (mask & HDLCDRV_PARMASK_PARIOBASE) ? " pario" : "", (mask & HDLCDRV_PARMASK_MIDIIOBASE) ? " midiio" : ""); } argv += 2; argc -= 2; } if (argc >= 1) fprintf(stderr, "%s: orphaned parameter type '%s', no data\n", progname, argv[0]); printf("current parameters: mode %s", curm.data.modename); if (mask & HDLCDRV_PARMASK_IOBASE) printf(" io 0x%x", curp.data.mp.iobase); if (mask & HDLCDRV_PARMASK_IRQ) printf(" irq %u", curp.data.mp.irq); if (mask & HDLCDRV_PARMASK_DMA) printf(" dma %u", curp.data.mp.dma); if (mask & HDLCDRV_PARMASK_DMA2) printf(" dma2 %u", curp.data.mp.dma2); if (mask & HDLCDRV_PARMASK_SERIOBASE) printf(" serio 0x%x", curp.data.mp.seriobase); if (mask & HDLCDRV_PARMASK_PARIOBASE) printf(" pario 0x%x", curp.data.mp.pariobase); if (mask & HDLCDRV_PARMASK_MIDIIOBASE) printf(" midiio 0x%x", curp.data.mp.midiiobase); printf("\n"); if (set & 1) ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_SETMODE, &newm); if (ret < 0) { perror("ioctl (HDLCDRVCTL_SETMODE)"); ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_MODELIST, &mlist); if (ret < 0) { perror("ioctl (HDLCDRVCTL_MODELIST)"); exit(1); } printf("driver supported modes: %s\n", mlist.data.modename); exit(1); } if (set & 2) { ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_SETMODEMPAR, &newp); if (ret < 0) { perror("ioctl (HDLCDRVCTL_SETMODEMPAR)"); fprintf(stderr, "%s: trying to restore old " "parameters\n", progname); ret = hdrvc_hdlcdrv_ioctl(HDLCDRVCTL_SETMODEMPAR, &curp); if (ret < 0) perror("ioctl (HDLCDRVCTL_SETMODEMPAR)"); exit(1); } } if (set & 3) { printf("new parameters: mode %s", newm.data.modename); if (mask & HDLCDRV_PARMASK_IOBASE) printf(" io 0x%x", newp.data.mp.iobase); if (mask & HDLCDRV_PARMASK_IRQ) printf(" irq %u", newp.data.mp.irq); if (mask & HDLCDRV_PARMASK_DMA) printf(" dma %u", newp.data.mp.dma); if (mask & HDLCDRV_PARMASK_DMA2) printf(" dma2 %u", newp.data.mp.dma2); if (mask & HDLCDRV_PARMASK_SERIOBASE) printf(" serio 0x%x", newp.data.mp.seriobase); if (mask & HDLCDRV_PARMASK_PARIOBASE) printf(" pario 0x%x", newp.data.mp.pariobase); if (mask & HDLCDRV_PARMASK_MIDIIOBASE) printf(" midiio 0x%x", newp.data.mp.midiiobase); printf("\n"); } exit(0); } /* ---------------------------------------------------------------------- */ static void display_channel_params(const struct hdrvc_channel_params *par) { printf("TX delay %ums, TX tail %ums, slottime %ums, p-persistence " " %u/256, %s duplex\n", 10*par->tx_delay, 10*par->tx_tail, 10*par->slottime, par->ppersist, par->fulldup ? "Full" : "Half"); } /* ---------------------------------------------------------------------- */ static void do_set_channel_params(int argc, char **argv) { struct hdrvc_channel_params par1, par2; char set = 0; int ret; ret = hdrvc_get_channel_access_param(&par1); if (ret < 0) { perror("hdrvc_get_channel_access_param"); exit(1); } par2 = par1; while (argc > 0) { if (argc >= 2 && !strcasecmp(argv[0], "txd")) { par2.tx_delay = strtoul(argv[1], NULL, 0) / 10; set = 1; argv += 2; argc -= 2; } else if (argc >= 2 && !strcasecmp(argv[0], "txtail")) { par2.tx_tail = strtoul(argv[1], NULL, 0) / 10; set = 1; argv += 2; argc -= 2; } else if (argc >= 2 && !strcasecmp(argv[0], "slot")) { par2.slottime = strtoul(argv[1], NULL, 0) / 10; set = 1; argv += 2; argc -= 2; } else if (argc >= 2 && !strcasecmp(argv[0], "ppersist")) { par2.ppersist = strtoul(argv[1], NULL, 0); set = 1; argv += 2; argc -= 2; } else if (!strcasecmp(argv[0], "half")) { par2.fulldup = 0; set = 1; argv += 1; argc -= 1; } else if (!strcasecmp(argv[0], "full")) { par2.fulldup = 1; set = 1; argv += 1; argc -= 1; } else { argv += 1; argc -= 1; } } printf("current parameters: "); display_channel_params(&par1); if (set) { ret = hdrvc_set_channel_access_param(par2); if (ret < 0) { perror("hdrvc_set_channel_access_param"); exit(1); } printf("new parameters: "); display_channel_params(&par2); } exit(0); } /* ---------------------------------------------------------------------- */ static const char *usage_str = "[-b] [-i] [-d] [-i ] [-h] [-c ]\n" "[-p] [hw ] [type ] [io ] [irq ] [dma ]\n" " [options ] [serio ] [pario ] [midiio ]\n" "[-a] [txd ] [txtail ] [slot ]\n" " [ppersist ] [full] [half]\n" " -a: set or display channel access parameters\n" " -b: trace demodulated bits\n" " -s: trace sampled input from tcm3105 (ser12 only)\n" " -d: trace dcd and ptt status on stdout\n" " -i: specify the name of the baycom kernel driver interface\n" " -c: calibrate (i.e. send calibration pattern) for cal seconds\n" " -p: set or display interface parameters\n" " -h: this help\n\n"; int main(int argc, char *argv[]) { int ret; fd_set fds_read; fd_set fds_write; struct timeval tm; char getsetparams = 0; char cal = 0; int cal_val = 0; struct sm_ioctl bsi; struct baycom_ioctl bbi; struct hdrvc_channel_state chst; char pktbuf[2048]; progname = *argv; printf("%s: Version " FULL_VER "; (C) 1996-1997 by Thomas Sailer HB9JNX/AE4WA\n", *argv); hdrvc_args(&argc, argv, "bc0"); while ((ret = getopt(argc, argv, "bsdhpc:a")) != -1) { switch (ret) { case 'b': trace_stat = 2; break; case 's': trace_stat = 3; break; case 'd': trace_stat = 1; break; case 'p': getsetparams = 1; break; case 'a': getsetparams = 2; break; case 'c': cal = 1; cal_val = strtoul(optarg, NULL, 0); break; default: printf("usage: %s %s", *argv, usage_str); exit(-1); } } hdrvc_init(); if (getsetparams == 1) { do_set_params(argc - optind, argv+optind); exit(0); } if (getsetparams == 2) { do_set_channel_params(argc - optind, argv+optind); exit(0); } if (cal) { ret = hdrvc_calibrate(cal_val); if (ret < 0) { perror("hdrvc_calibrate"); exit(1); } fprintf(stdout, "%s: calibrating for %i seconds\n", *argv, cal_val); exit(0); } for (;;) { while ((ret = hdrvc_recvpacket(pktbuf, sizeof(pktbuf))) > 1) display_packet(pktbuf+1, ret-1); switch (trace_stat) { case 1: ret = hdrvc_get_channel_state(&chst); if (ret < 0) perror("hdrvc_get_channel_state"); printf("%c%c rx: %lu tx: %lu rxerr: %lu txerr: %lu", chst.dcd ? 'D' : '-', chst.ptt ? 'P' : '-', chst.rx_packets, chst.tx_packets, chst.rx_errors, chst.tx_errors); if (hdrvc_sm_ioctl(SMCTL_GETDEBUG, &bsi) >= 0) { printf(" intrate: %u modcyc: %u " "demodcyc: %u dmares: %u", bsi.data.dbg.int_rate, bsi.data.dbg.mod_cycles, bsi.data.dbg.demod_cycles, bsi.data.dbg.dma_residue); } if (hdrvc_baycom_ioctl(BAYCOMCTL_GETDEBUG, &bbi) >= 0) { printf(" dbg1: %lu dbg2: %lu dbg3: %li", bbi.data.dbg.debug1, bbi.data.dbg.debug2, bbi.data.dbg.debug3); } printf("\n"); break; case 2: print_bits(hdrvc_get_bits); printf("\n"); break; case 3: print_bits(hdrvc_get_samples); printf("\n"); break; default: break; } tm.tv_usec = 500000L; tm.tv_sec = 0L; if (hdrvc_getfd() >= 0) { FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_SET(hdrvc_getfd(), &fds_read); ret = select(hdrvc_getfd()+1, &fds_read, &fds_write, NULL, &tm); } else ret = select(0, NULL, NULL, NULL, &tm); if (ret < 0) { fprintf(stderr, "%s: Error %s (%i) in select\n", *argv, strerror(errno), errno); exit(-2); } } } /* ---------------------------------------------------------------------- */ hdlcutil/smdiag.8000066400000000000000000000034341442776067000142440ustar00rootroot00000000000000.\" Copyright 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) .\" May be distributed under the GNU General Public License .\" " .TH SMDIAG 8 "27 April 2008" "Smdiag 0.1" "Linux Programmer's Manual" .SH NAME smdiag \- Linux soundcard packet radio modem driver diagnostics utility .SH SYNOPSIS .B smdiag .B "[\-i device]" .B "[\-d display]" .B "[ \-ce ]" .SH DESCRIPTION .B smdiag may help to adjust the audio levels of the soundcard modem driver, as well as to control the quality of the radio link. It may either display an oscilloscope like view of the input signal, or display an eye diagram. The display may be gated with DCD, i.e. only updated if the modem detects a data carrier. .SH OPTIONS .B smdiag accepts the following options: .TP .B \-i The .I device argument specifies the soundcard modem device which should be configured or interrogated. It will usually have the following form: .I sm[0-3]. .TP .B \-d sets the address of the X server to display the window. .TP .B \-h display an overview of the available command line parameters and exit. .TP .B \-c toggle the carrier gating of the display. .TP .B \-e display an eye diagram (overlay of the synchronized demodulator output) instead of an oscilloscope view of the input signal. .SH KEYS .TP .B C clears the window .TP .B D toggles the DCD gating. .TP .B E displays an eye diagram .TP .B I displays the input signal .TP .B Q quits .I smdiag .SH BUGS .B smdiag Reacts sluggishly to keypresses. The window size is fixed. For speed reasons, two square root operations per sample are not implemented in the AFSK 1200 baud modes. The eye diagram for the AFSK 1200 baud mode is therefore distorted. .SH "SEE ALSO" .BR smmixer " (8), " sethdlc " (8)," linux/drivers/net/soundmodem.c .SH AUTHOR smdiag was written by Thomas Sailer (t.sailer@alumni.ethz.ch). hdlcutil/smdiag.c000066400000000000000000000300071442776067000143130ustar00rootroot00000000000000/*****************************************************************************/ /* * smdiag.c -- kernel soundcard radio modem driver diagnostics utility. * * Copyright (C) 1996,1997,2000 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 26.06.1996 Started * 0.2 11.05.1997 introduced hdrvcomm.h * 0.3 05.01.2000 glibc compile fixes */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hdrvcomm.h" #include #include /* ---------------------------------------------------------------------- */ static char *progname; static Display *display; static Window window; static Pixmap pixmap; static unsigned long col_zeroline; static unsigned long col_background; static unsigned long col_trace; static GC gr_context; static int xmul; /* ---------------------------------------------------------------------- */ static int x_error_handler(Display *disp, XErrorEvent *evt) { char err_buf[256], mesg[256], number[256]; char *mtype = "XlibMessage"; XGetErrorText(disp, evt->error_code, err_buf, sizeof(err_buf)); fprintf(stderr, "X Error: %s\n", err_buf); XGetErrorDatabaseText(disp, mtype, "MajorCode", "Request Major code %d", mesg, sizeof(mesg)); fprintf(stderr, mesg, evt->request_code); sprintf(number, "%d", evt->request_code); XGetErrorDatabaseText(disp, "XRequest", number, "", err_buf, sizeof(err_buf)); fprintf(stderr, " (%s)\n", err_buf); abort(); } /* ---------------------------------------------------------------------- */ #define WIDTH 512 #define HEIGHT (constell ? 512 : 256) static int openwindow(char *disp, int constell, int samplesperbit) { XSetWindowAttributes attr; XGCValues gr_values; XColor color, dummy; XSizeHints sizehints; display = XOpenDisplay(NULL); if (!display) return -1; XSetErrorHandler(x_error_handler); XAllocNamedColor(display, DefaultColormap(display, 0), "red", &color, &dummy); col_zeroline = color.pixel; col_background = WhitePixel(display, 0); col_trace = BlackPixel(display, 0); attr.background_pixel = col_background; window = XCreateWindow(display, XRootWindow(display, 0), 200, 200, WIDTH, HEIGHT, 5, DefaultDepth(display, 0), InputOutput, DefaultVisual(display, 0), CWBackPixel, &attr); if (!window) { fprintf(stderr, "smdiag: unable to open X window\n"); exit(1); } pixmap = XCreatePixmap(display, window, WIDTH, HEIGHT, DefaultDepth(display, 0)); if (!pixmap) { fprintf(stderr, "smdiag: unable to open offscreen pixmap\n"); exit(1); } xmul = WIDTH / (2*(samplesperbit > 0 ? samplesperbit : 1)); XSelectInput(display, window, KeyPressMask | StructureNotifyMask | ExposureMask) ; XAllocNamedColor(display, DefaultColormap(display, 0), "red", &color, &dummy); gr_values.foreground = col_trace; gr_values.line_width = 1; gr_values.line_style = LineSolid; gr_context = XCreateGC(display, window, GCForeground | GCLineWidth | GCLineStyle, &gr_values); XStoreName(display, window, "diagnostics"); /* * Do not allow the window to be resized */ memset(&sizehints, 0, sizeof(sizehints)); sizehints.min_width = sizehints.max_width = WIDTH; sizehints.min_height = sizehints.max_height = HEIGHT; sizehints.flags = PMinSize | PMaxSize; XSetWMNormalHints(display, window, &sizehints); XMapWindow(display, window); XSynchronize(display, 1); return 0; } #undef WIDTH #undef HEIGHT /* ---------------------------------------------------------------------- */ static void closewindow(void) { if (!display) return; XDestroyWindow(display, window); XCloseDisplay(display); display = NULL; } /* ---------------------------------------------------------------------- */ #define XCOORD(x) ((x) * xm) #define YCOORD(y) ((SHRT_MAX - (int)(y)) * winattrs.height / USHRT_MAX) static void drawdata(short *data, int len, int replace, int xm) { int cnt; GC gc; XGCValues gcv; XWindowAttributes winattrs; if (!display || !pixmap) return; XGetWindowAttributes(display, window, &winattrs); gcv.line_width = 1; gcv.line_style = LineSolid; gc = XCreateGC(display, pixmap, GCLineWidth | GCLineStyle, &gcv); XSetState(display, gc, col_background, col_background, GXcopy, AllPlanes); if (replace) XFillRectangle(display, pixmap, gc, 0, 0, winattrs.width, winattrs.height); else XCopyArea(display, window, pixmap, gr_context, 0, 0, winattrs.width, winattrs.height, 0, 0); XSetForeground(display, gc, col_trace); for (cnt = 0; cnt < len-1; cnt++) XDrawLine(display, pixmap, gc, XCOORD(cnt), YCOORD(data[cnt]), XCOORD(cnt+1), YCOORD(data[cnt+1])); XSetForeground(display, gc, col_zeroline); XDrawLine(display, pixmap, gc, 0, YCOORD(0), winattrs.width, YCOORD(0)); XCopyArea(display, pixmap, window, gr_context, 0, 0, winattrs.width, winattrs.height, 0, 0); XFreeGC(display, gc); XSync(display, 0); } #undef XCOORD #undef YCOORD /* ---------------------------------------------------------------------- */ #define XCOORD(x) ((SHRT_MAX - (int)(x)) * winattrs.width / USHRT_MAX) #define YCOORD(y) ((SHRT_MAX - (int)(y)) * winattrs.height / USHRT_MAX) static void drawconstell(short *data, int len) { int cnt; GC gc; XGCValues gcv; XWindowAttributes winattrs; if (!display || !pixmap) return; XGetWindowAttributes(display, window, &winattrs); gcv.line_width = 1; gcv.line_style = LineSolid; gc = XCreateGC(display, pixmap, GCLineWidth | GCLineStyle, &gcv); XSetState(display, gc, col_background, col_background, GXcopy, AllPlanes); XCopyArea(display, window, pixmap, gr_context, 0, 0, winattrs.width, winattrs.height, 0, 0); XSetForeground(display, gc, col_trace); for (cnt = 0; cnt < len-1; cnt += 2) XDrawPoint(display, pixmap, gc, XCOORD(data[cnt]), YCOORD(data[cnt+1])); XSetForeground(display, gc, col_zeroline); XDrawLine(display, pixmap, gc, 0, YCOORD(0), winattrs.width, YCOORD(0)); XDrawLine(display, pixmap, gc, XCOORD(0), 0, XCOORD(0), winattrs.height); XCopyArea(display, pixmap, window, gr_context, 0, 0, winattrs.width, winattrs.height, 0, 0); XFreeGC(display, gc); XSync(display, 0); } #undef XCOORD #undef YCOORD /* ---------------------------------------------------------------------- */ static void clearwindow(void) { XWindowAttributes winattrs; GC gc; XGCValues gcv; if (!display || !pixmap) return; XGetWindowAttributes(display, window, &winattrs); gcv.line_width = 1; gcv.line_style = LineSolid; gc = XCreateGC(display, pixmap, GCLineWidth | GCLineStyle, &gcv); XSetState(display, gc, col_background, col_background, GXcopy, AllPlanes); XFillRectangle(display, pixmap, gc, 0, 0, winattrs.width, winattrs.height); XSetForeground(display, gc, col_zeroline); XClearArea(display, window, 0, 0, 0, 0, False); XCopyArea(display, pixmap, window, gr_context, 0, 0, winattrs.width, winattrs.height, 0, 0); XFreeGC(display, gc); } /* ---------------------------------------------------------------------- */ static Bool predicate(Display *display, XEvent *event, char *arg) { return True; } /* ---------------------------------------------------------------------- */ static char *getkey(void) { XWindowAttributes winattrs; XEvent evt; static char kbuf[32]; int i; if (!display) return NULL; XSync(display, 0); while (XCheckIfEvent(display, &evt, predicate, NULL)) { switch (evt.type) { case KeyPress: i = XLookupString((XKeyEvent *)&evt, kbuf, sizeof(kbuf)-1, NULL, NULL); if (!i) return NULL; kbuf[i] = 0; return kbuf; case DestroyNotify: XCloseDisplay(display); display = NULL; return NULL; case Expose: XGetWindowAttributes(display, window, &winattrs); XCopyArea(display, pixmap, window, gr_context, 0, 0, winattrs.width, winattrs.height, 0, 0); break; default: break; } } return NULL; } /* ---------------------------------------------------------------------- */ static void printmode(unsigned int mode, unsigned int trigger) { printf("Source: %s%s\n", (mode == SM_DIAGMODE_DEMOD) ? "demodulator (eye diagram)" : "input (oscilloscope)", (trigger & SM_DIAGFLAG_DCDGATE) ? " gated with DCD" : ""); } /* ---------------------------------------------------------------------- */ static const char *usage_str = "[-d display] [-i smif] [-c] [-e]\n" " -d: display host\n" " -i: specify the name of the baycom kernel driver interface\n" " -c: toggle carrier trigger\n" " -e: eye diagram mode\n\n" " -p: constellation plot\n\n"; int main(int argc, char *argv[]) { char *disp = NULL; unsigned int mode = HDRVC_DIAGMODE_INPUT; unsigned int trigger = 0; unsigned int ifflags; short data[256]; char *cp; int ret; unsigned int samplesperbit; progname = *argv; printf("%s: Version " FULL_VER "; (C) 1996-1997 by Thomas Sailer HB9JNX/AE4WA\n", *argv); hdrvc_args(&argc, argv, "sm0"); while ((ret = getopt(argc, argv, "d:ecp")) != -1) { switch (ret) { case 'd': disp = optarg; break; case 'e': mode = HDRVC_DIAGMODE_DEMOD; trigger = HDRVC_DIAGFLAG_DCDGATE; break; case 'c': trigger ^= HDRVC_DIAGFLAG_DCDGATE; break; case 'p': mode = HDRVC_DIAGMODE_CONSTELLATION; break; default: printf("usage: %s %s", *argv, usage_str); exit(-1); } } hdrvc_init(); ifflags = hdrvc_get_ifflags(); if (!(ifflags & IFF_UP)) { fprintf(stderr, "interface %s down\n", hdrvc_ifname()); exit(1); } if (!(ifflags & IFF_RUNNING)) { fprintf(stderr, "interface %s not running\n", hdrvc_ifname()); exit(1); } printmode(mode, trigger); for (;;) { ret = hdrvc_diag2(mode, trigger, data, sizeof(data) / sizeof(short), &samplesperbit); if (ret < 0) { perror("hdrvc_diag2"); exit(1); } if (ret > 0) { if (!display) { openwindow(disp, mode == HDRVC_DIAGMODE_CONSTELLATION, samplesperbit); clearwindow(); } if (mode == SM_DIAGMODE_CONSTELLATION) drawconstell(data, ret); else drawdata(data, ret, mode == HDRVC_DIAGMODE_INPUT, mode == HDRVC_DIAGMODE_INPUT ? 5:xmul); } else usleep(100000L); if (display) { cp = getkey(); if (cp) { for (; *cp; cp++) { printf("char pressed: '%c'\n", *cp); switch (*cp) { case 'c': case 'C': clearwindow(); printmode(mode, trigger); break; case 'q': case 'Q': closewindow(); break; case 'i': case 'I': if (mode == HDRVC_DIAGMODE_CONSTELLATION) break; mode = HDRVC_DIAGMODE_INPUT; clearwindow(); printmode(mode, trigger); break; case 'e': case 'E': if (mode == HDRVC_DIAGMODE_CONSTELLATION) break; mode = HDRVC_DIAGMODE_DEMOD; clearwindow(); printmode(mode, trigger); break; case 'd': case 'D': trigger ^= HDRVC_DIAGFLAG_DCDGATE; clearwindow(); printmode(mode, trigger); break; } } } if (!display) exit(0); } } } /* ---------------------------------------------------------------------- */ hdlcutil/smmixer.8000066400000000000000000000100401442776067000144530ustar00rootroot00000000000000.\" Copyright 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) .\" May be distributed under the GNU General Public License .\" " .TH SMMIXER 8 "27 April 2008" "Smmixer 0.1" "Linux Programmer's Manual" .SH NAME smmixer \- get/set Linux soundcard packet radio modem driver mixer .SH SYNOPSIS .B smmixer .B "[\-i device]" .BR "[ " params " ]" .SH DESCRIPTION .B smmixer displays and/or sets the input source and input and output levels of a soundcard modem port. .SH OPTIONS .B smmixer accepts the following option: .TP .B \-i The .I device argument specifies the soundcard modem device which should be configured or interrogated. It will usually have the following form: .I sm[0-3]. .SH PARAMETERS The .B AD1848 (WSS) mixer accepts the following parameters: .TP .B ol=val sets the level of the left output to the specified value. Legal values are from -100..0dB. .TP .B or=val sets the level of the right output to the specified value. Legal values are from -100..0dB. .TP .B o=val sets the level of both outputs to the specified value. Legal values are from -100..0dB. .TP .B il=val sets the level of the left input to the specified value. Legal values are from 0..43dB. .TP .B ir=val sets the level of the right input to the specified value. Legal values are from 0..43dB. .TP .B i=val sets the level of both inputs to the specified value. Legal values are from 0..43dB. .TP .B sl=val sets the source of the left input to the specified value. Legal values are .I line, aux1, mic or .I dac. .TP .B sr=val sets the source of the right input to the specified value. Legal values are .I line, aux1, mic or .I dac. .TP .B s=val sets the source of both inputs to the specified value. Legal values are .I line, aux1, mic or .I dac. .in \n[IN]u The .B CT1335 (SB2.x) mixer accepts the following parameter: .TP .B o=val sets the output level to the specified value. Legal values are from -46..0dB. .in \n[IN]u The .B CT1345 (SBPro) mixer accepts the following parameters: .TP .B ol=val sets the level of the left output to the specified value. Legal values are from -46..0dB. .TP .B or=val sets the level of the right output to the specified value. Legal values are from -46..0dB. .TP .B o=val sets the level of both outputs to the specified value. Legal values are from -46..0dB. .TP .B s=val sets the input source to the specified value. Legal values are .I mic, cd or .I line. .in \n[IN]u The .B CT1745 (SB16, SB32 AWE) mixer accepts the following parameters: .TP .B ol=val sets the level of the left output to the specified value. Legal values are from -62..18dB. .TP .B or=val sets the level of the right output to the specified value. Legal values are from -62..18dB. .TP .B o=val sets the level of both outputs to the specified value. Legal values are from -62..18dB. .TP .B il=val sets the level of the left input to the specified value. Legal values are from -62..18dB. .TP .B ir=val sets the level of the right input to the specified value. Legal values are from -62..18dB. .TP .B i=val sets the level of both inputs to the specified value. Legal values are from -62..18dB. .TP .B s=val enables the specified value as a source. Legal values are .I line, line.l, line.r, midi, midi.l, midi.r, cd, cd.l, cd.r or .I mic. .SH CONSIDERATIONS It is important that the audio levels of your radio match those of the soundcard. To help achieve this, use the .I smdiag utility. This utility can only be used after the interface is started up, i.e. after ifconfig sm? up. The sound driver and the soundcard modem driver are mutually exclusive, i.e. they cannot both access the same soundcard at the same time. Even worse, the sound driver reserves the soundcard as soon as it gets loaded. The souncard modem driver however reserves the card only when the interface is started, i.e. during ifconfig if up. 9600 baud may not currently work on SoundBlaster cards with DSP revision 4.x, i.e. SB16 and SB32 AWE. This is because they seem to not be fully backwards compatible. .SH "SEE ALSO" .BR smdiag " (8), " sethdlc " (8)," linux/drivers/net/soundmodem.c .SH AUTHOR smmixer was written by Thomas Sailer (t.sailer@alumni.ethz.ch). hdlcutil/smmixer.c000066400000000000000000000541111442776067000145350ustar00rootroot00000000000000/*****************************************************************************/ /* * smmixer.c -- kernel soundcard radio modem driver mixer utility. * * Copyright (C) 1996,1997,2000 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 26.06.1996 Started * 0.2 11.05.1997 introduced hdrvcomm.h * 0.3 05.01.2000 glibc compile fixes */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include "hdrvcomm.h" #include #include /* ---------------------------------------------------------------------- */ static char *progname; static unsigned int mixdevice; /* ---------------------------------------------------------------------- */ static int do_mix_ioctl(int cmd, struct sm_mixer_data *mixdat) { struct sm_ioctl par; int i; par.cmd = cmd; par.data.mix = *mixdat; i = hdrvc_sm_ioctl(cmd, &par); *mixdat = par.data.mix; return i; } /* ---------------------------------------------------------------------- */ static unsigned char get_mixer_reg(unsigned char addr) { int i; struct sm_mixer_data mixdat; mixdat.reg = addr; i = do_mix_ioctl(SMCTL_GETMIXER, &mixdat); if (i < 0) { perror("do_mix_ioctl: SMCTL_GETMIXER"); exit(1); } if (!i) fprintf(stderr, "warning: mixer device %u register %u not " "accessible for reading!\n", mixdat.mixer_type, addr); if (mixdat.mixer_type != mixdevice) { fprintf(stderr, "error: mixer type changed !?\n"); exit(2); } return mixdat.data; } /* ---------------------------------------------------------------------- */ static void set_mixer_reg(unsigned char addr, unsigned char data) { struct sm_mixer_data mixdat; mixdat.reg = addr; mixdat.data = data; mixdat.mixer_type = mixdevice; if (do_mix_ioctl(SMCTL_SETMIXER, &mixdat) < 0) { perror("do_mix_ioctl: SMCTL_SETMIXER"); exit(1); } } /* ---------------------------------------------------------------------- */ static void display_mixer_ad1848(void) { static const char *src[4] = { "Line", "Aux1", "Mic", "Dac" }; unsigned char data; data = get_mixer_reg(0); printf("Left input: Source: %-4s Gain: %3ddB\n", src[(data>>6)&3], (((data & 0xe0) == 0xa0) ? 20 : 0) + (data & 0xf) * 3 / 2); data = get_mixer_reg(1); printf("Right input: Source: %-4s Gain: %3ddB\n", src[(data>>6)&3], (((data & 0xe0) == 0xa0) ? 20 : 0) + (data & 0xf) * 3 / 2); data = get_mixer_reg(2); if (!(data & 0x80)) printf("Left Aux1 mixing: Gain: %3ddB\n", (8 - (int)(data & 0x1f)) * 3 / 2); data = get_mixer_reg(3); if (!(data & 0x80)) printf("Right Aux1 mixing: Gain: %3ddB\n", (8 - (int)(data & 0x1f)) * 3 / 2); data = get_mixer_reg(4); if (!(data & 0x80)) printf("Left Aux2 mixing: Gain: %3ddB\n", (8 - (int)(data & 0x1f)) * 3 / 2); data = get_mixer_reg(5); if (!(data & 0x80)) printf("Right Aux2 mixing: Gain: %3ddB\n", (8 - (int)(data & 0x1f)) * 3 / 2); data = get_mixer_reg(6); if (data & 0x80) printf("Left output: muted\n"); else printf("Left output: Gain: %3ddB\n", ((int)(data & 0x3f)) * (-3) / 2); data = get_mixer_reg(7); if (data & 0x80) printf("Right output: muted\n"); else printf("Right output: Gain: %3ddB\n", ((int)(data & 0x3f)) * (-3) / 2); data = get_mixer_reg(13); if (data & 1) printf("Digital mix: Gain: %3ddB\n", ((int)((data >> 2) & 0x3f)) * (-3) / 2); } /* ---------------------------------------------------------------------- */ static void display_mixer_cs423x(void) { unsigned char data; display_mixer_ad1848(); data = get_mixer_reg(26); printf("Mono: %s%s%s Gain: %3ddB\n", (data & 0x80) ? "input muted, " : "", (data & 0x40) ? "output muted, " : "", (data & 0x20) ? "bypass, " : "", (int)(data & 0xf) * (-3)); data = get_mixer_reg(27); if (data & 0x80) printf("Left output: muted\n"); else printf("Left output: Gain: %3ddB\n", ((int)(data & 0xf)) * (-2)); data = get_mixer_reg(29); if (data & 0x80) printf("Right output: muted\n"); else printf("Right output: Gain: %3ddB\n", ((int)(data & 0xf)) * (-2)); } /* ---------------------------------------------------------------------- */ static void display_mixer_ct1335(void) { unsigned char data; data = get_mixer_reg(0x2); printf("Master volume: %3ddB\n", (((int)((data >> 1) & 7)) - 7) * 46 / 7); data = get_mixer_reg(0xa); printf("Voice volume: %3ddB\n", (((int)((data >> 1) & 3)) - 3) * 46 / 3); data = get_mixer_reg(0x6); printf("MIDI volume: %3ddB\n", (((int)((data >> 1) & 7)) - 7) * 46 / 7); data = get_mixer_reg(0x8); printf("CD volume: %3ddB\n", (((int)((data >> 1) & 7)) - 7) * 46 / 7); } /* ---------------------------------------------------------------------- */ static void display_mixer_ct1345(void) { static const char *src[4] = { "Mic", "CD", "Mic", "Line" }; unsigned char data, data2; data = get_mixer_reg(0xc); data2 = get_mixer_reg(0xe); printf("Input source: %s\n", src[(data >> 1) & 3]); if (!(data & data2 & 0x20)) { printf("Filter: Low pass %s kHz: ", (data & 0x8) ? "8.8" : "3.2"); if (data & 0x20) printf("output\n"); else printf("input%s\n", (data2 & 0x20) ? "" : ", output"); } if (data2 & 2) printf("stereo\n"); data = get_mixer_reg(0x22); printf("Master volume: Left: %3ddB Right: %3ddB\n", (((int)((data >> 5) & 7)) - 7) * 46 / 7, (((int)((data >> 1) & 7)) - 7) * 46 / 7); data = get_mixer_reg(0x4); printf("Voice volume: Left: %3ddB Right: %3ddB\n", (((int)((data >> 5) & 7)) - 7) * 46 / 7, (((int)((data >> 1) & 7)) - 7) * 46 / 7); data = get_mixer_reg(0x26); printf("MIDI volume: Left: %3ddB Right: %3ddB\n", (((int)((data >> 5) & 7)) - 7) * 46 / 7, (((int)((data >> 1) & 7)) - 7) * 46 / 7); data = get_mixer_reg(0x28); printf("CD volume: Left: %3ddB Right: %3ddB\n", (((int)((data >> 5) & 7)) - 7) * 46 / 7, (((int)((data >> 1) & 7)) - 7) * 46 / 7); data = get_mixer_reg(0x2e); printf("Line volume: Left: %3ddB Right: %3ddB\n", (((int)((data >> 5) & 7)) - 7) * 46 / 7, (((int)((data >> 1) & 7)) - 7) * 46 / 7); data = get_mixer_reg(0x0a); printf("Mic mixing volume: %3ddB\n", (((int)((data >> 1) & 3)) - 3) * 46 / 3); } /* ---------------------------------------------------------------------- */ static void display_mixer_ct1745(void) { unsigned char data, data2; printf("Master volume: Left: %3ddB Right: %3ddB\n", ((int)((get_mixer_reg(0x30) >> 3) & 0x1f) - 31) * 2, ((int)((get_mixer_reg(0x31) >> 3) & 0x1f) - 31) * 2); printf("Voice volume: Left: %3ddB Right: %3ddB\n", ((int)((get_mixer_reg(0x32) >> 3) & 0x1f) - 31) * 2, ((int)((get_mixer_reg(0x33) >> 3) & 0x1f) - 31) * 2); printf("MIDI volume: Left: %3ddB Right: %3ddB\n", ((int)((get_mixer_reg(0x34) >> 3) & 0x1f) - 31) * 2, ((int)((get_mixer_reg(0x35) >> 3) & 0x1f) - 31) * 2); printf("CD volume: Left: %3ddB Right: %3ddB\n", ((int)((get_mixer_reg(0x36) >> 3) & 0x1f) - 31) * 2, ((int)((get_mixer_reg(0x37) >> 3) & 0x1f) - 31) * 2); printf("Line volume: Left: %3ddB Right: %3ddB\n", ((int)((get_mixer_reg(0x38) >> 3) & 0x1f) - 31) * 2, ((int)((get_mixer_reg(0x39) >> 3) & 0x1f) - 31) * 2); printf("Mic volume: %3ddB\n", ((int)((get_mixer_reg(0x3a) >> 3) & 0x1f) - 31) * 2); printf("PC speaker volume: %3ddB\n", ((int)((get_mixer_reg(0x3b) >> 6) & 0x3) - 3) * 6); printf("Mic gain: %s\n", (get_mixer_reg(0x43) & 1) ? "fixed 20dB" : "AGC"); printf("Output gain: Left: %3ddB Right: %3ddB\n", ((int)((get_mixer_reg(0x41) >> 6) & 3)) * 6, ((int)((get_mixer_reg(0x42) >> 6) & 3)) * 6); printf("Input gain: Left: %3ddB Right: %3ddB\n", ((int)((get_mixer_reg(0x3f) >> 6) & 3)) * 6, ((int)((get_mixer_reg(0x40) >> 6) & 3)) * 6); data = (get_mixer_reg(0x44) >> 4) & 0xf; if (data > 7) data--; data2 = (get_mixer_reg(0x45) >> 4) & 0xf; if (data2 > 7) data2--; printf("Treble: Left: %3ddB Right: %3ddB\n", ((int)data - 7) * 2, ((int)data2 - 7) * 2); data = (get_mixer_reg(0x46) >> 4) & 0xf; if (data > 7) data--; data2 = (get_mixer_reg(0x47) >> 4) & 0xf; if (data2 > 7) data2--; printf("Bass: Left: %3ddB Right: %3ddB\n", ((int)data - 7) * 2, ((int)data2 - 7) * 2); data = get_mixer_reg(0x3c); printf("Output sources left: PCSpeaker Voice.L%s%s%s\n", (data & 1) ? " Mic" : "", (data & 4) ? " CD.L" : "", (data & 0x10) ? " Line.L" : ""); printf("Output sources right: PCSpeaker Voice.R%s%s%s\n", (data & 1) ? " Mic" : "", (data & 2) ? " CD.R" : "", (data & 8) ? " Line.R" : ""); data = get_mixer_reg(0x3d); printf("Input sources left: %s%s%s%s%s%s%s\n", (data & 1) ? " Mic" : "", (data & 2) ? " CD.R" : "", (data & 4) ? " CD.L" : "", (data & 8) ? " Line.R" : "", (data & 0x10) ? " Line.L" : "", (data & 0x20) ? " Midi.R" : "", (data & 0x40) ? " Midi.L" : ""); data = get_mixer_reg(0x3e); printf("Input sources right: %s%s%s%s%s%s%s\n", (data & 1) ? " Mic" : "", (data & 2) ? " CD.R" : "", (data & 4) ? " CD.L" : "", (data & 8) ? " Line.R" : "", (data & 0x10) ? " Line.L" : "", (data & 0x20) ? " Midi.R" : "", (data & 0x40) ? " Midi.L" : ""); } /* ---------------------------------------------------------------------- */ static int parse_ad_src(const char *cp) { if (!strcasecmp(cp, "line")) return 0; if (!strcasecmp(cp, "aux1")) return 1; if (!strcasecmp(cp, "mic")) return 2; if (!strcasecmp(cp, "dac")) return 3; return -1; } /* ---------------------------------------------------------------------- */ static int set_mixer_ad1848(int argc, char *argv[]) { unsigned int mask = 0; int olvll = 0, olvlr = 0; int isrcl = 0, isrcr = 0; int ilvll = 0, ilvlr = 0; unsigned int data; for (; argc >= 1; argc--, argv++) { if (!strncasecmp(argv[0], "ol=", 3)) { olvll = strtol(argv[0]+3, NULL, 0); mask |= 1; if (olvll < -46 || olvll > 0) { fprintf(stderr, "output level out of range " "-100..0dB\n"); return -1; } } else if (!strncasecmp(argv[0], "or=", 3)) { olvlr = strtol(argv[0]+3, NULL, 0); mask |= 2; if (olvlr < -46 || olvlr > 0) { fprintf(stderr, "output level out of range " "-100..0dB\n"); return -1; } } else if (!strncasecmp(argv[0], "o=", 2)) { olvll = olvlr = strtol(argv[0]+2, NULL, 0); mask |= 3; if (olvll < -46 || olvll > 0) { fprintf(stderr, "output level out of range " "-100..0dB\n"); return -1; } } else if (!strncasecmp(argv[0], "il=", 3)) { ilvll = strtol(argv[0]+3, NULL, 0); mask |= 4; if (ilvll < 0 || ilvll > 43) { fprintf(stderr, "input gain out of range " "0..43dB\n"); return -1; } } else if (!strncasecmp(argv[0], "ir=", 3)) { ilvlr = strtol(argv[0]+3, NULL, 0); mask |= 8; if (ilvll < 0 || ilvll > 43) { fprintf(stderr, "input gain out of range " "0..43dB\n"); return -1; } } else if (!strncasecmp(argv[0], "i=", 2)) { ilvll = ilvlr = strtol(argv[0]+2, NULL, 0); mask |= 12; if (ilvll < 0 || ilvll > 43) { fprintf(stderr, "input gain out of range " "0..43dB\n"); return -1; } } else if (!strncasecmp(argv[0], "sl=", 3)) { mask |= 16; isrcl = parse_ad_src(argv[0] + 3); if (isrcl < 0) { fprintf(stderr, "invalid input source, must " "be either line, aux1, mic or dac\n"); return -1; } } else if (!strncasecmp(argv[0], "sr=", 3)) { mask |= 32; isrcr = parse_ad_src(argv[0] + 3); if (isrcr < 0) { fprintf(stderr, "invalid input source, must " "be either line, aux1, mic or dac\n"); return -1; } } else if (!strncasecmp(argv[0], "s=", 2)) { mask |= 48; isrcl = isrcr = parse_ad_src(argv[0] + 2); if (isrcl < 0) { fprintf(stderr, "invalid input source, must " "be either line, aux1, mic or dac\n"); return -1; } } else { fprintf(stderr, "invalid parameter \"%s\"\n", argv[0]); return -1; } } data = get_mixer_reg(0x00); if (mask & 4) { data &= 0xc0; if (ilvll > 23) { data |= 0x20; ilvll -= 20; } data |= ilvll * 2 / 3; } if (mask & 16) data = (data & 0x3f) | (isrcl << 6); if ((data & 0xc0) != 0x80) data &= ~0x20; set_mixer_reg(0x00, data); data = get_mixer_reg(0x01); if (mask & 8) { data &= 0xc0; if (ilvlr > 23) { data |= 0x20; ilvlr -= 20; } data |= ilvlr * 2 / 3; } if (mask & 32) data = (data & 0x3f) | (isrcr << 6); if ((data & 0xc0) != 0x80) data &= ~0x20; set_mixer_reg(0x01, data); set_mixer_reg(0x02, 0x80); set_mixer_reg(0x03, 0x80); set_mixer_reg(0x04, 0x80); set_mixer_reg(0x05, 0x80); if (mask & 1) { if (olvll < -95) set_mixer_reg(0x06, 0x80); else set_mixer_reg(0x06, (olvll * (-2) / 3)); } if (mask & 2) { if (olvlr < -95) set_mixer_reg(0x07, 0x80); else set_mixer_reg(0x07, (olvlr * (-2) / 3)); } set_mixer_reg(0x0d, 0x00); return 0; } /* ---------------------------------------------------------------------- */ static int set_mixer_ct1335(int argc, char *argv[]) { unsigned int mask = 0; int olvl = 0; for (; argc >= 1; argc--, argv++) { if (!strncasecmp(argv[0], "o=", 2)) { olvl = strtol(argv[0]+2, NULL, 0); mask |= 1; if (olvl < -46 || olvl > 0) { fprintf(stderr, "output level out of range " "-46..0dB\n"); return -1; } } else { fprintf(stderr, "invalid parameter \"%s\"\n", argv[0]); return -1; } } if (mask & 1) set_mixer_reg(0x02, (((olvl * 7 / 46) + 7) << 1)); set_mixer_reg(0x06, 0x00); set_mixer_reg(0x08, 0x00); set_mixer_reg(0x0a, 0x06); return 0; } /* ---------------------------------------------------------------------- */ static int set_mixer_ct1345(int argc, char *argv[]) { unsigned int mask = 0; int olvll = 0, olvlr = 0; int isrc = 0; unsigned int data; for (; argc >= 1; argc--, argv++) { if (!strncasecmp(argv[0], "ol=", 3)) { olvll = strtol(argv[0]+3, NULL, 0); mask |= 1; if (olvll < -46 || olvll > 0) { fprintf(stderr, "output level out of range " "-46..0dB\n"); return -1; } } else if (!strncasecmp(argv[0], "or=", 3)) { olvlr = strtol(argv[0]+3, NULL, 0); mask |= 2; if (olvlr < -46 || olvlr > 0) { fprintf(stderr, "output level out of range " "-46..0dB\n"); return -1; } } else if (!strncasecmp(argv[0], "o=", 2)) { olvll = olvlr = strtol(argv[0]+2, NULL, 0); mask |= 3; if (olvll < -46 || olvll > 0) { fprintf(stderr, "output level out of range " "-46..0dB\n"); return -1; } } else if (!strncasecmp(argv[0], "s=", 2)) { mask |= 4; if (!strcasecmp(argv[0]+2, "mic")) isrc = 0; else if (!strcasecmp(argv[0]+2, "cd")) isrc = 1; else if (!strcasecmp(argv[0]+2, "line")) isrc = 3; else { fprintf(stderr, "invalid input source, must " "be either mic, cd or line\n"); return -1; } } else { fprintf(stderr, "invalid parameter \"%s\"\n", argv[0]); return -1; } } set_mixer_reg(0x04, 0xee); set_mixer_reg(0x0a, 0x00); if (mask & 4) data = (isrc & 3) << 1; else data = get_mixer_reg(0x0c) & 0x06; set_mixer_reg(0x0c, data | 0x20); set_mixer_reg(0x0e, 0x20); data = get_mixer_reg(0x22); if (mask & 1) data = (data & 0x0f) | (((olvll * 7 / 46) + 7) << 5); if (mask & 2) data = (data & 0xf0) | (((olvlr * 7 / 46) + 7) << 1); set_mixer_reg(0x22, data); set_mixer_reg(0x26, 0x00); set_mixer_reg(0x28, 0x00); set_mixer_reg(0x2e, 0x00); return 0; } /* ---------------------------------------------------------------------- */ static int set_mixer_ct1745(int argc, char *argv[]) { unsigned int mask = 0; int olvll = 0, olvlr = 0; int ilvll = 0, ilvlr = 0; unsigned int insrc = 0; unsigned int data; for (; argc >= 1; argc--, argv++) { if (!strncasecmp(argv[0], "ol=", 3)) { olvll = strtol(argv[0]+3, NULL, 0); mask |= 1; if (olvll < -62 || olvll > 18) { fprintf(stderr, "output level out of range " "-62..18dB\n"); return -1; } } else if (!strncasecmp(argv[0], "or=", 3)) { olvlr = strtol(argv[0]+3, NULL, 0); mask |= 2; if (olvlr < -62 || olvlr > 18) { fprintf(stderr, "output level out of range " "-62..18dB\n"); return -1; } } else if (!strncasecmp(argv[0], "o=", 2)) { olvll = olvlr = strtol(argv[0]+2, NULL, 0); mask |= 3; if (olvll < -62 || olvll > 18) { fprintf(stderr, "output level out of range " "-62..18dB\n"); return -1; } } else if (!strncasecmp(argv[0], "il=", 3)) { ilvll = strtol(argv[0]+3, NULL, 0); mask |= 4; if (ilvll < -62 || ilvll > 18) { fprintf(stderr, "input gain out of range " "-62..18dB\n"); return -1; } } else if (!strncasecmp(argv[0], "ir=", 3)) { ilvlr = strtol(argv[0]+3, NULL, 0); mask |= 8; if (ilvll < -62 || ilvll > 18) { fprintf(stderr, "input gain out of range " "-62..18dB\n"); return -1; } } else if (!strncasecmp(argv[0], "i=", 2)) { ilvll = ilvlr = strtol(argv[0]+2, NULL, 0); mask |= 12; if (ilvll < -62 || ilvll > 18) { fprintf(stderr, "input gain out of range " "-62..18dB\n"); return -1; } } else if (!strncasecmp(argv[0], "s=", 2)) { mask |= 16; if (!strcasecmp(argv[0]+2, "mic")) insrc |= 1; else if (!strcasecmp(argv[0]+2, "cd.r")) insrc |= 2; else if (!strcasecmp(argv[0]+2, "cd.l")) insrc |= 4; else if (!strcasecmp(argv[0]+2, "cd")) insrc |= 6; else if (!strcasecmp(argv[0]+2, "line.r")) insrc |= 0x08; else if (!strcasecmp(argv[0]+2, "line.l")) insrc |= 0x10; else if (!strcasecmp(argv[0]+2, "line")) insrc |= 0x18; else if (!strcasecmp(argv[0]+2, "midi.r")) insrc |= 0x20; else if (!strcasecmp(argv[0]+2, "midi.l")) insrc |= 0x40; else if (!strcasecmp(argv[0]+2, "midi")) insrc |= 0x60; else { fprintf(stderr, "invalid input source, must " "be either line, cd, mic or midi\n"); return -1; } } else { fprintf(stderr, "invalid parameter \"%s\"\n", argv[0]); return -1; } } if (mask & 3) { set_mixer_reg(0x3c, 0); /* output src: only voice and pcspk */ set_mixer_reg(0x44, 4<<4); /* treble.l: -6dB */ set_mixer_reg(0x45, 4<<4); /* treble.r: -6dB */ set_mixer_reg(0x46, 6<<4); /* bass.l: -2dB */ set_mixer_reg(0x47, 6<<4); /* bass.r: -2dB */ set_mixer_reg(0x3b, 0); /* PC speaker vol -18dB */ } if (mask & 12) set_mixer_reg(0x43, 1); /* mic: agc off, fixed 20dB gain */ if (mask & 1) { data = 0; while (olvll > 0) { olvll -= 6; data += 0x40; } set_mixer_reg(0x41, data); /* output gain */ set_mixer_reg(0x30, 0xf8); /* master vol */ set_mixer_reg(0x32, (olvll / 2 + 31) << 3); /* voice vol */ } if (mask & 2) { data = 0; while (olvlr > 0) { olvlr -= 6; data += 0x40; } set_mixer_reg(0x42, data); /* output gain */ set_mixer_reg(0x31, 0xf8); /* master vol */ set_mixer_reg(0x33, (olvlr / 2 + 31) << 3); /* voice vol */ } if (mask & 4) { data = 0; while (ilvll > 0) { ilvll -= 6; data += 0x40; } set_mixer_reg(0x3f, data); /* input gain */ data = (ilvll / 2 + 31) << 3; set_mixer_reg(0x34, data); /* midi vol */ set_mixer_reg(0x36, data); /* cd vol */ set_mixer_reg(0x38, data); /* line vol */ set_mixer_reg(0x3a, data); /* mic vol */ } if (mask & 8) { data = 0; while (ilvlr > 0) { ilvlr -= 6; data += 0x40; } set_mixer_reg(0x40, data); /* input gain */ data = (ilvlr / 2 + 31) << 3; set_mixer_reg(0x35, data); /* midi vol */ set_mixer_reg(0x37, data); /* cd vol */ set_mixer_reg(0x39, data); /* line vol */ set_mixer_reg(0x3a, data); /* mic vol */ } if (mask & 16) { set_mixer_reg(0x3d, insrc); /* input sources */ set_mixer_reg(0x3e, insrc); /* input sources */ } return 0; } /* ---------------------------------------------------------------------- */ static const char *usage_str = "[-i smif]\n\n"; int main(int argc, char *argv[]) { int c; struct sm_mixer_data mixdat; progname = *argv; printf("%s: Version " FULL_VER "; (C) 1996-1997 by Thomas Sailer HB9JNX/AE4WA\n", *argv); hdrvc_args(&argc, argv, "sm0"); while ((c = getopt(argc, argv, "")) != -1) { switch (c) { default: printf("usage: %s %s", *argv, usage_str); exit(-1); } } hdrvc_init(); mixdat.reg = 0; if (do_mix_ioctl(SMCTL_GETMIXER, &mixdat) < 0) { perror("do_mix_ioctl: SMCTL_GETMIXER"); exit(1); } mixdevice = mixdat.mixer_type; switch (mixdevice) { case SM_MIXER_INVALID: printf("invalid mixer device\n"); exit(1); case SM_MIXER_AD1848: if (optind < argc) set_mixer_ad1848(argc - optind, argv + optind); display_mixer_ad1848(); break; case SM_MIXER_CRYSTAL: if (optind < argc) set_mixer_ad1848(argc - optind, argv + optind); display_mixer_cs423x(); break; case SM_MIXER_CT1335: if (optind < argc) set_mixer_ct1335(argc - optind, argv + optind); display_mixer_ct1335(); break; case SM_MIXER_CT1345: if (optind < argc) set_mixer_ct1345(argc - optind, argv + optind); display_mixer_ct1345(); break; case SM_MIXER_CT1745: if (optind < argc) set_mixer_ct1745(argc - optind, argv + optind); display_mixer_ct1745(); break; default: printf("unknown mixer device %d\n", mixdevice); exit(1); } exit(0); } /* ---------------------------------------------------------------------- */ hdlcutil/soundmodem.9000066400000000000000000000053701442776067000151540ustar00rootroot00000000000000.\" Copyright 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) .\" May be distributed under the GNU General Public License .\" " .TH SOUNDMODEM 9 "27 April 2008" "Linux 2.1.x" "Kernel Reference Guide" .SH NAME soundmodem \- amateur (AX.25) packet radio network driver for soundcards .SH SYNOPSIS .nf .B #include .B #include .fi .SH DESCRIPTION The driver currently supports both 1200 baud AFSK and 9600 baud FSK (G3RUH compatible) using a standard SoundBlaster compatible or WindowsSoundSystem compatible soundcard. The whole decoding is done in software, so you definitely do not want to use it on a 386SX class machine. .SH "KEYING THE TRANSMITTER" Soundcards do not have a DC coupled output that could serve as a PTT signal. So there are basically for possibilities for obtaining a PTT signal. Sample schematic diagrams can be found on \fIhttp://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html\fP. .SS "VOX circuitry" A simple VOX circuitry that detects output signals at the output of the soundcard can be used, especially as it can be built with a single transistor plus a few passive components and typical soundcards have strong output signals. .SS "Parallel Port" A parallel port line can also be used to signal PTT. If selected, the PTT signal is output on the DATA0 line and DCD is output on the DATA1 line. .SS "Serial Port" A standard serial port (8250, 16450, 16550) can also be used to output PTT. PTT is output on RTS and TxD, while DCD is output on DTR. .SS "MPU401 MIDI Port" The MIDI port is basically an asynchronous serial interface and thus cannot output a straight DC level, however it may be used if connected through a retriggerable monoflop with about 15ms pulse duration. Note that only newer SoundBlaster models have a genuine MPU401 MIDI port. The older SB MIDI port cannot be used. .SH "IOCTL CALLS" The \fBioctl\fP calls follow the implementation in the \fIhdlcdrv\fP. .TP .B SMCTL_GETMODEMTYPE returns the modem type (i.e. \fISBC1200\fP, \fISBC9600\fP, \fIWSS1200\fP or \fIWSS9600\fP) .TP .B SMCTL_SETMODEMTYPE sets the modem type. Only superuser can do this. .TP .B SMCTL_GETMIXER returns the mixer type and the contents of the specified mixer register. .TP .B SMCTL_SETMIXER sets the specified mixer register, if the specified mixer type matches the mixer type of the soundcard. Only superuser can do this. .TP .B SMCTL_DIAGNOSE returns the contents of the diagnose buffer, which is used by \fIsmdiag\fP to display the eye and oscilloscope diagrams. .TP .B SMCTL_GETDEBUG return some debugging values. Not always available. .SH "SEE ALSO" .BR baycom " (9), " soundmodem " (9)," smdiag " (8)," smmixer " (9)," linux/drivers/net/hdlcdrv.c, .SH AUTHOR soundmodem was written by Thomas Sailer, HB9JNX/AE4WA, (t.sailer@alumni.ethz.ch). hdlcutil/soundmodem.h000066400000000000000000000034601442776067000152310ustar00rootroot00000000000000/* * The Linux soundcard driver for 1200 baud and 9600 baud packet radio * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA */ #ifndef _SOUNDMODEM_H #define _SOUNDMODEM_H /* -------------------------------------------------------------------- */ /* * structs for the IOCTL commands */ struct sm_debug_data { unsigned int int_rate; unsigned int mod_cycles; unsigned int demod_cycles; unsigned int dma_residue; }; struct sm_diag_data { unsigned int mode; unsigned int flags; unsigned int samplesperbit; unsigned int datalen; short *data; }; struct sm_mixer_data { unsigned int mixer_type; unsigned int sample_rate; unsigned int bit_rate; unsigned int reg; unsigned int data; }; struct sm_config { int hardware; int mode; }; struct sm_ioctl { int cmd; union { struct sm_config cfg; struct sm_diag_data diag; struct sm_mixer_data mix; struct sm_debug_data dbg; } data; }; /* -------------------------------------------------------------------- */ /* * diagnose modes */ #define SM_DIAGMODE_OFF 0 #define SM_DIAGMODE_INPUT 1 #define SM_DIAGMODE_DEMOD 2 #define SM_DIAGMODE_CONSTELLATION 3 /* * diagnose flags */ #define SM_DIAGFLAG_DCDGATE (1<<0) #define SM_DIAGFLAG_VALID (1<<1) /* * mixer types */ #define SM_MIXER_INVALID 0 #define SM_MIXER_AD1848 0x10 #define SM_MIXER_CRYSTAL 0x11 #define SM_MIXER_CT1335 0x20 #define SM_MIXER_CT1345 0x21 #define SM_MIXER_CT1745 0x22 /* * ioctl values */ #define SMCTL_DIAGNOSE 0x82 #define SMCTL_GETMIXER 0x83 #define SMCTL_SETMIXER 0x84 #define SMCTL_GETDEBUG 0x85 /* -------------------------------------------------------------------- */ #endif /* _SOUNDMODEM_H */ /* --------------------------------------------------------------------- */ hdlcutil/usersmdiag.h000066400000000000000000000070631442776067000152250ustar00rootroot00000000000000/*****************************************************************************/ /* * usersmdiag.h -- Diagnostics interface. * * Copyright (C) 1996 Thomas Sailer (t.sailer@alumni.ethz.ch) * Swiss Federal Institute of Technology (ETH), Electronics Lab * * 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. * * * This is the Linux realtime sound output driver */ /*****************************************************************************/ #ifndef _USERSMDIAG_H #define _USERSMDIAG_H /* --------------------------------------------------------------------- */ #define USERSM_KEY_PROJ '0' /* --------------------------------------------------------------------- */ #define USERSM_CMD_REQ_CHACCESS_PAR 0x00020 #define USERSM_CMD_ACK_CHACCESS_PAR 0x10021 #define USERSM_CMD_SET_CHACCESS_PAR 0x00022 #define USERSM_CMD_CALIBRATE 0x00030 #define USERSM_CMD_REQ_CHANNELSTATE 0x00031 #define USERSM_CMD_ACK_CHANNELSTATE 0x10032 #define USERSM_CMD_REQ_DIAG 0x00040 #define USERSM_CMD_ACK_DIAG 0x10041 #define USERSM_CMD_REQ_DRVNAME 0x00050 #define USERSM_CMD_ACK_DRVNAME 0x10051 #define USERSM_CMD_REQ_DRVMODE 0x00052 #define USERSM_CMD_ACK_DRVMODE 0x10053 /* * diagnose modes; same as in */ #define USERSM_DIAGMODE_OFF 0 #define USERSM_DIAGMODE_INPUT 1 #define USERSM_DIAGMODE_DEMOD 2 #define USERSM_DIAGMODE_CONSTELLATION 3 /* * diagnose flags; same as in */ #define USERSM_DIAGFLAG_DCDGATE (1<<0) #define USERSM_DIAGFLAG_VALID (1<<1) /* --------------------------------------------------------------------- */ #define DIAGDATALEN 64 /* --------------------------------------------------------------------- */ struct usersmmsg { struct usersm_hdr { long type; unsigned int channel; } hdr; union { struct usersm_chaccess_par { int tx_delay; /* the transmitter keyup delay in 10ms units */ int tx_tail; /* the transmitter keyoff delay in 10ms units */ int slottime; /* the slottime in 10ms; usually 10 = 100ms */ int ppersist; /* the p-persistence 0..255 */ int fulldup; /* some driver do not support full duplex, setting */ /* this just makes them send even if DCD is on */ } cp; int calib; struct usersm_channel_state { int ptt; int dcd; int ptt_keyed; unsigned long tx_packets; unsigned long tx_errors; unsigned long rx_packets; unsigned long rx_errors; } cs; struct usersm_diag { unsigned int mode; unsigned int flags; unsigned int samplesperbit; unsigned int datalen; } diag; struct usersm_diag_out { struct usersm_diag diag; short samples[DIAGDATALEN]; } diag_out; char by[0]; } data; }; /* --------------------------------------------------------------------- */ #endif /* _USERSMDIAG_H */ kiss/000077500000000000000000000000001442776067000120445ustar00rootroot00000000000000kiss/.gitignore000066400000000000000000000001141442776067000140300ustar00rootroot00000000000000*.o .deps Makefile Makefile.in kissattach kissnetd kissparms mkiss net2kiss kiss/Makefile.am000066400000000000000000000007721442776067000141060ustar00rootroot00000000000000 installconf: sbin_PROGRAMS = kissattach kissnetd kissparms mkiss net2kiss LDADD= $(AX25_LIB) net2kiss_LDADD = $(UTIL_LIB) dist_man_MANS = kissattach.8 spattach.8 kissnetd.8 kissparms.8 mkiss.8 \ net2kiss.8 AM_CPPFLAGS = -D_GNU_SOURCE \ -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" AX25_SYSCONFDIR=${sysconfdir}/ax25/ AX25_LOCALSTATEDIR=${localstatedir}/ax25/ install-exec-hook: (cd $(DESTDIR)$(sbindir) ; ln -sf kissattach spattach) kiss/kissattach.8000066400000000000000000000052161442776067000142770ustar00rootroot00000000000000.TH KISSATTACH 8 "28 March 2019" Linux "Linux System Managers Manual" .SH NAME kissattach, spattach \- Attach a KISS or 6PACK interface .SH SYNOPSIS .B kissattach [-b] [-6] [-l] [-m mtu] [-v] [-i inetaddr] tty port [inetaddr] .sp .B spattach [-b] [-6] [-l] [-m mtu] [-v] [-i inetaddr] tty port [inetaddr] .SH DESCRIPTION .LP Attach a KISS or a 6PACK interface to what is normally a tty line connected to a TNC in KISS or 6PACK mode. This program will turn itself into a background process. To down an interface send its attach process a SIGTERM. .LP .B Kissattach takes many of the parameters for the port from the axports(5) file. If the speed parameter in the file is not equal to zero then it is used for the serial port speed, a zero value means that no speed is set. The paclen parameter is used for the device mtu unless overridden by a value on the command line. .LP The tty argument will typically be that of a serial port with a KISS or 6PACK TNC attached, although it could be a pseudo tty or a KISS port emulator such as an SCC card. Kissattach supports BSD-style pseudo-terminals as well as the Unix98 pty's. If the tty argument is "/dev/ptmx", then Unix98 behaviour will automatically take effekt. With Unix98 pty's, the slave tty name could not be forseen. That's why kissattach will print the corresponding slave pty name as a separate line on stdout. .LP The port argument is the name of a port as given in the axports(5) file. .LP The optional inetaddr argument is the IP address of the new interface. Some time it was mandatory argument (although due to historical reasons this restriction is lifted if the old -i option is used). But there's really not a need for the interface to have an IP address assigned to. .SH OPTIONS .TP 16 .BI "\-6" Use the 6PACK line discipline instead of KISS. This is the default if the program is called as spattach so the option is a no-op for spattach. .TP 16 .BI "\-i inetaddr" Set the internet address of the interface. This address may either be a dotted decimal address or a host name. This option is now depreciated and the program will complain about it, though still work. .TP 16 .BI \-l Log messages to the system log, the default is not to. .TP 16 .BI \-b Allow broadcasts on the interface (default no - because for e.g. samba broadcasts are a pain..) .TP 16 .BI "\-m mtu" Sets the mtu of the interface. If this value is not given then the value is taken from the paclen parameter in axports. .TP 16 .BI \-v Display the version. .SH "SEE ALSO" .BR kill (1), .BR stty (1), .BR ax25 (4), .BR axports (5), .BR axparms (8), .BR ifconfig (8). .SH AUTHOR .nf Alan Cox GW4PTS .br Jonathan Naylor G4KLX .fi kiss/kissattach.c000066400000000000000000000202441442776067000143500ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #ifndef N_6PACK #define N_6PACK 7 /* This is valid for all architectures in 2.2.x */ #endif static char *callsign; static int speed; static int mtu; static int logging = FALSE; static char *progname; static char *kttyname; static char *portname; static char *inetaddr; static int allow_broadcast; static int i_am_unix98_pty_master; /* unix98 ptmx support */ static char *kiss_basename(char *s) { char *p = strrchr(s, '/'); return p ? p + 1 : s; } static void terminate(int sig) { if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } if (!i_am_unix98_pty_master) tty_unlock(kttyname); exit(0); } static int readconfig(char *port) { FILE *fp; char buffer[90], *s; int n = 0; fp = fopen(CONF_AXPORTS_FILE, "r"); if (fp == NULL) { fprintf(stderr, "%s: cannot open axports file %s\n", progname, CONF_AXPORTS_FILE); return FALSE; } while (fgets(buffer, 90, fp) != NULL) { n++; s = strchr(buffer, '\n'); if (s != NULL) *s = '\0'; if (*buffer == 0 || *buffer == '#') continue; s = strtok(buffer, " \t\r\n"); if (s == NULL) { fprintf(stderr, "%s: unable to parse line %d of the axports file\n", progname, n); return FALSE; } if (strcmp(s, port) != 0) continue; s = strtok(NULL, " \t\r\n"); if (s == NULL) { fprintf(stderr, "%s: unable to parse line %d of the axports file\n", progname, n); return FALSE; } callsign = strdup(s); s = strtok(NULL, " \t\r\n"); if (s == NULL) { fprintf(stderr, "%s: unable to parse line %d of the axports file\n", progname, n); return FALSE; } speed = atoi(s); s = strtok(NULL, " \t\r\n"); if (s == NULL) { fprintf(stderr, "%s: unable to parse line %d of the axports file\n", progname, n); return FALSE; } if (mtu == 0) { mtu = atoi(s); if (mtu <= 0) { fprintf(stderr, "%s: invalid paclen setting\n", progname); return FALSE; } } fclose(fp); return TRUE; } fclose(fp); fprintf(stderr, "%s: cannot find port %s in axports\n", progname, port); return FALSE; } static int setifcall(int fd, char *name) { char call[7]; if (ax25_aton_entry(name, call) == -1) return FALSE; if (ioctl(fd, SIOCSIFHWADDR, call) != 0) { close(fd); fprintf(stderr, "%s: ", progname); perror("SIOCSIFHWADDR"); return FALSE; } return TRUE; } static int startiface(char *dev, struct hostent *hp) { struct ifreq ifr; int fd; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { fprintf(stderr, "%s: ", progname); perror("socket"); return FALSE; } strcpy(ifr.ifr_name, dev); if (hp != NULL) { ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_data[0] = 0; ifr.ifr_addr.sa_data[1] = 0; ifr.ifr_addr.sa_data[2] = hp->h_addr_list[0][0]; ifr.ifr_addr.sa_data[3] = hp->h_addr_list[0][1]; ifr.ifr_addr.sa_data[4] = hp->h_addr_list[0][2]; ifr.ifr_addr.sa_data[5] = hp->h_addr_list[0][3]; ifr.ifr_addr.sa_data[6] = 0; if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { fprintf(stderr, "%s: ", progname); perror("SIOCSIFADDR"); return FALSE; } } ifr.ifr_mtu = mtu; if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) { fprintf(stderr, "%s: ", progname); perror("SIOCSIFMTU"); return FALSE; } if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { fprintf(stderr, "%s: ", progname); perror("SIOCGIFFLAGS"); return FALSE; } ifr.ifr_flags &= IFF_NOARP; ifr.ifr_flags |= IFF_UP; ifr.ifr_flags |= IFF_RUNNING; if (allow_broadcast) ifr.ifr_flags |= IFF_BROADCAST; /* samba broadcasts are a pain.. */ else ifr.ifr_flags &= ~(IFF_BROADCAST); /* samba broadcasts are a pain.. */ if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { fprintf(stderr, "%s: ", progname); perror("SIOCSIFFLAGS"); return FALSE; } close(fd); return TRUE; } static void usage(void) { fprintf(stderr, "usage: %s [-b] [-l] [-m mtu] [-v] tty port [inetaddr]\n", progname); } int main(int argc, char *argv[]) { int fd; int disc = N_AX25; char dev[64]; int v = 4; char *namepts = NULL; /* name of the unix98 pts slave, which * the client has to use */ struct hostent *hp = NULL; progname = kiss_basename(argv[0]); if (!strcmp(progname, "spattach")) disc = N_6PACK; while ((fd = getopt(argc, argv, "b6i:lm:v")) != -1) { switch (fd) { case '6': disc = N_6PACK; break; case 'b': allow_broadcast = 1; break; case 'i': fprintf(stderr, "%s: -i flag depreciated, use new command line format instead.\n", progname); inetaddr = optarg; break; case 'l': logging = TRUE; break; case 'm': mtu = atoi(optarg); if (mtu <= 0) { fprintf(stderr, "%s: invalid mtu size - %s\n", progname, optarg); return 1; } break; case 'v': printf("%s: %s\n", progname, FULL_VER); return 0; case ':': case '?': usage(); return 1; } } #ifdef notdef if ((argc - optind) != 3 && ((argc - optind) != 2 || !inetaddr)) { #else if ((argc - optind) < 2) { #endif usage(); return 1; } kttyname = argv[optind++]; portname = argv[optind++]; if (argc-1 >= optind && !inetaddr) inetaddr = argv[optind]; if (!strcmp("/dev/ptmx", kttyname)) i_am_unix98_pty_master = 1; if (!i_am_unix98_pty_master) { if (tty_is_locked(kttyname)) { fprintf(stderr, "%s: device %s already in use\n", progname, kttyname); return 1; } } if (!readconfig(portname)) return 1; if (inetaddr && (hp = gethostbyname(inetaddr)) == NULL) { fprintf(stderr, "%s: invalid internet name/address - %s\n", progname, inetaddr); return 1; } fd = open(kttyname, O_RDONLY | O_NONBLOCK); if (fd == -1) { if (errno == ENOENT) { fprintf(stderr, "%s: Cannot find serial device %s, no such file or directory.\n", progname, kttyname); } else { fprintf(stderr, "%s: %s: ", progname, kttyname); perror("open"); } return 1; } if (i_am_unix98_pty_master) { /* get name of pts-device */ namepts = ptsname(fd); if (namepts == NULL) { fprintf(stderr, "%s: Cannot get name of pts-device.\n", progname); return 1; } /* unlock pts-device */ if (unlockpt(fd) == -1) { fprintf(stderr, "%s: Cannot unlock pts-device %s\n", progname, namepts); return 1; } } if (speed != 0 && !tty_speed(fd, speed)) return 1; if (ioctl(fd, TIOCSETD, &disc) == -1) { fprintf(stderr, "%s: Error setting line discipline: ", progname); perror("TIOCSETD"); fprintf(stderr, "Are you sure you have enabled %s support in the kernel\n", disc == N_AX25 ? "MKISS" : "6PACK"); fprintf(stderr, "or, if you made it a module, that the module is loaded?\n"); return 1; } if (ioctl(fd, SIOCGIFNAME, dev) == -1) { fprintf(stderr, "%s: ", progname); perror("SIOCGIFNAME"); return 1; } if (!setifcall(fd, callsign)) return 1; /* Now set the encapsulation */ if (ioctl(fd, SIOCSIFENCAP, &v) == -1) { fprintf(stderr, "%s: ", progname); perror("SIOCSIFENCAP"); return 1; } /* ax25 ifaces should not really need to have an IP address assigned to */ if (!startiface(dev, hp)) return 1; printf("AX.25 port %s bound to device %s\n", portname, dev); if (i_am_unix98_pty_master) { /* Users await the slave pty to be referenced in the 3d line */ printf("Awaiting client connects on\n%s\n", namepts); } if (logging) { openlog(progname, LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "AX.25 port %s bound to device %s%s%s\n", portname, dev, (i_am_unix98_pty_master ? " with slave pty " : ""), (i_am_unix98_pty_master ? namepts : "")); } signal(SIGHUP, SIG_IGN); signal(SIGTERM, terminate); /* * Become a daemon if we can. */ if (!daemon_start(FALSE)) { fprintf(stderr, "%s: cannot become a daemon\n", progname); return 1; } if (!i_am_unix98_pty_master) { if (!tty_lock(kttyname)) return 1; } fflush(stdout); fflush(stderr); close(0); close(1); close(2); while (1) sleep(10000); /* NOT REACHED */ return 0; } kiss/kissnetd.8000066400000000000000000000021121442776067000137550ustar00rootroot00000000000000.TH KISSNETD 8 "31 March 2010" Linux "Linux System Managers Manual" .SH NAME kissnetd \- Create a virtual network. .SH SYNOPSIS .B kissnetd [-f size] [-v] [-p | tty... ] .SH DESCRIPTION .LP .B Kissnetd allows the creation of a virtual network of AX.25 systems that use the KISS protocol. Each tty named on the command line is opened and any KISS frames received on tty is copied to the other ttys. This allows a number of AX.25 systems to share the same packets. .SH OPTIONS .TP 10 .BI "\-p num" Automatically allocate num Unix98 slave pty's via /dev/ptmx. These are written to stdout and could be parsed by your startup scripts. "kissnetd -p 3" is a comfortable alternative to "kissnetd /dev/ptmx /dev/ptmx /dev/ptmx". .TP 10 .BI "\-f size" This sets the maximum KISS frame size that the program will handle. The default is 512 bytes which will be adequate under most circumstances. .TP 10 .BI \-v Enables verbose mode, tracing of data passed is sent to standard output. .SH "SEE ALSO" .BR kissattach (8), .BR kissparms (8), .BR mkiss (8). .SH AUTHOR Frederic Rible F1OAT kiss/kissnetd.c000066400000000000000000000230341442776067000140360ustar00rootroot00000000000000/* * kissnetd.c : Simple kiss broadcast daemon between several * pseudo-tty devices. * Each kiss frame received on one pty is broadcasted * to each other one. * * ATEPRA FPAC/Linux Project * * F1OAT 960804 - Frederic RIBLE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int VerboseMode; static int MaxFrameSize = 512; #define REOPEN_TIMEOUT 30 /* try tio reopen every 10 s */ struct PortDescriptor { char Name[PATH_MAX]; int Fd; unsigned char *FrameBuffer; int BufferIndex; time_t TimeLastOpen; char namepts[PATH_MAX]; /* name of the unix98 pts slaves, which * the client has to use */ int is_active; }; static struct PortDescriptor *PortList[FD_SETSIZE]; static int NbPort; static void Usage(void) { fprintf(stderr, "\nUsage : kissnetd [-v] [-f size] [-p num | /dev/pty?? [/dev/pty??]* ]\n"); fprintf(stderr, " -v : Verbose mode, trace on stdout\n"); fprintf(stderr, " -f size : Set max frame size to size bytes (default 512)\n"); fprintf(stderr, " -p num : Number of /dev/ptmx-master-devices has to open\n"); exit(1); } static void Banner(int Small) { if (Small) { printf("kissnetd V %s by Frederic RIBLE F1OAT - ATEPRA FPAC/Linux Project\n", FULL_VER); } else { printf("****************************************\n"); printf("* Network broadcast between kiss ports *\n"); printf("* ATEPRA FPAC/Linux Project *\n"); printf("****************************************\n"); printf("* %*skissnetd Version %s%*s*\n", (int) (10 - strlen(FULL_VER) / 2), "", FULL_VER, (int) (10 - (strlen(FULL_VER) + 1)) / 2, ""); printf("* by Frederic RIBLE F1OAT *\n"); printf("****************************************\n"); } } static void NewPort(char *Name) { struct PortDescriptor *MyPort; if (VerboseMode) { printf("Opening port %s\n", Name); } if (NbPort == FD_SETSIZE) { fprintf(stderr, "Cannot handle %s : too many ports\n", Name); exit(1); } MyPort = calloc(1, sizeof(struct PortDescriptor)); if (MyPort) MyPort->FrameBuffer = calloc(sizeof (unsigned char), MaxFrameSize); if (!MyPort || !MyPort->FrameBuffer) { perror("cannot allocate port descriptor"); exit(1); } strncpy(MyPort->Name, Name, PATH_MAX-1); MyPort->Name[PATH_MAX-1] = '\0'; MyPort->Fd = -1; MyPort->FrameBuffer[0] = 0xC0; MyPort->BufferIndex = 1; MyPort->namepts [0] = '\0'; MyPort->is_active = 0; PortList[NbPort++] = MyPort; } static void ReopenPort(int PortNumber) { char MyString[28 + PATH_MAX]; PortList[PortNumber]->TimeLastOpen = time(NULL); if (VerboseMode) { printf("Reopening port %d\n", PortNumber); } if (PortList[PortNumber]->namepts[0] == '\0') { syslog(LOG_WARNING, "kissnetd : Opening port %s\n", PortList[PortNumber]->Name); PortList[PortNumber]->Fd = open(PortList[PortNumber]->Name, O_RDWR | O_NONBLOCK); if (PortList[PortNumber]->Fd < 0) { syslog(LOG_WARNING, "kissnetd : Error opening port %s : %s\n", PortList[PortNumber]->Name, strerror(errno)); if (VerboseMode) { sprintf(MyString, "cannot reopen %s", PortList[PortNumber]->Name); perror(MyString); } return; } PortList[PortNumber]->is_active = 1; if (!strcmp(PortList[PortNumber]->Name, "/dev/ptmx")) { char *npts; /* get name of pts-device */ npts = ptsname(PortList[PortNumber]->Fd); if (npts == NULL) { sprintf(MyString, "Cannot get name of pts-device.\n"); syslog(LOG_WARNING, "kissnetd : Cannot get name of pts-device\n"); exit(1); } strncpy(PortList[PortNumber]->namepts, npts, PATH_MAX-1); PortList[PortNumber]->namepts[PATH_MAX-1] = '\0'; /* unlock pts-device */ if (unlockpt(PortList[PortNumber]->Fd) == -1) { sprintf(MyString, "Cannot unlock pts-device %s\n", PortList[PortNumber]->namepts); syslog(LOG_WARNING, "kissnetd : Cannot unlock pts-device %s\n", PortList[PortNumber]->namepts); exit(1); } syslog(LOG_WARNING, "kissnetd : Using /dev/ptmx with slave pty %s\n", PortList[PortNumber]->namepts); } } else { if (PortList[PortNumber]->Fd == -1) { syslog(LOG_WARNING, "kissnetd : Cannot reopen port ptmx (slave %s) : not supported by ptmx-device\n", PortList[PortNumber]->namepts); if (VerboseMode) { sprintf(MyString, "cannot reopen ptmx (slave %s).", PortList[PortNumber]->namepts); perror(MyString); } return; } syslog(LOG_WARNING, "kissnetd : Trying to poll port ptmx (slave %s).\n", PortList[PortNumber]->namepts); PortList[PortNumber]->is_active = 1; } } static void TickReopen(void) { int i; static int wrote_info; time_t CurrentTime = time(NULL); for (i=0; iFd >= 0 && PortList[i]->is_active == 1) continue; if ( (CurrentTime - PortList[i]->TimeLastOpen) > REOPEN_TIMEOUT ) ReopenPort(i); } if (!wrote_info) { for (i=0; inamepts[0] != '\0') { if (wrote_info == 0) printf("\nAwaiting client connects on:\n"); else printf(" "); printf("%s", PortList[i]->namepts); wrote_info = 1; } } if (wrote_info > 0) { printf("\n"); if (!VerboseMode) { fflush(stdout); fflush(stderr); close(0); close(1); close(2); } } } } static void Broadcast(int InputPort) { int i; int rc; /* Broadcast only info frames */ if (PortList[InputPort]->FrameBuffer[1] != 0x00 && \ PortList[InputPort]->FrameBuffer[1] != 0x20 && \ PortList[InputPort]->FrameBuffer[1] != 0x80) return; for (i=0; iFd < 0 || PortList[i]->is_active == 0) continue; again: rc = write(PortList[i]->Fd, PortList[InputPort]->FrameBuffer+offset, PortList[InputPort]->BufferIndex-offset); if (rc < 0) { if (errno == EAGAIN) { if (PortList[i]->namepts[0] == '\0') syslog(LOG_WARNING, "kissnetd : write buffer full on port %s. dropping frame. %s", PortList[i]->Name, strerror(errno)); else syslog(LOG_WARNING, "kissnetd : write buffer full on ptmx port %s. dropping frame. %s", PortList[i]->namepts, strerror(errno)); continue; } if (PortList[i]->namepts[0] == '\0') syslog(LOG_WARNING, "kissnetd : Error writing to port %s : %s\n", PortList[i]->Name, strerror(errno)); else syslog(LOG_WARNING, "kissnetd : Error writing to port ptmx (slave %s) : %s\n", PortList[i]->namepts, strerror(errno)); if (VerboseMode) perror("write"); PortList[i]->is_active = 0; if (PortList[i]->namepts[0] == '\0') { close(PortList[i]->Fd); PortList[i]->Fd = -1; } continue; } if (VerboseMode) { printf("Sending %d bytes on port %d : rc=%d\n", PortList[InputPort]->BufferIndex, i, rc); } if (rc < PortList[InputPort]->BufferIndex-offset) { offset += rc; goto again; } } } static void ProcessInput(int PortNumber) { static unsigned char MyBuffer[2048]; int Length; int i; struct PortDescriptor *MyPort = PortList[PortNumber]; Length = read(MyPort->Fd, MyBuffer, sizeof(MyBuffer)); if (VerboseMode) { printf("Read port %d : rc=%d\n", PortNumber, Length); } if (!Length) return; if (Length < 0) { if (errno == EAGAIN) return; if (MyPort->namepts[0] == '\0') syslog(LOG_WARNING, "kissnetd : Error reading from port %s : %s\n", PortList[PortNumber]->Name, strerror(errno)); else syslog(LOG_WARNING, "kissnetd : Error reading from port ptmx (slave %s) : %s\n", PortList[PortNumber]->namepts, strerror(errno)); if (VerboseMode) perror("read"); MyPort->is_active = 0; if (MyPort->namepts[0] == '\0') { close(MyPort->Fd); MyPort->Fd = -1; } return; } for (i=0; iBufferIndex == MaxFrameSize) { if (MyBuffer[i] == 0xC0) { if (VerboseMode) printf("Drop frame too long\n"); MyPort->BufferIndex = 1; } } else { MyPort->FrameBuffer[MyPort->BufferIndex++] = MyBuffer[i]; if (MyBuffer[i] == 0xC0) { Broadcast(PortNumber); MyPort->BufferIndex = 1; } } } } static void ProcessPortList(void) { static fd_set MyFdSet; int i, rc; struct timeval Timeout; Timeout.tv_sec = 1; Timeout.tv_usec = 0; FD_ZERO(&MyFdSet); for (i=0; iFd >= 0 && PortList[i]->is_active) FD_SET(PortList[i]->Fd, &MyFdSet); } rc = select(FD_SETSIZE, &MyFdSet, NULL, NULL, &Timeout); if (VerboseMode) printf("select : rc=%d\n", rc); if (!rc ) TickReopen(); if (rc > 0) { for (i=0; iFd < 0) continue; if (FD_ISSET(PortList[i]->Fd, &MyFdSet)) { ProcessInput(i); rc--; } } } } static void ProcessArgv(int argc, char *argv[]) { int opt; int i=0; int ptmxdevices = 0; while ((opt = getopt(argc, argv, "vf:p:")) != -1) { switch (opt) { case 'v': VerboseMode = 1; break; case 'f': MaxFrameSize = atoi(argv[optind]); break; case 'p': ptmxdevices = atoi(optarg); if (ptmxdevices < 1) { fprintf(stderr, "error: too many devices\n"); exit(1); } for (i=0; i < ptmxdevices; i++) NewPort("/dev/ptmx"); break; default: fprintf(stderr, "Invalid option %s\n", argv[optind]); Usage(); exit(1); } } while (optind < argc) NewPort(argv[optind++]); if (NbPort < 2) { fprintf(stderr, "This multiplexer needs at least two pty's\n"); exit(1); } } int main(int argc, char *argv[]) { if (argc < 2) { Banner(0); Usage(); } else { Banner(1); } ProcessArgv(argc, argv); while (1) ProcessPortList(); return 0; } kiss/kissparms.8000066400000000000000000000055341442776067000141600ustar00rootroot00000000000000.TH KISSPARMS 8 "30 October 2005" Linux "Linux System Managers Manual" .SH NAME kissparms \- Configure KISS TNCs. .SH SYNOPSIS .B kissparms [-c crc-type] -p [-f y|n] [-h hw] [-l txtail] [-r pers] [-s slot] [-t txd] [-e feclevel] [-v] [-x] [-X raw] .SH DESCRIPTION .LP .B Kissparms is used to dynamically configure KISS TNCs that have been setup for AX.25 use by .B kissattach. This program uses the packet interface to allow it to communicate with the KISS TNC without interrupting the AX.25 data stream. Therefore the KISS parameters may be set at any time during the operation of the AX.25 port. .LP A full description of the KISS protocol can be found in the ARRL 6th Computer Networking Conference papers pp 38-43. While use of the Tx Tail value is now deprecated, it has been included to satisfy the requirements of users of old TNC firmware. .LP Although this utility was originally designed for controlling KISS TNCs connected to a serial port, it is used by a number of other Linux packet radio devices such as the Z8530 SCC driver for controlling their parameters in exactly the same manner. Therefore .B kissparms functionality extends to more than KISS TNCs. .SH OPTIONS .TP 12 .BI "\-c crc-type" Sets the crc-type to use. For e.g. kernel mkiss: 0 = auto, 1 = none, 2 = flexnet, 3 = smack .TP 12 .BI "\-p port" Sets the port that is being configured. .TP 12 .BI "\-f y|n" This sets the TNC into either full duplex ``y'', or half duplex ``n'' mode. .TP 12 .BI "\-h hardware" This is used to set hardware specific parameters. .TP 12 .BI "\-e FEC error correction level" Sets the FEC error correction level in a DSP card based modem (KISS parameter 8). Larger correction level means better noise resistance, but slower throughput on a good connection. This is an experimental feature found in a QPSK modem for the Motorola DSP56001 based DSP4 and EVM cards only. .TP 12 .BI "\-l txtail" Sets the TX Tail time in milliseconds. Note that the command to the TNC only operates in steps of ten milliseconds, so only use values like 10, 20 etc. .TP 12 .BI "\-r persist" Sets the persist value. This parameter is scaled to the range 0 to 255. .TP 12 .BI "\-s slottime" Sets the slottime in milliseconds. Note that the command to the TNC only operates in steps of ten milliseconds, so only use values like 10, 20 etc. .TP 12 .BI "\-t txdelay" Sets the TX Delay in milliseconds. Note that the command to the TNC only operates in steps of ten milliseconds, so only use values like 10, 20 etc. .TP 12 .BI \-v Display the version. .TP 12 .BI \-x Takes the TNC out of KISS mode back into ``normal'' mode. This command is manufacturer specific but works in many cases. This option overrides all options except the port number. .TP 12 .BI \-X raw Sends the specified raw value to the kiss driver. .SH "SEE ALSO" .BR axports (5), .BR kissattach (8). .SH AUTHOR Jonathan Naylor G4KLX kiss/kissparms.c000066400000000000000000000123561442776067000142330ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PARAM_TXDELAY 1 #define PARAM_PERSIST 2 #define PARAM_SLOTTIME 3 #define PARAM_TXTAIL 4 #define PARAM_FULLDUP 5 #define PARAM_HARDWARE 6 #define PARAM_FECLEVEL 8 #define PARAM_RETURN 255 #define USAGE "usage: kissparms [-c crc-type] -p [-f y|n] [-h hw] [-l txtail]\n [-r pers ] [-s slot] [-t txd] [-e feclevel] [-v] [-x] [-X raw]\n" int main(int argc, char *argv[]) { unsigned char buffer[256]; struct sockaddr sa; int proto = ETH_P_AX25; int txdelay = -1; int txtail = -1; int persist = -1; int slottime = -1; int fulldup = -1; int hardware = -1; int feclevel = -1; int crcmode = -1; int kissoff = 0; int buflen = 0; int s; int X = 0; char *port = NULL; if (ax25_config_load_ports() == 0) { fprintf(stderr, "kissparms: no AX.25 ports configured\n"); return 1; } while ((s = getopt(argc, argv, "c:e:f:h:l:p:r:s:t:X:vx")) != -1) { switch (s) { case 'c': crcmode = atoi(optarg); break; case 'e': feclevel = atoi(optarg); if (feclevel < 0 || feclevel > 3) { fprintf(stderr, "kissparms: invalid FEC level value\n"); return 1; } break; case 'f': if (*optarg != 'y' && *optarg != 'n') { fprintf(stderr, "kissparms: invalid full duplex setting\n"); return 1; } fulldup = *optarg == 'y'; break; case 'l': txtail = atoi(optarg) / 10; if (txtail < 0 || txtail > 255) { fprintf(stderr, "kissparms: invalid txtail value\n"); return 1; } break; case 'h': hardware = atoi(optarg); if (hardware < 0 || hardware > 255) { fprintf(stderr, "kissparms: invalid hardware value\n"); return 1; } break; case 'p': port = optarg; if (ax25_config_get_addr(port) == NULL) { fprintf(stderr, "kissparms: invalid port name - %s\n", port); return 1; } break; case 'r': persist = atoi(optarg); if (persist < 0 || persist > 255) { fprintf(stderr, "kissparms: invalid persist value\n"); return 1; } break; case 's': slottime = atoi(optarg) / 10; if (slottime < 0 || slottime > 255) { fprintf(stderr, "kissparms: invalid slottime value\n"); return 1; } break; case 't': txdelay = atoi(optarg) / 10; if (txdelay < 0 || txdelay > 255) { fprintf(stderr, "kissparms: invalid txdelay value\n"); return 1; } break; case 'v': printf("kissparms: %s\n", FULL_VER); return 0; case 'x': kissoff = 1; break; case 'X': do { buffer[buflen++] = atoi(optarg); while (*optarg && isalnum(*optarg & 0xff)) optarg++; while (*optarg && isspace(*optarg & 0xff)) optarg++; } while (*optarg); X = 1; break; case ':': case '?': fprintf(stderr, USAGE); return 1; } } if (port == NULL) { fprintf(stderr, USAGE); return 1; } s = socket(PF_PACKET, SOCK_PACKET, htons(proto)); if (s < 0) { perror("kissparms: socket"); return 1; } strcpy(sa.sa_data, ax25_config_get_dev(port)); if (X && buflen) goto rawsend; if (kissoff) { buffer[0] = PARAM_RETURN; buflen = 1; rawsend: if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } else { if (txdelay != -1) { buffer[0] = PARAM_TXDELAY; buffer[1] = txdelay; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } if (txtail != -1) { buffer[0] = PARAM_TXTAIL; buffer[1] = txtail; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } if (persist != -1) { buffer[0] = PARAM_PERSIST; buffer[1] = persist; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } if (slottime != -1) { buffer[0] = PARAM_SLOTTIME; buffer[1] = slottime; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } if (fulldup != -1) { buffer[0] = PARAM_FULLDUP; buffer[1] = fulldup; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } if (hardware != -1) { buffer[0] = PARAM_HARDWARE; buffer[1] = hardware; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } if (feclevel != -1) { buffer[0] = PARAM_FECLEVEL; buffer[1] = feclevel; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } if (crcmode != -1) { buffer[0] = 0x85; buffer[1] = crcmode; buflen = 2; if (sendto(s, buffer, buflen, 0, &sa, sizeof(struct sockaddr)) == -1) { perror("kissparms: sendto"); return 1; } } } close(s); return 0; } kiss/mkiss.8000066400000000000000000000066011442776067000132660ustar00rootroot00000000000000.TH MKISS 8 "13 April 2010" Linux "Linux System Managers Manual" .SH NAME mkiss \- Attach a multi KISS interface .SH SYNOPSIS .B mkiss [-c] [-f] [-h] [-l] [-s speed] [-p pollrate] [-v] [-x n_ptmx] ttyinterface pty .. .SH DESCRIPTION .LP .B Mkiss allows dual port TNCs or multiple TNCs sharing the same serial port to be used with the Linux AX.25 kernel software. The AX.25 software has no support for dual port TNCs or multiple TNCs charing the same serial line. The different ports are addressed by encoding the port number in the control byte of every kiss frame. .B Mkiss watches a serial port, and routes kiss frames to/from the pseudo ttys. The other side of the pseudo ttys are then attached with .B kissattach as normal. .sp 1 Statistics about the operation of .B mkiss may be obtained by sending the SIGUSR1 signal to the running program. On reception of such a signal .B mkiss will print a set of statistics to the system log if logging has been enabled. .sp 1 Although mention is made of using pseudo ttys as the last arguments, these devices may be normal serial ports. However .B mkiss provides no way in which to set their speed, the speed must therefore be set by some other method. .sp 1 If the pty argument is "/dev/ptmx", then Unix98 behaviour will automatically take effekt. With Unix98 pty's, the slave pty name could not be forseen. That's why mkiss will print the corresponding slave pty name as a separate line on stdout. .sp 1 If the pty name is the special name "none", no pty is opened. This is useful if you have multiport tnc like the KPC-9612 on i.e. /dev/ttyUSB0 and you only like to handle packets for the second port. The KPC has no option to configure the second tnc to listen on kiss port number 0. Thus, if you like to send all frames from the pty to the kiss port number 1, we need to tell mkiss to tag them for port number 1. This is done by "mkiss /dev/ttyUSB0 none /dev/ptmx". Frames received with port number 0 are discarded. .SH OPTIONS .TP 10 .BI \-c This enables a one-byte checksum on each incoming and outgoing KISS frame on the serial port. This checksum is used by G8BPQ KISS roms to maintain the integrity of KISS frames. .TP 10 .BI \-f This enables a 16-bit checksum on each incoming and outgoing KISS frame on the serial port. This checksum is used by Flexnet Node and BayCom Mailbox to maintain the integrity of KISS frames. .TP 10 .BI \-h Enables hardware handshaking on the serial line to the TNC. The KISS specification states that no hardware flow control shall be used so the default is off. But some KISS implementations do use hardware flow control. .TP 10 .BI \-l Enables system logging, the default is off. .TP 10 .BI "\-s speed" Set the speed of the serial port. .TP 10 .BI "\-p pollrate" Enables polling. Polled mode is used by G8BPQ KISS roms to prevent contention on systems where multiple TNCs share the same serial line. Pollrate is interval between polls (in 100ms units). .TP 10 .BI \-v Display the version. .TP 10 .BI "\-x number" This option is for Unix98 PTYs. It allocates "number" ptys; their names are written to stdout. When -x is used, the pty arguments are optional. "mkiss -x 3 ttyname" is an comfortable alternative to "mkiss ttyname /dev/ptmx /dev/ptmx /dev/ptmx". .SH "SEE ALSO" .BR kissattach (8), .BR ifconfig (8), .BR kill (1). .SH AUTHORS Tomi Manninen OH2BNS .br Jonathan Naylor G4KLX .br Kevin Uhlir N0BEL kiss/mkiss.c000066400000000000000000000375161442776067000133520ustar00rootroot00000000000000/* * mkiss.c * Fake out AX.25 code into supporting dual port TNCS by routing serial * port data to/from two pseudo ttys. * * Version 1.03 * * N0BEL * Kevin Uhlir * kevinu@flochart.com * * 1.01 12/30/95 Ron Curry - Fixed FD_STATE bug where FD_STATE was being used for * three state machines causing spurious writes to wrong TNC port. FD_STATE is * now used for real serial port, FD0_STATE for first pseudo tty, and FD1_STATE * for second pseudo tty. This was an easy fix but a MAJOR bug. * * 1.02 3/1/96 Jonathan Naylor - Make hardware handshaking default to off. * Allowed for true multiplexing of the data from the two pseudo ttys. * Numerous formatting changes. * * 1.03 4/20/96 Tomi Manninen - Rewrote KISS en/decapsulation (much of the * code taken from linux/drivers/net/slip.c). Added support for all the 16 * possible kiss ports and G8BPQ-style checksumming on ttyinterface. Now * mkiss also sends a statistic report to stderr if a SIGUSR1 is sent to it. * * 1.04 25/5/96 Jonathan Naylor - Added check for UUCP style tty lock files. * As they are now supported by kissattach as well. * * 1.05 20/8/96 Jonathan Naylor - Convert to becoming a daemon and added * system logging. * * 1.06 23/11/96 Tomi Manninen - Added simple support for polled kiss. * * 1.07 12/24/97 Deti Fliegl - Added Flexnet/BayCom CRC mode with commandline * parameter -f * * 1.08 xx/xx/99 Tom Mazouch - Adjustable poll interval */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FLEX_CRC 2 #define G8BPQ_CRC 1 #define SIZE 4096 #define FEND 0300 /* Frame End (0xC0) */ #define FESC 0333 /* Frame Escape (0xDB) */ #define TFEND 0334 /* Transposed Frame End (0xDC) */ #define TFESC 0335 /* Transposed Frame Escape (0xDD) */ #define ACKREQ 0x0C #define POLL 0x0E /* * Keep these off the stack. */ static unsigned char ibuf[SIZE]; /* buffer for input operations */ static unsigned char obuf[SIZE]; /* buffer for kiss_tx() */ static int crc_errors; static int invalid_ports; static int return_polls ; static char *usage_string = "usage: mkiss [-p interval] [-c] [-f] [-h] [-l] [-s speed] [-v] [-x ] ttyinterface pty ..\n"; static int dump_report = FALSE; static int logging = FALSE; static int crcflag = FALSE; static int hwflag = FALSE; static int pollspeed; /* CRC-stuff */ typedef unsigned short int u16; #define CRCTYP 0x20 static u16 crctab[256]; struct iface { char *name; /* Interface name (/dev/???) */ int fd; /* File descriptor */ int escaped; /* FESC received? */ u16 crc; /* Incoming frame crc */ unsigned char obuf[SIZE]; /* TX buffer */ unsigned char *optr; /* Next byte to transmit */ unsigned int errors; /* KISS protocol error count */ unsigned int nondata; /* Non-data frames rx count */ unsigned int rxpackets; /* RX frames count */ unsigned int txpackets; /* TX frames count */ unsigned long rxbytes; /* RX bytes count */ unsigned long txbytes; /* TX bytes count */ char namepts[PATH_MAX]; /* name of the unix98 pts slaves, which * the client has to use */ }; static struct iface *tty; static struct iface *pty[16]; static int numptys; static void init_crc(void) { short int i, j; u16 accum, data; for (i = 0; i < 256; i++) { /* fill table with CRC of values... */ accum = 0xffff; data = i; for (j = 0; j < 8; ++j) { if ((data^accum) & 0x0001) /* if msb of data^accum is TRUE */ /* then shift and subtract poly */ accum = (accum >> 1) ^ 0x8408; else /* otherwise: transparent shift */ accum >>= 1; data >>= 1; /* move up next bit for XOR */ } crctab[i] = accum; } } static int poll(int fd, int ports) { unsigned char buffer[3]; static int port; buffer[0] = FEND; buffer[1] = POLL | (port << 4); buffer[2] = FEND; if (write(fd, buffer, 3) == -1) { perror("mkiss: poll: write"); return 1; } if (++port >= ports) port = 0; return 0; } static int put_ubyte(unsigned char* s, u16* crc, unsigned char c, int usecrc) { int len = 1; if (c == FEND) { *s++ = FESC; *s++ = TFEND; len++; } else { *s++ = c; if (c == FESC) { *s++ = TFESC; len++; } } switch (usecrc) { case G8BPQ_CRC: *crc ^= c; /* Adjust checksum */ break; case FLEX_CRC: *crc = (*crc<<8)^crctab[(*crc>>8)^((u16)((c)&255))]; break; } return len; } static int kiss_tx(int fd, int port, unsigned char *s, int len, int usecrc) { unsigned char *ptr = obuf; unsigned char c, cmd; u16 crc = 0; int i; cmd = s[0] & 0x0F; /* Non-data frames don't get a checksum byte */ if (usecrc == G8BPQ_CRC && cmd != 0 && cmd != ACKREQ) usecrc = FALSE; /* * Send an initial FEND character to flush out any * data that may have accumulated in the receiver * due to line noise. */ *ptr++ = FEND; if (usecrc == FLEX_CRC) { crc = 0xffff; ptr += put_ubyte(ptr, &crc, CRCTYP, usecrc); c = *s++; } else { c = *s++; c = (c & 0x0F) | (port << 4); ptr += put_ubyte(ptr, &crc, c, usecrc); } /* * For each byte in the packet, send the appropriate * character sequence, according to the SLIP protocol. */ for (i = 0; i < len - 1; i++) ptr += put_ubyte(ptr, &crc, s[i], usecrc); /* * Now the checksum... */ switch (usecrc) { case G8BPQ_CRC: c = crc & 0xFF; ptr += put_ubyte(ptr, &crc, c, usecrc); break; case FLEX_CRC: { u16 u = crc; ptr += put_ubyte(ptr, &crc, u / 256, usecrc); ptr += put_ubyte(ptr, &crc, u & 255, usecrc); } break; } *ptr++ = FEND; return write(fd, obuf, ptr - obuf); } static int kiss_rx(struct iface *ifp, unsigned char c, int usecrc) { int len; switch (c) { case FEND: len = ifp->optr - ifp->obuf; if (len != 0 && ifp->escaped) { /* protocol error... */ len = 0; /* ...drop frame */ ifp->errors++; } if (len != 0) { switch (usecrc) { case G8BPQ_CRC: if ((ifp->obuf[0] & 0x0F) != 0) { /* * Non-data frames don't have checksum. */ usecrc = 0; if ((ifp->obuf[0] & 0x0F) == POLL) { /* drop returned polls */ len = 0; return_polls++; } else ifp->nondata++; } else { if ((ifp->crc & 0xFF) != 0) { /* checksum failed... */ /* ...drop frame */ len = 0; crc_errors++; } else /* delete checksum byte */ len--; } break; case FLEX_CRC: if (len > 14 && ifp->crc == 0x7070) { len -= 2; *ifp->obuf = 0; } else { len = 0; crc_errors++; } break; } } if (len != 0) { ifp->rxpackets++; ifp->rxbytes += len; } /* * Clean up. */ ifp->optr = ifp->obuf; if (usecrc == FLEX_CRC) ifp->crc = 0xffff; else ifp->crc = 0; ifp->escaped = FALSE; return len; case FESC: ifp->escaped = TRUE; return 0; case TFESC: if (ifp->escaped) { ifp->escaped = FALSE; c = FESC; } break; case TFEND: if (ifp->escaped) { ifp->escaped = FALSE; c = FEND; } break; default: if (ifp->escaped) { /* protocol error... */ ifp->escaped = FALSE; ifp->errors++; } break; } *ifp->optr++ = c; switch (usecrc) { case G8BPQ_CRC: ifp->crc ^= c; break; case FLEX_CRC: ifp->crc = (ifp->crc << 8) ^ crctab[(ifp->crc >> 8) ^ c]; break; default: break; } return 0; } static void sigterm_handler(int sig) { int i; if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } tty_unlock(tty->name); close(tty->fd); free(tty); for (i = 0; i < numptys; i++) { if (pty[i]->fd == -1) continue; if (pty[i]->namepts[0] != '\0') continue; tty_unlock(pty[i]->name); close(pty[i]->fd); free(pty[i]); } exit(0); } static void sigusr1_handler(int sig) { signal(SIGUSR1, sigusr1_handler); dump_report = TRUE; } static void report(void) { int i; long t; time(&t); syslog(LOG_INFO, "version %s.", FULL_VER); syslog(LOG_INFO, "Status report at %s", ctime(&t)); syslog(LOG_INFO, "Hardware handshaking %sabled.", hwflag ? "en" : "dis"); syslog(LOG_INFO, "G8BPQ checksumming %sabled.", crcflag == G8BPQ_CRC ? "en" : "dis"); syslog(LOG_INFO, "FLEX checksumming %sabled.", crcflag == FLEX_CRC ? "en" : "dis"); syslog(LOG_INFO, "polling %sabled.", pollspeed ? "en" : "dis"); if (pollspeed) syslog(LOG_INFO, "Poll interval %d00ms", pollspeed); syslog(LOG_INFO, "ttyinterface is %s (fd=%d)", tty->name, tty->fd); for (i = 0; i < numptys; i++) syslog(LOG_INFO, "pty%d is %s (fd=%d)", i, pty[i]->name, pty[i]->fd); syslog(LOG_INFO, "Checksum errors: %d", crc_errors); syslog(LOG_INFO, "Invalid ports: %d", invalid_ports); syslog(LOG_INFO, "Returned polls: %d", return_polls); syslog(LOG_INFO, "Interface TX frames TX bytes RX frames RX bytes Errors"); syslog(LOG_INFO, "%-11s %-9u %-9lu %-9u %-9lu %u", tty->name, tty->txpackets, tty->txbytes, tty->rxpackets, tty->rxbytes, tty->errors); for (i = 0; i < numptys; i++) { syslog(LOG_INFO, "%-11s %-9u %-9lu %-9u %-9lu %u", pty[i]->name, pty[i]->txpackets, pty[i]->txbytes, pty[i]->rxpackets, pty[i]->rxbytes, pty[i]->errors); } } int main(int argc, char *argv[]) { unsigned char *icp; int topfd; fd_set readfd; struct timeval timeout, pollinterval; int retval, i, size, len; int speed = -1; int ptmxdevices = 0; char *npts; int wrote_info = 0; while ((size = getopt(argc, argv, "cfhlp:s:vx:")) != -1) { switch (size) { case 'c': crcflag = G8BPQ_CRC; break; case 'f': crcflag = FLEX_CRC; break; case 'h': hwflag = TRUE; break; case 'l': logging = TRUE; break; case 'p': pollspeed = atoi(optarg); pollinterval.tv_sec = pollspeed / 10; pollinterval.tv_usec = (pollspeed % 10) * 100000L; break; case 's': speed = atoi(optarg); break; case 'x': ptmxdevices = atoi(optarg); if (ptmxdevices < 1 || ptmxdevices > 16) { fprintf(stderr, "mkiss: too %s devices\n", ptmxdevices < 1 ? "few" : "many"); return 1; } break; case 'v': printf("mkiss: %s\n", FULL_VER); return 1; case ':': case '?': fprintf(stderr, "%s", usage_string); return 1; } } if ((argc - optind) < 2 && ptmxdevices == 0) { fprintf(stderr, "%s", usage_string); return 1; } if ((argc - optind) < 1 && ptmxdevices > 0) { fprintf(stderr, "%s", usage_string); return 1; } numptys = argc - optind - 1; if ((numptys + ptmxdevices) > 16) { fprintf(stderr, "mkiss: max 16 pty interfaces allowed.\n"); return 1; } /* * Check for lock files before opening any TTYs */ if (tty_is_locked(argv[optind])) { fprintf(stderr, "mkiss: tty %s is locked by another process\n", argv[optind]); return 1; } for (i = 0; i < numptys; i++) { if (!strcmp("/dev/ptmx", argv[optind + i + 1])) continue; if (!strcmp("none", argv[optind + i + 1])) continue; if (tty_is_locked(argv[optind + i + 1])) { fprintf(stderr, "mkiss: pty %s is locked by another process\n", argv[optind + i + 1]); return 1; } } /* * Open and configure the tty interface. Open() is * non-blocking so it won't block regardless of the modem * status lines. */ tty = calloc(1, sizeof(struct iface)); if (tty == NULL) { perror("mkiss: malloc"); return 1; } tty->fd = open(argv[optind], O_RDWR | O_NDELAY); if (tty->fd == -1) { perror("mkiss: open"); return 1; } tty->name = argv[optind]; tty_raw(tty->fd, hwflag); if (speed != -1 && !tty_speed(tty->fd, speed)) { close(tty->fd); return 1; } tty->optr = tty->obuf; topfd = tty->fd; tty->namepts[0] = '\0'; /* * Make it block again... */ fcntl(tty->fd, F_SETFL, 0); /* * Open and configure the pty interfaces */ for (i = 0; i < numptys+ptmxdevices; i++) { static char name_ptmx[] = "/dev/ptmx"; char *pty_name = (i < numptys ? argv[optind+i+1] : name_ptmx); pty[i] = calloc(1, sizeof(struct iface)); if (pty[i] == NULL) { perror("mkiss: malloc"); return 1; } if (!strcmp(pty_name, "none")) { pty[i]->fd = -1; strcpy(pty[i]->namepts, "none"); } else { pty[i]->fd = open(pty_name, O_RDWR); if (pty[i]->fd == -1) { perror("mkiss: open"); free(pty[i]); pty[i] = NULL; return 1; } tty_raw(pty[i]->fd, FALSE); topfd = (pty[i]->fd > topfd) ? pty[i]->fd : topfd; pty[i]->namepts[0] = '\0'; } pty[i]->name = pty_name; pty[i]->optr = pty[i]->obuf; if (!strcmp(pty[i]->name, "/dev/ptmx")) { /* get name of pts-device */ npts = ptsname(pty[i]->fd); if (npts == NULL) { fprintf(stderr, "mkiss: Cannot get name of pts-device.\n"); free(pty[i]); pty[i] = NULL; return 1; } strncpy(pty[i]->namepts, npts, PATH_MAX-1); pty[i]->namepts[PATH_MAX-1] = '\0'; /* unlock pts-device */ if (unlockpt(pty[i]->fd) == -1) { fprintf(stderr, "mkiss: Cannot unlock pts-device %s\n", pty[i]->namepts); free(pty[i]); pty[i] = NULL; return 1; } if (wrote_info == 0) printf("\nAwaiting client connects on:\n"); else printf(" "); printf("%s", pty[i]->namepts); wrote_info = 1; } } if (wrote_info > 0) printf("\n"); numptys=numptys+ptmxdevices; /* * Now all the ports are open, lock them. */ tty_lock(tty->name); for (i = 0; i < numptys; i++) { if (pty[i]->namepts[0] == '\0') tty_lock(pty[i]->name); } if (logging) { openlog("mkiss", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting"); } if (wrote_info > 0) { fflush(stdout); fflush(stderr); close(0); close(1); close(2); } signal(SIGHUP, SIG_IGN); signal(SIGUSR1, sigusr1_handler); signal(SIGTERM, sigterm_handler); if (!daemon_start(FALSE)) { fprintf(stderr, "mkiss: cannot become a daemon\n"); return 1; } init_crc(); /* * Loop until an error occurs on a read. */ while (TRUE) { FD_ZERO(&readfd); FD_SET(tty->fd, &readfd); for (i = 0; i < numptys; i++) if (pty[i]->fd != -1) FD_SET(pty[i]->fd, &readfd); if (pollspeed) timeout = pollinterval; errno = 0; retval = select(topfd + 1, &readfd, NULL, NULL, pollspeed ? &timeout : NULL); if (retval == -1) { if (dump_report) { if (logging) report(); dump_report = FALSE; continue; } else { perror("mkiss: select"); continue; } } /* * Timer expired - let's poll... */ if (retval == 0 && pollspeed) { poll(tty->fd, numptys); continue; } /* * A character has arrived on the ttyinterface. */ if (tty->fd > -1 && FD_ISSET(tty->fd, &readfd)) { if ((size = read(tty->fd, ibuf, SIZE)) < 0 && errno != EINTR) { if (logging) syslog(LOG_ERR, "tty->fd: %m"); break; } for (icp = ibuf; size > 0; size--, icp++) { len = kiss_rx(tty, *icp, crcflag); if (len != 0) { if ((i = (*tty->obuf & 0xF0) >> 4) < numptys) { if (pty[i]->fd != -1) { kiss_tx(pty[i]->fd, 0, tty->obuf, len, FALSE); pty[i]->txpackets++; pty[i]->txbytes += len; } } else invalid_ports++; if (pollspeed) poll(tty->fd, numptys); } } } for (i = 0; i < numptys; i++) { /* * A character has arrived on pty[i]. */ if (pty[i]->fd > -1 && FD_ISSET(pty[i]->fd, &readfd)) { if ((size = read(pty[i]->fd, ibuf, SIZE)) < 0 && errno != EINTR) { if (logging) syslog(LOG_ERR, "pty[%d]->fd: %m\n", i); goto end; } for (icp = ibuf; size > 0; size--, icp++) { len = kiss_rx(pty[i], *icp, FALSE); if (len != 0) { kiss_tx(tty->fd, i, pty[i]->obuf, len, crcflag); tty->txpackets++; tty->txbytes += len; } } } } } end: if (logging) closelog(); tty_unlock(tty->name); close(tty->fd); free(tty); for (i = 0; i < numptys; i++) { if (pty[i]->fd == -1) continue; if (pty[i]->namepts[0] != '\0') continue; tty_unlock(pty[i]->name); close(pty[i]->fd); free(pty[i]); } return 1; } kiss/net2kiss.8000066400000000000000000000024221442776067000136770ustar00rootroot00000000000000.TH NET2KISS 8 "15 January 2009" "" "" .SH NAME net2kiss \- convert a network AX.25 driver to a KISS stream on a pseudo-tty .SH SYNOPSIS .B "net2kiss [-sfzva] [-i ifname] ptyname" .br .SH DESCRIPTION .B net2kiss takes AX.25 packets from an AX.25 kernel network interface and converts them into a KISS data stream via a pseudo-tty. .SH PARAMETERS .TP .B ptyname specifies the path to the pseudo-tty. If you use the special name "/dev/ptmx", then a Unix98-PTY pair is used; the pty name is written to stdout. .SH OPTIONS .TP .B "\-i ifname" specifies the name of the kernel AX.25 network interface. The default is bc0. .TP .B \-s tells net2kiss to search by itself for a free pseudoterminal, open the master side and create a symlink named by -p to the slave side. .TP .B \-f can be used in conjunction with -s to force the creation of the symlink even if the target file does already exist. *DANGEROUS* .TP .B \-v Verbose output. Displays error messages during operation and decoded packets to stdout. .TP .B \-z put the network interface into promiscious mode. .TP .B \-a receive every packet instead of only AX.25 from the kernel AX.25 network interface. .SH BUGS None known. .SH SEE ALSO .BR ifconfig (8), .BR kissattach (8). .SH AUTHORS Thomas M. Sailer, HB9JNX/AE4WA, kiss/net2kiss.c000066400000000000000000000346611442776067000137640ustar00rootroot00000000000000/*****************************************************************************/ /* * 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); } /* --------------------------------------------------------------------- */ kiss/spattach.8000066400000000000000000000000261442776067000137420ustar00rootroot00000000000000.so man8/kissattach.8 netrom/000077500000000000000000000000001442776067000123775ustar00rootroot00000000000000netrom/.gitignore000066400000000000000000000001241442776067000143640ustar00rootroot00000000000000*.o .deps Makefile Makefile.in netromd nodesave nrattach nrbroadcast nrparms nrsdrv netrom/Makefile.am000066400000000000000000000013621442776067000144350ustar00rootroot00000000000000 etcfiles = nrbroadcast installconf: $(mkinstalldirs) $(DESTDIR)$(AX25_SYSCONFDIR) @list='$(etcfiles)'; for p in $$list; do \ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p"; \ $(INSTALL_DATA) $$p $(DESTDIR)$(AX25_SYSCONFDIR)/$$p; \ done sbin_PROGRAMS = netromd nodesave nrattach nrparms nrsdrv LDADD = $(AX25_LIB) dist_man_MANS = netrom.4 nrbroadcast.5 netromd.8 nodesave.8 \ nrattach.8 nrparms.8 nrsdrv.8 EXTRA_DIST = $(etcfiles) netromd_SOURCES = \ netromd.c \ netromd.h \ netromr.c \ netromt.c AM_CPPFLAGS = -D_GNU_SOURCE \ -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" AX25_SYSCONFDIR=${sysconfdir}/ax25/ AX25_LOCALSTATEDIR=${localstatedir}/ax25/ netrom/netrom.4000066400000000000000000000036001442776067000137670ustar00rootroot00000000000000.TH NETROM 4 "15 July 2011" Linux "Linux Programmer's Manual" .SH NAME AF_NETROM \- NET/ROM amateur packet radio protocol family .SH DESCRIPTION .LP .B NET/ROM is a protocol used extensively by radio amateurs. The Linux NET/ROM protocol family permits access to these protocols via the standard networking .B socket metaphor. .LP The NET/ROM protocol layer only supports connected mode. IP traffic may be stacked on top of NET/ROM frames using a non-standard extension to the NET/ROM protocol. .LP The only mode of operation is connected mode which is the mode used for a socket of type SOCK_SEQPACKET (stream sockets are not available in NET/ROM). This requires that the user ensures output data is suitably packetised, and that input data is read a packet at a time into a buffer of suitable size. .LP NET/ROM addresses consist of 6 ascii characters and a number called the SSID. These are encoded into a sockaddr_ax25 structure which is provided to the relevant system calls. .LP NET/ROM has some unusual properties. Notably in a multi-user system an AX.25 address is often associated with a user, and some users may not have such an association. a set of ioctl calls are provided to manage an association table. .LP NET/ROM supports the following socket options for SOL_NETROM. NETROM_T1 is the T1 timer in 1/10ths of a second, NETROM_T2 is the T2 timer in 1/10ths of a second. NETROM_N2, the retry counter is also configurable. There is no 'infinite retry' option supported however. It is possible for an application to request that the NET/ROM layer return the NET/ROM header as well as the application data, this is done via the NETROM_HDRINCL socket option. .SH "SEE ALSO" .BR call (1), .BR socket (2), .BR setsockopt(2), .BR getsockopt(2), .BR nrbroadcast (5), .BR nrports (5), .BR netromd (8), .BR noderest (8), .BR nodesave (8), .BR nrparms (8). .LP .SH AUTHOR Jonathan Naylor G4KLX netrom/netromd.8000066400000000000000000000071071442776067000141450ustar00rootroot00000000000000.TH NETROMD 8 "20 August 1996" Linux "Linux System Managers Manual" .SH NAME netromd \- Send and receive NET/ROM routing messages .SH SYNOPSIS .B netromd [-c] [-d] [-i] [-l] [-p pause] [-q quality] [-t interval] [-v] .SH DESCRIPTION .LP For a NET/ROM based network to operate correctly, a periodic broadcast of routing information needs to occur. Typically this occurs once every hour on every port which is expected to carry NET/ROM traffic. The purpose of .B netromd is to send and receive NET/ROM routing broadcasts. To operate correctly a set of parameters that corresponds to each AX.25 port needs to be passed to the program. This information is encoded in a configuration file, by default which is /etc/ax25/nrbroadcast with each line representing one port, see the manual page for .BR nrbroadcast (5). .LP To cut down the length of these routing broadcasts, only the information about the highest quality neighbour for a particular node is transmitted. The transmission is also limited to those node that have a certain minimum value in their obsolesence count, this value is decremented every time a routing broadcast is transmitted, and is refreshed by receiving a routing broadcast which contains that particular node. .LP The value of the default quality is traditionally assigned a value that represents the quality of the radio links on that port. A higher number representing better radio links with 255 (the maximum) reserved for wire connections. The practise in the UK is to set the default quality to a low value, typically 10, and manually set up the trusted neighbouring nodes in the neighbour list manually. The worst quality for auto-updates value is a way to filter out low quality (ie distant) nodes. .LP The verbose flag may be either 0 or 1, representing no and yes. By specifying no, the program will only generate a routing message containing information about the node on which it is running, by specifying the yes option, all the information in the nodes routing tables will be transmitted. The quality advertised for the other node callsigns on this machine may be set using the \-q option. .LP Between each transmission .B netromd pauses for five seconds (default) in order to avoid flooding the channels that it must broadcast on. The value of this delay is settable with the \-p option. .SH OPTIONS .TP 16 .BI \-c Forces strict compliance to Software 2000 specifications. At present this only determines how node mnemonics with lower case characters will be handled. With compliance enabled mixed case node mnemonics will be ignored. The default is to accept node mnemonics of mixed case. .TP 16 .BI \-d Switches on debugging messages, the default is off. Logging must be enabled for them to be output. .TP 16 .BI \-i Transmit a routing broadcast immediately, the default is to wait for the time interval to elapse before transmitting the first routing broadcast. .TP 16 .BI \-l Enables logging of errors and debug messages to the system log. The default is off. .TP 16 .BI "\-p pause" Sets the delay between transmissions of individual routing broadcast packets. The default is five seconds. .TP 16 .BI "\-q quality" Sets the quality of the subsidiary nodes relative to the main node. The default is 255. .TP 16 .BI "\-t interval" The time interval between routing broadcasts, in minutes. The default is 60 minutes. .TP 16 .BI \-v Display the version. .SH FILES .nf /proc/net/nr_neigh .br /proc/net/nr_nodes .br /etc/ax25/axports .br /etc/ax25/nrbroadcast .fi .SH "SEE ALSO" .BR ax25 (4), .BR axports (5), .BR nrbroadcast (5), .BR netrom (4), .BR nrparms (8). .SH AUTHOR Jonathan Naylor G4KLX netrom/netromd.c000066400000000000000000000136131442776067000142170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #include "netromd.h" struct port_struct port_list[20]; int port_count = FALSE; int compliant = FALSE; int logging = FALSE; int sock, debug; ax25_address my_call; ax25_address node_call; static void terminate(int sig) { if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } exit(0); } static int bcast_config_load_ports(void) { char buffer[255], port[32], *s; struct ifreq ifr; FILE *fp; fp = fopen(CONF_NETROMD_FILE, "r"); if (fp == NULL) { fprintf(stderr, "netromd: cannot open config file\n"); return -1; } while (fgets(buffer, 255, fp) != NULL) { s = strchr(buffer, '\n'); if (s != NULL) *s = '\0'; if (strlen(buffer) == 0 || buffer[0] == '#') continue; if (sscanf(buffer, "%s %d %d %d %d", port, &port_list[port_count].minimum_obs, &port_list[port_count].default_qual, &port_list[port_count].worst_qual, &port_list[port_count].verbose) == -1) { fprintf(stderr, "netromd: unable to parse: %s", buffer); return -1; } if (ax25_config_get_addr(port) == NULL) { fprintf(stderr, "netromd: invalid port name - %s\n", port); return -1; } port_list[port_count].port = strdup(port); port_list[port_count].device = strdup(ax25_config_get_dev(port_list[port_count].port)); strncpy(ifr.ifr_name, port_list[port_count].device, sizeof(ifr.ifr_name)); if (ioctl(sock, SIOCGIFINDEX, &ifr)) { fprintf(stderr, "netromd: SIOCGIFINDEX failed for device %s.\n", port_list[port_count].device); return -1; } port_list[port_count].index = ifr.ifr_ifindex; if (port_list[port_count].minimum_obs < 0 || port_list[port_count].minimum_obs > 6) { fprintf(stderr, "netromd: invalid minimum obsolescence\n"); return -1; } if (port_list[port_count].default_qual < 0 || port_list[port_count].default_qual > 255) { fprintf(stderr, "netromd: invalid default quality\n"); return -1; } if (port_list[port_count].worst_qual < 0 || port_list[port_count].worst_qual > 255) { fprintf(stderr, "netromd: invalid worst quality\n"); return -1; } if (port_list[port_count].verbose != 0 && port_list[port_count].verbose != 1) { fprintf(stderr, "netromd: invalid verbose setting\n"); return -1; } port_count++; } fclose(fp); if (port_count == 0) return -1; return 0; } int main(int argc, char **argv) { unsigned char buffer[512]; int size, i; struct sockaddr_ll sa; socklen_t asize; struct timeval timeout; time_t timenow, timelast; int interval = 3600; int localval = 255; int pause = 5; fd_set fdset; time(&timelast); while ((i = getopt(argc, argv, "cdilp:q:t:v")) != -1) { switch (i) { case 'c': compliant = TRUE; break; case 'd': debug++; break; case 'i': timelast = 0; break; case 'l': logging = TRUE; break; case 'p': pause = atoi(optarg); if (pause < 1 || pause > 120) { fprintf(stderr, "netromd: invalid pause value\n"); return 1; } break; case 'q': localval = atoi(optarg); if (localval < 10 || localval > 255) { fprintf(stderr, "netromd: invalid local quality\n"); return 1; } break; case 't': interval = atoi(optarg) * 60; if (interval < 60 || interval > 7200) { fprintf(stderr, "netromd: invalid time interval\n"); return 1; } break; case 'v': printf("netromd: %s\n", FULL_VER); return 0; case '?': case ':': fprintf(stderr, "usage: netromd [-d] [-i] [-l] [-q quality] [-t interval] [-v]\n"); return 1; } } signal(SIGTERM, terminate); sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_AX25)); if (sock == -1) { perror("netromd: socket"); return 1; } if (ax25_config_load_ports() == 0) { fprintf(stderr, "netromd: no AX.25 ports defined\n"); return 1; } if (nr_config_load_ports() == 0) { fprintf(stderr, "netromd: no NET/ROM ports defined\n"); return 1; } if (bcast_config_load_ports() == -1) { fprintf(stderr, "netromd: no NET/ROM broadcast ports defined\n"); return 1; } ax25_aton_entry(nr_config_get_addr(NULL), (char *)&my_call); ax25_aton_entry("NODES", (char *)&node_call); if (!daemon_start(TRUE)) { fprintf(stderr, "netromd: cannot become a daemon\n"); return 1; } if (logging) { openlog("netromd", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting"); } for (;;) { FD_ZERO(&fdset); FD_SET(sock, &fdset); timeout.tv_sec = 30; timeout.tv_usec = 0; if (select(sock + 1, &fdset, NULL, NULL, &timeout) == -1) continue; /* Signal received ? */ if (FD_ISSET(sock, &fdset)) { asize = sizeof(sa); size = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &sa, &asize); if (size == -1) { if (logging) { syslog(LOG_ERR, "recvfrom: %m"); closelog(); } return 1; } if (ax25_cmp((ax25_address *)(buffer + 1), &node_call) == 0 && ax25_cmp((ax25_address *)(buffer + 8), &my_call) != 0 && buffer[16] == NETROM_PID && buffer[17] == NODES_SIG) { for (i = 0; i < port_count; i++) { if (port_list[i].index == sa.sll_ifindex) { if (debug && logging) syslog(LOG_DEBUG, "receiving NODES broadcast on port %s from %s\n", port_list[i].port, ax25_ntoa((ax25_address *)(buffer + 8))); receive_nodes(buffer + 18, size - 18, (ax25_address *)(buffer + 8), i); break; } } } } time(&timenow); if ((timenow - timelast) > interval) { timelast = timenow; transmit_nodes(localval, pause); } } } netrom/netromd.h000066400000000000000000000013661442776067000142260ustar00rootroot00000000000000#ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define NETROM_PID 0xCF #define NODES_SIG 0xFF #define CALLSIGN_LEN 7 #define MNEMONIC_LEN 6 #define QUALITY_LEN 1 #define NODES_PACLEN 256 #define ROUTE_LEN (CALLSIGN_LEN+MNEMONIC_LEN+CALLSIGN_LEN+QUALITY_LEN) struct port_struct { char *device; char *port; int index; int minimum_obs; int default_qual; int worst_qual; int verbose; }; extern struct port_struct port_list[]; extern int port_count; extern int debug; extern int logging; extern int compliant; extern ax25_address my_call; extern ax25_address node_call; /* In netromt.c */ extern void transmit_nodes(int, int); /* In netromr.c */ extern void receive_nodes(unsigned char *, int, ax25_address *, int); netrom/netromr.c000066400000000000000000000127101442776067000142320ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #include "netromd.h" static int validcallsign(ax25_address *a) { char c; int n, end = 0; for (n = 0; n < 6; n++) { c = (a->ax25_call[n] >> 1) & 0x7F; if (!end && (isupper(c) || isdigit(c))) continue; if (c == ' ') { end = 1; continue; } return FALSE; } return TRUE; } static int validmnemonic(char *mnemonic) { if (compliant) { if (strspn(mnemonic, "#_&-/ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") == strlen(mnemonic)) return TRUE; } else { if (strspn(mnemonic, "#_&-/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") == strlen(mnemonic)) return TRUE; } return FALSE; } static int add_node(int s, unsigned char *buffer, struct nr_route_struct *nr_node, int quality, int index) { ax25_address best_neighbour; int best_quality; char *p; memcpy(&nr_node->callsign, buffer + 0, CALLSIGN_LEN); memcpy(nr_node->mnemonic, buffer + 7, MNEMONIC_LEN); memcpy(&best_neighbour, buffer + 13, CALLSIGN_LEN); best_quality = buffer[20]; nr_node->mnemonic[MNEMONIC_LEN] = '\0'; p = strchr(nr_node->mnemonic, ' '); if (p != NULL) *p = '\0'; if (!validcallsign(&nr_node->callsign)) { if (debug && logging) syslog(LOG_DEBUG, "netromr: add_node: invalid node callsign - %s", ax25_ntoa(&nr_node->callsign)); return -1; } if (!validmnemonic(nr_node->mnemonic)) { if (debug && logging) syslog(LOG_DEBUG, "netromr: add_node: invalid mnemonic - %s", nr_node->mnemonic); return -1; } if (!validcallsign(&best_neighbour)) { if (debug && logging) syslog(LOG_DEBUG, "netromr: add_node: invalid best neighbour callsign - %s", ax25_ntoa(&best_neighbour)); return -1; } if (ax25_cmp(&my_call, &best_neighbour) == 0) { if (debug && logging) syslog(LOG_DEBUG, "netromr: add_node: route to me"); return FALSE; } nr_node->quality = ((quality * best_quality) + 128) / 256; if (nr_node->quality < port_list[index].worst_qual) { if (debug && logging) syslog(LOG_DEBUG, "netromr: add_node: quality less than worst_qual"); return FALSE; } /* log this only when logging verbosely */ if (debug > 1 && logging) { syslog(LOG_DEBUG, "Node update: %s:%s", ax25_ntoa(&nr_node->callsign), nr_node->mnemonic); syslog(LOG_DEBUG, "Neighbour: %s device: %s", ax25_ntoa(&nr_node->neighbour), nr_node->device); syslog(LOG_DEBUG, "Quality: %d obs: %d ndigis: %d", nr_node->quality, nr_node->obs_count, nr_node->ndigis); } if (ioctl(s, SIOCADDRT, nr_node) == -1) { if (logging) syslog(LOG_ERR, "netromr: SIOCADDRT: %m"); return -1; } return TRUE; } void receive_nodes(unsigned char *buffer, int length, ax25_address *neighbour, int index) { struct nr_route_struct nr_node; char neigh_buffer[90], *portcall, *p; FILE *fp; int s; int quality, obs_count, qual, lock; char *callsign, *device; if (!validcallsign(neighbour)) { if (debug && logging) syslog(LOG_DEBUG, "rejecting frame, invalid neighbour callsign - %s\n", ax25_ntoa(neighbour)); return; } nr_node.type = NETROM_NODE; nr_node.ndigis = 0; sprintf(neigh_buffer, "%s/obsolescence_count_initialiser", PROC_NR_SYSCTL_DIR); fp = fopen(neigh_buffer, "r"); if (fp == NULL) { if (logging) syslog(LOG_ERR, "netromr: cannot open %s\n", neigh_buffer); return; } fgets(neigh_buffer, 90, fp); obs_count = atoi(neigh_buffer); fclose(fp); s = socket(AF_NETROM, SOCK_SEQPACKET, 0); if (s < 0) { if (logging) syslog(LOG_ERR, "netromr: socket: %m"); return; } fp = fopen(PROC_NR_NEIGH_FILE, "r"); if (fp == NULL) { if (logging) syslog(LOG_ERR, "netromr: cannot open %s\n", PROC_NR_NEIGH_FILE); close(s); return; } fgets(neigh_buffer, 90, fp); portcall = ax25_ntoa(neighbour); quality = port_list[index].default_qual; while (fgets(neigh_buffer, 90, fp) != NULL) { strtok(neigh_buffer, " "); /* skip addr field */ callsign = strtok(NULL, " "); device = strtok(NULL, " "); qual = atoi(strtok(NULL, " ")); lock = atoi(strtok(NULL, " ")); if (strcmp(callsign, portcall) == 0 && strcmp(device, port_list[index].device) == 0 && lock == 1) { quality = qual; break; } } fclose(fp); nr_node.callsign = *neighbour; memcpy(nr_node.mnemonic, buffer, MNEMONIC_LEN); nr_node.mnemonic[MNEMONIC_LEN] = '\0'; p = strchr(nr_node.mnemonic, ' '); if (p != NULL) *p = '\0'; if (!validmnemonic(nr_node.mnemonic)) { if (debug && logging) syslog(LOG_DEBUG, "rejecting route, invalid mnemonic - %s\n", nr_node.mnemonic); } else { nr_node.neighbour = *neighbour; strcpy(nr_node.device, port_list[index].device); nr_node.quality = quality; nr_node.obs_count = obs_count; if (ioctl(s, SIOCADDRT, &nr_node) == -1) { if (logging) syslog(LOG_ERR, "netromr: SIOCADDRT: %m"); close(s); return; } } buffer += MNEMONIC_LEN; length -= MNEMONIC_LEN; while (length >= ROUTE_LEN) { nr_node.neighbour = *neighbour; strcpy(nr_node.device, port_list[index].device); nr_node.obs_count = obs_count; if (add_node(s, buffer, &nr_node, quality, index) == -1) { close(s); return; } buffer += ROUTE_LEN; length -= ROUTE_LEN; } close(s); } netrom/netromt.c000066400000000000000000000137521442776067000142430ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" #include "netromd.h" static int build_header(char *message) { message[0] = (char) NODES_SIG; strcpy(message + 1, nr_config_get_alias(NULL)); strncat(message + 1, " ", MNEMONIC_LEN - strlen(message + 1)); return 7; } static void build_mine(int s, struct full_sockaddr_ax25 *dest, int dlen, int localval, int pause) { char message[100]; char buffer[255], *port, *p; FILE *fp; int len; len = build_header(message); fp = fopen(CONF_NRPORTS_FILE, "r"); if (fp == NULL) { if (logging) syslog(LOG_ERR, "netromt: cannot open nrports file\n"); return; } while (fgets(buffer, 255, fp) != NULL) { p = strchr(buffer, '\n'); if (p != NULL) *p = '\0'; if (strlen(buffer) == 0 || buffer[0] == '#') continue; port = strtok(buffer, " \t"); if (nr_config_get_addr(port) == NULL) continue; if (strcmp(nr_config_get_addr(port), nr_config_get_addr(NULL)) == 0) continue; if (ax25_aton_entry(nr_config_get_addr(port), message + len) == -1) { if (logging) syslog(LOG_ERR, "netromt: invalid callsign in nrports\n"); fclose(fp); return; } len += CALLSIGN_LEN; strcpy(message + len, nr_config_get_alias(port)); strncat(message + len, " ", MNEMONIC_LEN - strlen(message + len)); len += MNEMONIC_LEN; ax25_aton_entry(nr_config_get_addr(NULL), message + len); len += CALLSIGN_LEN; message[len] = localval; len += QUALITY_LEN; } fclose(fp); if (sendto(s, message, len, 0, (struct sockaddr *)dest, dlen) == -1) { if (logging) syslog(LOG_ERR, "netromt: sendto: %m"); } sleep(pause); } static void build_others(int s, int min_obs, struct full_sockaddr_ax25 *dest, int dlen, int port, int pause) { char message[300]; FILE *fpnodes, *fpneigh; char nodes_buffer[90]; char neigh_buffer[90]; char *callsign, *mnemonic, *neighbour; int quality, neigh_no, obs_count; int olen, len; fpnodes = fopen(PROC_NR_NODES_FILE, "r"); if (fpnodes == NULL) { if (logging) syslog(LOG_ERR, "netromt: cannot open %s\n", PROC_NR_NODES_FILE); return; } fpneigh = fopen(PROC_NR_NEIGH_FILE, "r"); if (fpneigh == NULL) { if (logging) syslog(LOG_ERR, "netromt: cannot open %s\n", PROC_NR_NEIGH_FILE); fclose(fpnodes); return; } fgets(nodes_buffer, 90, fpnodes); do { len = olen = build_header(message); while (fgets(nodes_buffer, 90, fpnodes) != NULL) { callsign = strtok(nodes_buffer, " "); mnemonic = strtok(NULL, " "); strtok(NULL, " "); /* skip which field */ strtok(NULL, " "); /* skip number field */ quality = atoi(strtok(NULL, " ")); obs_count = atoi(strtok(NULL, " ")); neigh_no = atoi(strtok(NULL, " ")); neighbour = NULL; if (obs_count < min_obs || quality == 0) continue; /* "Blank" mnemonic */ if (strcmp(mnemonic, "*") == 0) mnemonic = ""; fseek(fpneigh, 0L, SEEK_SET); fgets(neigh_buffer, 90, fpneigh); while (fgets(neigh_buffer, 90, fpneigh) != NULL) { if (atoi(strtok(neigh_buffer, " ")) == neigh_no) { neighbour = strtok(NULL, " "); break; } } if (neighbour == NULL) { if (logging) syslog(LOG_ERR, "netromt: corruption in nodes/neighbour matching\n"); continue; } if (ax25_aton_entry(callsign, message + len) == -1) { if (logging) syslog(LOG_ERR, "netromt: invalid callsign '%s' in "PROC_NR_NODES_FILE"\n", callsign); continue; } len += CALLSIGN_LEN; strcpy(message + len, mnemonic); strncat(message + len, " ", MNEMONIC_LEN - strlen(message + len)); len += MNEMONIC_LEN; if (ax25_aton_entry(neighbour, message + len) == -1) { if (logging) syslog(LOG_ERR, "netromt: invalid callsign '%s' in "PROC_NR_NEIGH_FILE"\n", neighbour); len -= (CALLSIGN_LEN + MNEMONIC_LEN); continue; } len += CALLSIGN_LEN; message[len] = quality; len += QUALITY_LEN; /* No room for another entry? */ if (len + ROUTE_LEN > NODES_PACLEN) break; } /* Only send it if there is more that just the header */ if (len > olen) { if (sendto(s, message, len, 0, (struct sockaddr *)dest, dlen) == -1) { if (logging) syslog(LOG_ERR, "netromt: sendto: %m"); } sleep(pause); } /* If the packet was not full then we are done */ } while (len + ROUTE_LEN > NODES_PACLEN); fclose(fpnodes); fclose(fpneigh); } void transmit_nodes(int localval, int pause) { struct full_sockaddr_ax25 dest; struct full_sockaddr_ax25 src; int s, dlen, slen; char path[25], *addr; int i; switch (fork()) { case 0: break; case -1: if (logging) syslog(LOG_ERR, "netromt: fork: %m\n"); return; default: return; } dlen = ax25_aton("NODES", &dest); for (i = 0; i < port_count; i++) { addr = ax25_config_get_addr(port_list[i].port); if (addr == NULL) continue; if (debug && logging) syslog(LOG_DEBUG, "transmitting NODES broadcast on port %s\n", port_list[i].port); sprintf(path, "%s %s", nr_config_get_addr(NULL), addr); ax25_aton(path, &src); slen = sizeof(struct full_sockaddr_ax25); s = socket(AF_AX25, SOCK_DGRAM, NETROM_PID); if (s < 0) { if (logging) syslog(LOG_ERR, "netromt: socket: %m"); continue; } if (bind(s, (struct sockaddr *)&src, slen) == -1) { if (logging) syslog(LOG_ERR, "netromt: bind: %m"); close(s); continue; } build_mine(s, &dest, dlen, localval, pause); if (port_list[i].verbose) build_others(s, port_list[i].minimum_obs, &dest, dlen, i, pause); close(s); } s = socket(AF_NETROM, SOCK_SEQPACKET, 0); if (s < 0) { if (logging) syslog(LOG_ERR, "netromt: socket: %m"); exit(1); } if (ioctl(s, SIOCNRDECOBS, &i) == -1) { if (logging) syslog(LOG_ERR, "netromt: SIOCNRDECOBS: %m"); exit(1); } close(s); exit(0); } netrom/nodesave.8000066400000000000000000000017771442776067000143100ustar00rootroot00000000000000.TH NODESAVE 8 "27 April 2008" Linux "Linux System Managers Manual" .SH NAME nodesave \- Saves NET/ROM routing information .SH SYNOPSIS .B nodesave [-p path] [-v] [filename] .SH DESCRIPTION .LP .B Nodesave saves the contents of the /proc filesystem entries for the NET/ROM routing tables. The output of the program is in the form of a Bourne shell script that calls nrparms(8) to recreate the NET/ROM routing information. If no filename is given on the command line, the program writes the script to stdout. .LP Typically .B nodesave would be used when taking a system off-air so that the NET/ROM routing may be quickly restored when the system is brought back on-line. .SH OPTIONS .TP 10 .BI "\-p path" Set the path to the nrparms binary. This only affects the output of the program. Default is /usr/sbin. .TP 10 .BI "\-v" Display the version. .SH FILES .nf /proc/net/nr_neigh .br /proc/net/nr_nodes .fi .SH "SEE ALSO" .BR netrom (4), .BR netromd (8), .BR nrparms (8). .LP .SH AUTHOR Tomi Manninen OH2BNS netrom/nodesave.c000066400000000000000000000065361442776067000143610ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *Nrparms = "/usr/sbin/nrparms"; static char *Usage = "Usage: nodesave [-p ] [-v] []\n"; int main(int argc, char **argv) { FILE *fp = stdout; struct proc_nr_nodes *nodes, *nop; struct proc_nr_neigh *neighs, *nep; char buf[256]; int s; if (ax25_config_load_ports() == 0) { fprintf(stderr, "nodesave: no AX.25 port data configured\n"); return 1; } if (nr_config_load_ports() == 0) { fprintf(stderr, "nodesave: no NET/ROM port data configured\n"); return 1; } while ((s = getopt(argc, argv, "p:v")) != -1) { switch (s) { case 'p': sprintf(buf, "%s/nrparms", optarg); Nrparms = strdup(buf); break; case 'v': printf("nodesave: %s\n", FULL_VER); return 0; case ':': case '?': fputs(Usage, stderr); return 1; } } if ((argc - optind) > 1) { fputs(Usage, stderr); return 1; } if ((argc - optind) == 1) { fp = fopen(argv[optind], "w"); if (fp == NULL) { fprintf(stderr, "nodesave: cannot open file %s\n", argv[optind]); return 1; } } if ((neighs = read_proc_nr_neigh()) == NULL && errno != 0) { perror("nodesave: read_proc_nr_neigh"); fclose(fp); return 1; } if ((nodes = read_proc_nr_nodes()) == NULL && errno != 0) { perror("nodesave: read_proc_nr_nodes"); free_proc_nr_neigh(neighs); fclose(fp); return 1; } fprintf(fp, "#! /bin/sh\n#\n# Locked routes:\n#\n"); for (nep = neighs; nep != NULL; nep = nep->next) { if (nep->lock) { fprintf(fp, "%s -routes ", Nrparms); sprintf(buf, "\"%s\"", ax25_config_get_name(nep->dev)); fprintf(fp, "%-8s %-9s + %d\n", buf, nep->call, nep->qual); } } fprintf(fp, "#\n# Nodes:\n#\n"); for (nop = nodes; nop != NULL; nop = nop->next) { /* * nop->n == 0 indicates a local node with no routes. */ if (nop->n > 0 && (nep = find_neigh(nop->addr1, neighs)) != NULL) { fprintf(fp, "%s -nodes %-9s + ", Nrparms, nop->call); sprintf(buf, "\"%s\"", nop->alias); fprintf(fp, "%-8s %-3d %d ", buf, nop->qual1, nop->obs1); sprintf(buf, "\"%s\"", ax25_config_get_name(nep->dev)); fprintf(fp, "%-8s %s\n", buf, nep->call); } if (nop->n > 1 && (nep = find_neigh(nop->addr2, neighs)) != NULL) { fprintf(fp, "%s -nodes %-9s + ", Nrparms, nop->call); sprintf(buf, "\"%s\"", nop->alias); fprintf(fp, "%-8s %-3d %d ", buf, nop->qual2, nop->obs2); sprintf(buf, "\"%s\"", ax25_config_get_name(nep->dev)); fprintf(fp, "%-8s %s\n", buf, nep->call); } if (nop->n > 2 && (nep = find_neigh(nop->addr3, neighs)) != NULL) { fprintf(fp, "%s -nodes %-9s + ", Nrparms, nop->call); sprintf(buf, "\"%s\"", nop->alias); fprintf(fp, "%-8s %-3d %d ", buf, nop->qual3, nop->obs3); sprintf(buf, "\"%s\"", ax25_config_get_name(nep->dev)); fprintf(fp, "%-8s %s\n", buf, nep->call); } } free_proc_nr_neigh(neighs); free_proc_nr_nodes(nodes); fclose(fp); if ((argc - optind) == 1) { if (chmod(argv[optind], 0755) == -1) { perror("nodesave: chmod"); return 1; } } return 0; } netrom/nrattach.8000066400000000000000000000024101442776067000142710ustar00rootroot00000000000000.TH NRATTACH 8 "21 May 1996" Linux "Linux System Managers Manual" .SH NAME nrattach \- Start a NET/ROM interface .SH SYNOPSIS .B nrattach [-i inetaddr] [-m mtu] [-v] port .SH DESCRIPTION .LP .B Nrattach takes many of the parameters for the port from the nrports(5) file. The paclen parameter is used for the device mtu unless overridden by a value on the command line. The port argument is the name of a port as given in the nrports(5) file. .LP .B Nrattach tries to find the first free NET/ROM device in the system. The devices checked are nr0, nr1, nr2 and nr3 in that order. If no free NET/ROM device is available an error is generated and the program terminates. .SH OPTIONS .TP 16 .BI "\-i inetaddr" Set the internet address of the interface. This address may either be a dotted decimal address or a host name. .TP 16 .BI "\-m mtu" Sets the mtu of the interface. If this value is not given then the value is taken from the paclen parameter in nrports. .TP 16 .BI \-v Display the version. .SH "SEE ALSO" .BR netrom (4), .BR nrparms (4), .BR nrports (5), .BR ifconfig (8). .SH BUGS The program can be run many times with the same arguments creating many instances of the same attributes on different devices. Not a good idea. .SH AUTHOR Jonathan Naylor G4KLX netrom/nrattach.c000066400000000000000000000112541442776067000143520ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" static char *callsign; static int mtu; static int readconfig(char *port) { FILE *fp; char buffer[90], *s; int n = 0; fp = fopen(CONF_NRPORTS_FILE, "r"); if (fp == NULL) { fprintf(stderr, "nrattach: cannot open nrports file\n"); return FALSE; } while (fgets(buffer, 90, fp) != NULL) { n++; s = strchr(buffer, '\n'); if (s != NULL) *s = '\0'; if (strlen(buffer) > 0 && *buffer == '#') continue; s = strtok(buffer, " \t\r\n"); if (s == NULL) { fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n); return FALSE; } if (strcmp(s, port) != 0) continue; s = strtok(NULL, " \t\r\n"); if (s == NULL) { fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n); return FALSE; } callsign = strdup(s); s = strtok(NULL, " \t\r\n"); if (s == NULL) { fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n); return FALSE; } s = strtok(NULL, " \t\r\n"); if (s == NULL) { fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n); return FALSE; } if (mtu == 0) { mtu = atoi(s); if (mtu <= 0) { fprintf(stderr, "nrattach: invalid paclen setting\n"); return FALSE; } } fclose(fp); return TRUE; } fclose(fp); fprintf(stderr, "nrattach: cannot find port %s in nrports\n", port); return FALSE; } static int getfreedev(char *dev) { struct ifreq ifr; int fd; int i; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("nrattach: socket"); return FALSE; } for (i = 0; i < INT_MAX; i++) { sprintf(dev, "nr%d", i); strcpy(ifr.ifr_name, dev); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { perror("nrattach: SIOCGIFFLAGS"); return FALSE; } if (!(ifr.ifr_flags & IFF_UP)) { close(fd); return TRUE; } } close(fd); return FALSE; } static int startiface(char *dev, struct hostent *hp) { struct ifreq ifr; char call[7]; int fd; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("nrattach: socket"); return FALSE; } strcpy(ifr.ifr_name, dev); if (hp != NULL) { ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_data[0] = 0; ifr.ifr_addr.sa_data[1] = 0; ifr.ifr_addr.sa_data[2] = hp->h_addr_list[0][0]; ifr.ifr_addr.sa_data[3] = hp->h_addr_list[0][1]; ifr.ifr_addr.sa_data[4] = hp->h_addr_list[0][2]; ifr.ifr_addr.sa_data[5] = hp->h_addr_list[0][3]; ifr.ifr_addr.sa_data[6] = 0; if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { perror("nrattach: SIOCSIFADDR"); return FALSE; } } if (ax25_aton_entry(callsign, call) == -1) return FALSE; ifr.ifr_hwaddr.sa_family = ARPHRD_NETROM; memcpy(ifr.ifr_hwaddr.sa_data, call, 7); if (ioctl(fd, SIOCSIFHWADDR, &ifr) != 0) { perror("nrattach: SIOCSIFHWADDR"); return FALSE; } ifr.ifr_mtu = mtu; if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) { perror("nrattach: SIOCSIFMTU"); return FALSE; } if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { perror("nrattach: SIOCGIFFLAGS"); return FALSE; } ifr.ifr_flags &= IFF_NOARP; ifr.ifr_flags |= IFF_UP; ifr.ifr_flags |= IFF_RUNNING; if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { perror("nrattach: SIOCSIFFLAGS"); return FALSE; } close(fd); return TRUE; } int main(int argc, char *argv[]) { int fd; char dev[64]; struct hostent *hp = NULL; while ((fd = getopt(argc, argv, "i:m:v")) != -1) { switch (fd) { case 'i': hp = gethostbyname(optarg); if (hp == NULL) { fprintf(stderr, "nrattach: invalid internet name/address - %s\n", optarg); return 1; } break; case 'm': mtu = atoi(optarg); if (mtu <= 0) { fprintf(stderr, "nrattach: invalid mtu size - %s\n", optarg); return 1; } break; case 'v': printf("nrattach: %s\n", FULL_VER); return 0; case ':': case '?': fprintf(stderr, "usage: nrattach [-i inetaddr] [-m mtu] [-v] port\n"); return 1; } } if ((argc - optind) != 1) { fprintf(stderr, "usage: nrattach [-i inetaddr] [-m mtu] [-v] port\n"); return 1; } if (!readconfig(argv[optind])) return 1; if (!getfreedev(dev)) { fprintf(stderr, "nrattach: cannot find free NET/ROM device\n"); return 1; } if (!startiface(dev, hp)) return 1; printf("NET/ROM port %s bound to device %s\n", argv[optind], dev); return 0; } netrom/nrbroadcast000066400000000000000000000002101442776067000146150ustar00rootroot00000000000000# /etc/ax25/nrbroadcast # # The format of this file is: # # ax25_name min_obs def_qual worst_qual verbose # 1 5 192 100 0 2 5 255 100 1 netrom/nrbroadcast.5000066400000000000000000000022431442776067000147700ustar00rootroot00000000000000.TH NRBROADCAST 5 "2 August 1996" Linux "Linux Programmer's Manual" .SH NAME nrbroadcast \- NET/ROM routing broadcast configuration file. .SH DESCRIPTION .LP .B Nrbroadcast is an ASCII file that contains information about each of the physical AX.25 ports that are to have NET/ROM routing broadcasts transmitted from them. .LP Each line has the following format, each field being delimited by white space: .sp .RS port minobs defqual worstqual verbose .RE .sp The field descriptions are: .sp .RS .TP 14 .B port the port name of the AX.25 port to broadcast on. .TP .B minobs this is the minimum obsolescence count of a routing table entry to be broadcast on this port. .TP .B defqual this is the default quality of an incoming routing broadcast from an unknown neighbour. .TP .B worstqual this is the worst quality node received from a routing broadcast that will be added to our routing table. .TP .B verbose whether the routes to all of my nodes in my routing table, or just the nodes resident on my machine are to be broadcast. .RE .SH FILES .LP /etc/ax25/nrbroadcast .br /etc/ax25/axports .SH "SEE ALSO" .BR call (1), .BR netrom (4), .BR axports (5), .BR netromd (8), .BR nrparms (8). netrom/nrparms.8000066400000000000000000000062431442776067000141570ustar00rootroot00000000000000.TH NRPARMS 8 "15 July 2011" Linux "Linux System Managers Manual" .SH NAME nrparms \- Configure the NET/ROM interface. .SH SYNOPSIS .B nrparms -nodes node +|- ident quality count port neighbour [digicall...] .LP .B nrparms -routes port nodecall [digicall...] +|- quality .LP .B nrparms -version .SH DESCRIPTION .LP This program is used to manipulate the routing tables of the NET/ROM network layer, or to get and set many of the network and transport layer parameters for the NET/ROM protocol. The program has three basic modes of operation, node setting, neighbour setting and general parameter setting. The syntax for the node and neighbour setting is taken from the original NET/ROM manual and is therefore not very UNIXy but should be familiar to those familiar with NET/ROMs or TheNet. .LP To set up a new route to a NET/ROM node in the routing tables you must use the nodes option. All of the parameters are needed to add the node. It is probably best to illustrate with an example: .LP .B nrparms -nodes GB7BPQ + NMCLUS 120 6 vhf G8ROU-3 .LP This creates a new route to a distant node with the callsign GB7BPQ and the alias NMCLUS, it has a quality of 120 (out of 255) and has an obsolescence count of six and packets for that node should be sent on the AX.25 port named vhf to my immediate neighbour G8ROU-3. The callsigns of the node and the neighbour may be the same. For example to set up the node G8ROU-3 which is also my immediate neighbour, I would use: .LP .B nrparms -nodes G8ROU-3 + MATLCK 200 6 vhf G8ROU-3 .LP If the ident of the remote node is not known, it is possible to add a node with a blank ident. To do this an ident of '*' must be entered on the command line. Because of the command line expansion that shells do, the * must be escaped by enclosing it in quotes. .LP It is also possible to remove a route to a distant node with the same command except that the + is replaced by a -. The other parameters must also be present. If the node has not other routes then the node will be deleted, and the neighbour node that the connections go via may also be deleted if no other node route uses it, and it is not a locked neighbour entry. .LP When setting up a new node, a new neighbour entry may also be created. This will have a default value. For that neighbour to be meaningful in the automatic routing process, it must have a more reasonable entry in the neighbours list. To do this the routes option of the command must be used. An example: .LP .B nrparms -routes ax0 G8ROU-3 + 120 .LP This will create (or update) the neighbour entry for G8ROU-3 with a quality of 120 and it will be locked, it will not create a node entry for the neighbour. This quality will be used by the .BR netromd (8) program when calculating route qualities via this neighbour. Normally once a neighbour has zero node routes going via it, it will be deleted. Locking a neighbour prevents the deletion from occurring. To unlock a neighbour entry, the same command is used but with the + replaced by a -. .SH FILES .LP /etc/ax25/axports .br /etc/ax25/nrports .SH "SEE ALSO" .BR call (1), .BR netrom (4), .BR nrports (5), .BR axparms (8), .BR netromd (8), .BR nrparms (8). .SH AUTHOR Jonathan Naylor G4KLX netrom/nrparms.c000066400000000000000000000123751442776067000142350ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char nodes_usage[] = "usage: nrparms -nodes nodecall +|- " "ident quality count port neighbour [digicall...]\n"; static char routes_usage[] = "usage: nrparms -routes port nodecall " "[digicall...] +|- pathquality\n"; static void nodes(int s, char *nodecall, char *op, char *ident, int quality, int count, char *port, char *neighbour, char *digis[]) { struct nr_route_struct nr_node; char *p, *q, *dev; int i; if (ax25_config_load_ports() == 0) { fprintf(stderr, "nrparms: nodes: no AX.25 ports configured\n"); exit(1); } nr_node.type = NETROM_NODE; nr_node.ndigis = 0; if (op[0] != '+' && op[0] != '-') { fprintf(stderr, "nrparms: nodes: invalid operation %s\n", op); close(s); exit(1); } if (quality < 1 || quality > 255) { fprintf(stderr, "nrparms: nodes: invalid quality %d\n", quality); close(s); exit(1); } if (count < 1 || count > 6) { fprintf(stderr, "nrparms: nodes: invalid obs count %d\n", count); close(s); exit(1); } if (ax25_aton_entry(nodecall, nr_node.callsign.ax25_call) != 0) { fprintf(stderr, "nrparms: nodes: invalid callsign %s\n", nodecall); close(s); exit(1); } if (strlen(ident) > 7) { fprintf(stderr, "nrparms: nodes: invalid length of mnemonic %s\n", ident); close(s); exit(1); } if (strcmp(ident, "*") != 0) { for (p = ident, q = nr_node.mnemonic; *p != '\0'; p++, q++) *q = toupper(*p); *q = '\0'; if (strspn(nr_node.mnemonic, "&#-_/ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") != strlen(nr_node.mnemonic)) { fprintf(stderr, "nrparms: nodes: invalid ident %s\n", ident); close(s); exit(1); } } else { strcpy(nr_node.mnemonic, ""); } if (ax25_aton_entry(neighbour, nr_node.neighbour.ax25_call) != 0) { fprintf(stderr, "nrparms: nodes: invalid callsign %s\n", neighbour); close(s); exit(1); } for (i = 0; i < AX25_MAX_DIGIS && digis[i] != NULL; i++) { if (ax25_aton_entry(digis[i], nr_node.digipeaters[i].ax25_call) != 0) { fprintf(stderr, "nrparms: invalid callsign %s\n", digis[i]); close(s); exit(1); } nr_node.ndigis++; } dev = ax25_config_get_dev(port); if (dev == NULL) { fprintf(stderr, "nrparms: nodes: invalid port name - %s\n", port); close(s); exit(1); } strcpy(nr_node.device, dev); nr_node.quality = quality; nr_node.obs_count = count; if (op[0] == '+') { if (ioctl(s, SIOCADDRT, &nr_node) == -1) { perror("nrparms: SIOCADDRT"); close(s); exit(1); } } else { if (ioctl(s, SIOCDELRT, &nr_node) == -1) { perror("nrparms: SIOCDELRT"); close(s); exit(1); } } } static void routes(int s, char *port, char *nodecall, char *rest[]) { struct nr_route_struct nr_neigh; char *dev, *op; int i, quality; if (ax25_config_load_ports() == 0) { fprintf(stderr, "nrparms: routes: no AX.25 ports configured\n"); exit(1); } nr_neigh.type = NETROM_NEIGH; nr_neigh.ndigis = 0; for (i = 0; i < AX25_MAX_DIGIS && rest[i][0] != '-' && rest[i][0] != '+'; i++) { if (ax25_aton_entry(rest[i], nr_neigh.digipeaters[i].ax25_call) != 0) { fprintf(stderr, "nrparms: routes: invalid callsign %s\n", rest[i]); close(s); exit(1); } nr_neigh.ndigis++; } op = rest[i + 0]; quality = atoi(rest[i + 1]); if (op[0] != '+' && op[0] != '-') { fprintf(stderr, "nrparms: routes: invalid operation %s\n", op); close(s); exit(1); } if (quality < 1 || quality > 255) { fprintf(stderr, "nrparms: routes: invalid quality %d\n", quality); close(s); exit(1); } dev = ax25_config_get_dev(port); if (dev == NULL) { fprintf(stderr, "nrparms: routes: invalid port name - %s\n", port); close(s); exit(1); } strcpy(nr_neigh.device, dev); if (ax25_aton_entry(nodecall, nr_neigh.callsign.ax25_call) != 0) { fprintf(stderr, "nrparms: routes: invalid callsign %s\n", nodecall); close(s); exit(1); } nr_neigh.quality = quality; if (op[0] == '+') { if (ioctl(s, SIOCADDRT, &nr_neigh) == -1) { perror("nrparms: SIOCADDRT"); close(s); exit(1); } } else { if (ioctl(s, SIOCDELRT, &nr_neigh) == -1) { perror("nrparms: SIOCDELRT"); close(s); exit(1); } } } int main(int argc, char *argv[]) { int s; if (argc == 1) { fprintf(stderr, "usage: nrparms -nodes|-routes|-version ...\n"); return 1; } if (strncmp(argv[1], "-v", 2) == 0) { printf("nrparms: %s\n", FULL_VER); return 0; } s = socket(AF_NETROM, SOCK_SEQPACKET, 0); if (s < 0) { perror("nrparms: socket"); return 1; } if (strncmp(argv[1], "-n", 2) == 0) { if (argc < 9) { fprintf(stderr, "%s", nodes_usage); close(s); return 1; } nodes(s, argv[2], argv[3], argv[4], atoi(argv[5]), atoi(argv[6]), argv[7], argv[8], argv + 9); close(s); return 0; } if (strncmp(argv[1], "-r", 2) == 0) { if (argc < 6) { fprintf(stderr, "%s", routes_usage); close(s); return 1; } routes(s, argv[2], argv[3], argv + 4); close(s); return 0; } fprintf(stderr, "usage: nrparms -nodes|-routes|-version ...\n"); close(s); return 1; } netrom/nrsdrv.8000066400000000000000000000025061442776067000140110ustar00rootroot00000000000000.TH NRSDRV 8 "22 December 1996" Linux "Linux System Managers Manual" .SH NAME nrsdrv \- KISS to NET/ROM serial converter .SH SYNOPSIS .B nrsdrv [-f] [-l] [-s speed] [-v] kissdev nrsdev .SH DESCRIPTION .LP .B Nrsdrv is a program designed to convert from the KISS protocol to the NET/ROM serial protocol used by real NET/ROM's and TheNet's. The protocols are fairly similar, although the NET/ROM serial protocol does include a one byte checksum which KISS does not. .sp 1 Typically .B nrsdrv will be attached to one end of a pseudo-tty of which the other end has been attached to a KISS capable program. The NET/ROM device will probably be a real serial port attached to a TNC or a Hexipus. The full specification of the NET/ROM serial protocol can be found in the original Software 2000 documentation that accompanied NET/ROMs. .SH OPTIONS .TP 10 .BI \-f Flow control enabled for use with a Hexipus, the default is disabled. See the file hexipus.txt in the source distribution for wiring details. .TP 10 .BI \-l Log messages to the system log, the default is not to. .TP 10 .BI "\-s speed" Sets the speed of both interfaces. If no value is specified then no speed will be set. .TP 10 .BI \-v Display the version. .SH BUGS None known. .SH "SEE ALSO" .BR kill (1), .BR stty (1), .BR ax25 (4). .SH AUTHOR Jonathan Naylor G4KLX netrom/nrsdrv.c000066400000000000000000000202351442776067000140630ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" static int kissfd, nrsfd; static int logging = FALSE; static int debugging = FALSE; static int flowcontrol = FALSE; static char *kissdev, *nrsdev; #define NUL 000 #define STX 002 #define ETX 003 #define DLE 020 #define NRS_WAIT 0 #define NRS_DATA 1 #define NRS_ESCAPE 2 #define NRS_CKSUM 3 static int nrs_state = NRS_WAIT; static unsigned char nrs_cksum; static unsigned char nrs_rxbuffer[512]; static int nrs_rxcount; #define FEND 0300 #define FESC 0333 #define FESCEND 0334 #define FESCESC 0335 #define KISS_WAIT 0 #define KISS_CTRL 1 #define KISS_DATA 2 #define KISS_ESCAPE 3 static int kiss_state = KISS_WAIT; static unsigned char kiss_rxbuffer[512]; static int kiss_rxcount; static void terminate(int sig) { if (logging) { syslog(LOG_INFO, "terminating on SIGTERM\n"); closelog(); } tty_unlock(kissdev); tty_unlock(nrsdev); exit(0); } static void key_rts(int fd) { int status; if (!flowcontrol) return; /* Wait for CTS to be low */ while (1) { /* Get CTS status */ if (ioctl(fd, TIOCMGET, &status) < 0) { syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n"); flowcontrol = 0; return; } if (status & TIOCM_CTS) { if (debugging) { fprintf(stderr,"CTS high: waiting\n"); } ioctl(fd, TIOCMIWAIT, &status); } else { break; } } if (debugging) { fprintf(stderr,"CTS low: keying RTS\n"); } status |= TIOCM_RTS | TIOCM_DTR; if (ioctl(fd, TIOCMSET, &status) < 0) { syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n"); flowcontrol = 0; } } static void unkey_rts(int fd) { int status; if (!flowcontrol) return; if (debugging) { fprintf(stderr,"Transmission finished: unkeying RTS\n"); } ioctl(fd, TIOCMGET, &status); status &= ~TIOCM_RTS; status |= TIOCM_DTR; if (ioctl(fd, TIOCMSET, &status) < 0) { syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n"); flowcontrol = 0; } } static void nrs_esc(unsigned char *s, int len) { static unsigned char buffer[512]; unsigned char *ptr = buffer; unsigned char csum = 0; unsigned char c; *ptr++ = STX; while (len-- > 0) { switch (c = *s++) { case STX: case ETX: case DLE: *ptr++ = DLE; /* Fall through */ default: *ptr++ = c; break; } csum += c; } *ptr++ = ETX; *ptr++ = csum; *ptr++ = NUL; *ptr++ = NUL; key_rts(nrsfd); write(nrsfd, buffer, ptr - buffer); unkey_rts(nrsfd); } static void kiss_esc(unsigned char *s, int len) { static unsigned char buffer[512]; unsigned char *ptr = buffer; unsigned char c; *ptr++ = FEND; *ptr++ = 0x00; /* KISS DATA */ while (len-- > 0) { switch (c = *s++) { case FESC: *ptr++ = FESC; *ptr++ = FESCESC; break; case FEND: *ptr++ = FESC; *ptr++ = FESCEND; break; default: *ptr++ = c; break; } } *ptr++ = FEND; write(kissfd, buffer, ptr - buffer); } static void nrs_unesc(unsigned char *buffer, int len) { int i; for (i = 0; i < len; i++) { switch (nrs_state) { case NRS_WAIT: if (buffer[i] == STX) { nrs_state = NRS_DATA; nrs_rxcount = 0; nrs_cksum = 0; } break; case NRS_DATA: switch (buffer[i]) { case STX: /* !! */ nrs_rxcount = 0; nrs_cksum = 0; break; case DLE: nrs_state = NRS_ESCAPE; break; case ETX: nrs_state = NRS_CKSUM; break; default: if (nrs_rxcount < 512) { nrs_cksum += buffer[i]; nrs_rxbuffer[nrs_rxcount++] = buffer[i]; } break; } break; case NRS_ESCAPE: nrs_state = NRS_DATA; if (nrs_rxcount < 512) { nrs_cksum += buffer[i]; nrs_rxbuffer[nrs_rxcount++] = buffer[i]; } break; case NRS_CKSUM: if (buffer[i] == nrs_cksum) kiss_esc(nrs_rxbuffer, nrs_rxcount); nrs_state = NRS_WAIT; nrs_cksum = 0; nrs_rxcount = 0; break; } } } static void kiss_unesc(unsigned char *buffer, int len) { int i; for (i = 0; i < len; i++) { switch (kiss_state) { case KISS_WAIT: if (buffer[i] == FEND) { kiss_state = KISS_CTRL; kiss_rxcount = 0; } break; case KISS_CTRL: if ((buffer[i] & 0x0F) == 0x00) { kiss_state = KISS_DATA; kiss_rxcount = 0; } else { kiss_state = KISS_WAIT; kiss_rxcount = 0; } break; case KISS_DATA: switch (buffer[i]) { case FEND: if (kiss_rxcount > 2) nrs_esc(kiss_rxbuffer, kiss_rxcount); kiss_state = KISS_WAIT; kiss_rxcount = 0; break; case FESC: kiss_state = KISS_ESCAPE; break; default: if (kiss_rxcount < 512) kiss_rxbuffer[kiss_rxcount++] = buffer[i]; break; } break; case KISS_ESCAPE: kiss_state = KISS_DATA; switch (buffer[i]) { case FESCESC: if (kiss_rxcount < 512) kiss_rxbuffer[kiss_rxcount++] = FESC; break; case FESCEND: if (kiss_rxcount < 512) kiss_rxbuffer[kiss_rxcount++] = FEND; break; } break; } } } int main(int argc, char *argv[]) { static unsigned char buffer[512]; unsigned int speed = 0; fd_set read_fd; int c, n; while ((c = getopt(argc, argv, "dfls:v")) != -1) { switch (c) { case 'd': debugging = TRUE; break; case 'f': flowcontrol = TRUE; break; case 'l': logging = TRUE; break; case 's': speed = atoi(optarg); if (speed <= 0) { fprintf(stderr, "nrsdrv: invalid speed %s\n", optarg); return 1; } break; case 'v': printf("kissattach: %s\n", FULL_VER); return 0; case ':': case '?': fprintf(stderr, "usage: nrsdrv [-f] [-l] [-s speed] [-v] kisstty nrstty\n"); return 1; } } if (debugging) { fprintf(stderr,"Flow control %s\n", flowcontrol ? "enabled" : "disabled"); } if ((argc - optind) != 2) { fprintf(stderr, "usage: nrsdrv [-f] [-l] [-s speed] [-v] kisstty nrstty\n"); return 1; } kissdev = argv[optind + 0]; nrsdev = argv[optind + 1]; if (tty_is_locked(kissdev)) { fprintf(stderr, "nrsdrv: device %s already in use\n", argv[optind]); return 1; } if (tty_is_locked(nrsdev)) { fprintf(stderr, "nrsdrv: device %s already in use\n", argv[optind + 1]); return 1; } kissfd = open(kissdev, O_RDWR); if (kissfd == -1) { perror("nrsdrv: open kiss device"); return 1; } nrsfd = open(nrsdev, O_RDWR); if (nrsfd == -1) { perror("nrsdrv: open nrs device"); return 1; } tty_lock(kissdev); tty_lock(nrsdev); if (!tty_raw(kissfd, FALSE)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (!tty_raw(nrsfd, FALSE)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (speed != 0 && !tty_speed(kissfd, speed)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (speed != 0 && !tty_speed(nrsfd, speed)) { tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (logging) { openlog("nrsdrv", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "KISS device %s connected to NRS device %s\n", argv[optind + 0], argv[optind + 1]); } signal(SIGHUP, SIG_IGN); signal(SIGTERM, terminate); /* * Become a daemon if we can. */ if (!daemon_start(FALSE)) { fprintf(stderr, "nrsdrv: cannot become a daemon\n"); tty_unlock(kissdev); tty_unlock(nrsdev); return 1; } if (flowcontrol) { unkey_rts(nrsfd); } c = ((kissfd > nrsfd) ? kissfd : nrsfd) + 1; for (;;) { FD_ZERO(&read_fd); FD_SET(kissfd, &read_fd); FD_SET(nrsfd, &read_fd); n = select(c, &read_fd, NULL, NULL, NULL); if (FD_ISSET(kissfd, &read_fd)) { n = read(kissfd, buffer, 512); if (n <= 0) { if (logging) { syslog(LOG_INFO, "terminating on KISS device closure\n"); closelog(); } break; } kiss_unesc(buffer, n); } if (FD_ISSET(nrsfd, &read_fd)) { n = read(nrsfd, buffer, 512); if (n <= 0) { if (logging) { syslog(LOG_INFO, "terminating on NRS device closure\n"); closelog(); } break; } nrs_unesc(buffer, n); } } tty_unlock(kissdev); tty_unlock(nrsdev); return 0; } pathnames.h000066400000000000000000000023471442776067000132320ustar00rootroot00000000000000 #define CONF_AX25D_FILE AX25_SYSCONFDIR"ax25d.conf" #define CONF_AXPORTS_FILE AX25_SYSCONFDIR"axports" #define CONF_AXSPAWN_FILE AX25_SYSCONFDIR"axspawn.conf" #define CONF_AXSPAWN_PROF_FILE AX25_SYSCONFDIR"ax25.profile" #define CONF_NETROMD_FILE AX25_SYSCONFDIR"nrbroadcast" #define CONF_NRPORTS_FILE AX25_SYSCONFDIR"nrports" #define CONF_RIP98D_FILE AX25_SYSCONFDIR"rip98d.conf" #define CONF_RSPORTS_FILE AX25_SYSCONFDIR"rsports" #define CONF_RXECHO_FILE AX25_SYSCONFDIR"rxecho.conf" #define CONF_TTYLINKD_FILE AX25_SYSCONFDIR"ttylinkd.conf" #define DATA_MHEARD_FILE AX25_LOCALSTATEDIR"mheard/mheard.dat" #define LOCK_AXSPAWN_FILE "/var/lock/axspawn" #define LOCK_SERIAL_DIR "/var/lock" #define PROC_LOADAVG_FILE "/proc/loadavg" #define PROC_MEMINFO_FILE "/proc/meminfo" #define PROC_UPTIME_FILE "/proc/uptime" #define PROC_AX25_FILE "/proc/net/ax25" #define PROC_AX25_ROUTE_FILE "/proc/net/ax25_route" #define PROC_IP_ROUTE_FILE "/proc/net/route" #define PROC_NR_SYSCTL_DIR "/proc/sys/net/netrom" #define PROC_RS_NEIGH_FILE "/proc/net/rose_neigh" #define PROC_RS_NODES_FILE "/proc/net/rose_nodes" #define PROC_AX25_CALLS_FILE "/proc/net/ax25_calls" #define PROC_NR_NEIGH_FILE "/proc/net/nr_neigh" #define PROC_NR_NODES_FILE "/proc/net/nr_nodes" rose/000077500000000000000000000000001442776067000120435ustar00rootroot00000000000000rose/.gitignore000066400000000000000000000001121442776067000140250ustar00rootroot00000000000000*.o .deps Makefile Makefile.in rsattach rsdwnlnk rsmemsiz rsparms rsuplnk rose/Makefile.am000066400000000000000000000007061442776067000141020ustar00rootroot00000000000000installconf: sbin_PROGRAMS = rsattach rsdwnlnk rsmemsiz rsparms rsuplnk LDADD = $(AX25_LIB) rsmemsiz_LDADD = sbin_SCRIPTS = rsusers.sh dist_man_MANS = rose.4 rsattach.8 rsparms.8 rsdwnlnk.8 rsuplnk.8 EXTRA_DIST = $(sbin_SCRIPTS) AM_CPPFLAGS = -D_GNU_SOURCE \ -DAX25_SYSCONFDIR=\""$(AX25_SYSCONFDIR)"\" \ -DAX25_LOCALSTATEDIR=\""$(AX25_LOCALSTATEDIR)"\" AX25_SYSCONFDIR=${sysconfdir}/ax25/ AX25_LOCALSTATEDIR=${localstatedir}/ax25/ rose/rose.4000066400000000000000000000031211442776067000130750ustar00rootroot00000000000000.TH ROSE 4 "15 July 2011" Linux "Linux Programmer's Manual" .SH NAME AF_ROSE \- Rose amateur packet radio protocol family .SH DESCRIPTION .LP .B Rose is a protocol used extensively by radio amateurs. The Linux Rose protocol family permits access to these protocols via the standard networking .B socket metaphor. .LP The Rose protocol layer only supports connected mode. .LP The only mode of operation is connected mode which is the mode used for a socket of type SOCK_SEQPACKET (stream sockets are not available in Rose). This requires that the user ensures output data is suitably packetised, and that input data is read a packet at a time into a buffer of suitable size. .LP Rose addresses consist of 10 digits. These are encoded into a sockaddr_rose structure which is provided to the relevant system calls. .LP Rose has some unusual properties. Notably in a multi-user system an AX.25 address is often associated with a user, and some users may not have such an association. a set of ioctl calls are provided to manage an association table. .LP Rose supports the following socket options for SOL_ROSE. ROSE_T1 is the T11/T21 timer in 1/10ths of a second, ROSE_T2 is the T12/T22 timer in 1/10ths of a second. ROSE_T3, is the T13/T23 timer in 1/10ths of a second. It is possible for an application to request that the Rose layer return the Rose header as well as the application data, this is done via the ROSE_HDRINCL socket option. .SH "SEE ALSO" .BR call (1), .BR socket (2), .BR setsockopt(2), .BR getsockopt(2), .BR rsports (5), .BR rsparms (8). .LP .SH AUTHOR Jonathan Naylor G4KLX rose/rsattach.8000066400000000000000000000017471442776067000137560ustar00rootroot00000000000000.TH RSATTACH 8 "27 August 1996" Linux "Linux System Managers Manual" .SH NAME rsattach \- Start a Rose interface .SH SYNOPSIS .B rsattach [-i inetaddr] [-v] port .SH DESCRIPTION .LP .B Rsattach takes many of the parameters for the port from the rsports(5) file. The port argument is the name of a port as given in the rsports(5) file. .LP .B Rsattach tries to find the free Rose device in the system. The device name checked is rose0. If no free Rose device is available an error is generated and the program terminates. .SH OPTIONS .TP 16 .BI "\-i inetaddr" Set the internet address of the interface. This address may either be a dotted decimal address or a host name. .TP 16 .BI \-v Display the version. .SH "SEE ALSO" .BR rose (4), .BR rsparms (4), .BR rsports (5), .BR ifconfig (8). .SH BUGS The program can be run many times with the same arguments creating many instances of the same attributes on different devices. Not a good idea. .SH AUTHOR Jonathan Naylor G4KLX rose/rsattach.c000066400000000000000000000101401442776067000140140ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include */ #include #include /* #include */ #include #include #include #include #include "../pathnames.h" static char *address; static int mtu = 128; static int readconfig(char *port) { FILE *fp; char buffer[90], *s; int n = 0; fp = fopen(CONF_RSPORTS_FILE, "r"); if (fp == NULL) { fprintf(stderr, "rsattach: cannot open rsports file\n"); return FALSE; } while (fgets(buffer, 90, fp) != NULL) { n++; s = strchr(buffer, '\n'); if (s != NULL) *s = '\0'; if (strlen(buffer) > 0 && *buffer == '#') continue; s = strtok(buffer, " \t\r\n"); if (s == NULL) { fprintf(stderr, "rsattach: unable to parse line %d of the rsports file\n", n); return FALSE; } if (strcmp(s, port) != 0) continue; s = strtok(NULL, " \t\r\n"); if (s == NULL) { fprintf(stderr, "rsattach: unable to parse line %d of the rsports file\n", n); return FALSE; } address = strdup(s); fclose(fp); return TRUE; } fclose(fp); fprintf(stderr, "rsattach: cannot find port %s in rsports\n", port); return FALSE; } static int getfreedev(char *dev) { struct ifreq ifr; int fd; int i; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("rsattach: socket"); return FALSE; } for (i = 0; i < INT_MAX; i++) { sprintf(dev, "rose%d", i); strcpy(ifr.ifr_name, dev); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { perror("rsattach: SIOCGIFFLAGS"); return FALSE; } if (!(ifr.ifr_flags & IFF_UP)) { close(fd); return TRUE; } } close(fd); return FALSE; } static int startiface(char *dev, struct hostent *hp) { struct ifreq ifr; char addr[5]; int fd; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("rsattach: socket"); return FALSE; } strcpy(ifr.ifr_name, dev); if (hp != NULL) { ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_addr.sa_data[0] = 0; ifr.ifr_addr.sa_data[1] = 0; ifr.ifr_addr.sa_data[2] = hp->h_addr_list[0][0]; ifr.ifr_addr.sa_data[3] = hp->h_addr_list[0][1]; ifr.ifr_addr.sa_data[4] = hp->h_addr_list[0][2]; ifr.ifr_addr.sa_data[5] = hp->h_addr_list[0][3]; ifr.ifr_addr.sa_data[6] = 0; if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { perror("rsattach: SIOCSIFADDR"); return FALSE; } } if (rose_aton(address, addr) == -1) return FALSE; ifr.ifr_hwaddr.sa_family = ARPHRD_ROSE; memcpy(ifr.ifr_hwaddr.sa_data, addr, 5); if (ioctl(fd, SIOCSIFHWADDR, &ifr) != 0) { perror("rsattach: SIOCSIFHWADDR"); return FALSE; } ifr.ifr_mtu = mtu; if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) { perror("rsattach: SIOCSIFMTU"); return FALSE; } if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { perror("rsattach: SIOCGIFFLAGS"); return FALSE; } ifr.ifr_flags &= IFF_NOARP; ifr.ifr_flags |= IFF_UP; ifr.ifr_flags |= IFF_RUNNING; if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { perror("rsattach: SIOCSIFFLAGS"); return FALSE; } close(fd); return TRUE; } int main(int argc, char *argv[]) { int fd; char dev[64]; struct hostent *hp = NULL; while ((fd = getopt(argc, argv, "i:m:v")) != -1) { switch (fd) { case 'i': hp = gethostbyname(optarg); if (hp == NULL) { fprintf(stderr, "rsattach: invalid internet name/address - %s\n", optarg); return 1; } break; case 'v': printf("rsattach: %s\n", FULL_VER); return 0; case ':': case '?': fprintf(stderr, "usage: rsattach [-i inetaddr] [-v] port\n"); return 1; } } if ((argc - optind) != 1) { fprintf(stderr, "usage: rsattach [-i inetaddr] [-v] port\n"); return 1; } if (!readconfig(argv[optind])) return 1; if (!getfreedev(dev)) { fprintf(stderr, "rsattach: cannot find free Rose device\n"); return 1; } if (!startiface(dev, hp)) return 1; printf("Rose port %s bound to device %s\n", argv[optind], dev); return 0; } rose/rsdwnlnk.8000066400000000000000000000031361442776067000140010ustar00rootroot00000000000000.TH RSDWNLNK 8 "29 April 1997" Linux "Linux Programmer's Manual" .SH NAME rsdwnlnk \- User exit from the ROSE network. .SH SYNOPSIS .B rsdwnlnk .SH DESCRIPTION .LP The .B rdwnplnk program allows a user to leave the ROSE network using the standard pseudo-digipeating method. Recent Linux kernels are aware of this form of operation and the .B ax25d program can create such connections. The normal mode of operation of a ROSE switch is to have one ROSE address (ie port) per physical AX.25 port. Therefore ROSE users coming in on a given ROSE port will come out of the associated AX.25 port. .LP To use .B rsdwnlnk you need an entry in .B ax25d.conf to listen for incoming ROSE connections to any callsigns that are not matched by the more specific ROSE entries. In the example below, I will be listening for ROSE connections on my ROSE port rs144, the exiting AX.25 connection will be via the associated AX.25 port, 144, using the callsign KE4GAJ-10. Please note that the callsign should be on the same line as the rest of the command, it may be wrapped onto the next line on your screen. .LP # .br {* VIA rs144} .br NOCALL * * * * * * L .br default * * * * * * - root /usr/sbin/rsdwnlnk rsdwnlnk 144 KE4GAJ-10 .br # .LP There would typically be one .B rsdwnlnk per ROSE port. The associated program for entering a ROSE network is .B rsuplnk. .LP All errors generated by .B rsdwnlnk are written to the system debug log file. .SH FILES .br /etc/ax25/axports .SH "SEE ALSO" .BR rose (4), .BR ax25d.conf (5), .BR ax25d (8), .BR rsuplnk (8), .BR rose_call (8). .SH AUTHOR Jonathan Naylor G4KLX rose/rsdwnlnk.c000066400000000000000000000141151442776067000140530ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AX25_HBIT 0x80 static void alarm_handler(int sig) { } int main(int argc, char **argv) { char buffer[512], *addr; fd_set read_fd; int n = 0, s, yes = 1; struct full_sockaddr_ax25 axbind, axconnect; struct sockaddr_rose rosesock, rosepeer; socklen_t addrlen; openlog("rsdwnlnk", LOG_PID, LOG_DAEMON); /* * Arguments should be "rsdwnlnk ax25port ax25call" */ if (argc != 3) { syslog(LOG_ERR, "invalid number of parameters\n"); closelog(); return 1; } if (ax25_config_load_ports() == 0) { syslog(LOG_ERR, "problem with axports file\n"); closelog(); return 1; } addrlen = sizeof(struct sockaddr_rose); if (getsockname(STDIN_FILENO, (struct sockaddr *)&rosesock, &addrlen) == -1) { syslog(LOG_ERR, "cannot getsockname - %s\n", strerror(errno)); closelog(); return 1; } addrlen = sizeof(struct sockaddr_rose); if (getpeername(STDIN_FILENO, (struct sockaddr *)&rosepeer, &addrlen) == -1) { syslog(LOG_ERR, "cannot getpeername - %s\n", strerror(errno)); closelog(); return 1; } if (setsockopt(STDIN_FILENO, SOL_ROSE, ROSE_QBITINCL, &yes, sizeof(yes)) == -1) { syslog(LOG_ERR, "cannot setsockopt(ROSE_QBITINCL) - %s\n", strerror(errno)); closelog(); return 1; } /* * Parse the passed values for correctness. */ axbind.fsa_ax25.sax25_family = AF_AX25; axbind.fsa_ax25.sax25_ndigis = 1; axbind.fsa_ax25.sax25_call = rosepeer.srose_call; addr = ax25_config_get_addr(argv[1]); if (addr == NULL) { syslog(LOG_ERR, "invalid AX.25 port name - %s\n", argv[1]); closelog(); return 1; } if (ax25_aton_entry(addr, axbind.fsa_digipeater[0].ax25_call) == -1) { syslog(LOG_ERR, "invalid AX.25 port callsign - %s\n", argv[1]); closelog(); return 1; } axconnect.fsa_ax25.sax25_family = AF_AX25; axconnect.fsa_ax25.sax25_call = rosesock.srose_call; /* * The path at the far end has a digi in it. */ if (rosepeer.srose_ndigis == 1) { axconnect.fsa_digipeater[n] = rosepeer.srose_digi; axconnect.fsa_digipeater[n].ax25_call[6] |= AX25_HBIT; n++; } /* * Incoming call has a different DNIC */ if (memcmp(rosepeer.srose_addr.rose_addr, rosesock.srose_addr.rose_addr, 2) != 0) { addr = rose_ntoa(&rosepeer.srose_addr); addr[4] = '\0'; if (ax25_aton_entry(addr, axconnect.fsa_digipeater[n].ax25_call) == -1) { syslog(LOG_ERR, "invalid callsign - %s\n", addr); closelog(); return 1; } axconnect.fsa_digipeater[n].ax25_call[6] |= AX25_HBIT; n++; } /* * Put the remote address sans DNIC into the digi chain. */ addr = rose_ntoa(&rosepeer.srose_addr); if (ax25_aton_entry(addr + 4, axconnect.fsa_digipeater[n].ax25_call) == -1) { syslog(LOG_ERR, "invalid callsign - %s\n", addr + 4); closelog(); return 1; } axconnect.fsa_digipeater[n].ax25_call[6] |= AX25_HBIT; n++; /* * And my local ROSE callsign. */ if (ax25_aton_entry(argv[2], axconnect.fsa_digipeater[n].ax25_call) == -1) { syslog(LOG_ERR, "invalid callsign - %s\n", argv[2]); closelog(); return 1; } axconnect.fsa_digipeater[n].ax25_call[6] |= AX25_HBIT; n++; /* * A digi has been specified for this end. */ if (rosesock.srose_ndigis == 1) { axconnect.fsa_digipeater[n] = rosesock.srose_digi; n++; } axconnect.fsa_ax25.sax25_ndigis = n; addrlen = sizeof(struct full_sockaddr_ax25); /* * Open the socket into the kernel. */ s = socket(AF_AX25, SOCK_SEQPACKET, 0); if (s < 0) { syslog(LOG_ERR, "cannot open AX.25 socket, %s\n", strerror(errno)); closelog(); return 1; } #ifdef HAVE_AX25_IAMDIGI if (setsockopt(s, SOL_AX25, AX25_IAMDIGI, &yes, sizeof(yes)) == -1) { syslog(LOG_ERR, "cannot setsockopt(AX25_IAMDIGI), %s\n", strerror(errno)); close(s); closelog(); return 1; } #endif /* HAVE_AX25_IAMDIGI */ #ifdef HAVE_AX25_PIDINCL if (setsockopt(s, SOL_AX25, AX25_PIDINCL, &yes, sizeof(yes)) == -1) { syslog(LOG_ERR, "cannot setsockopt(AX25_PIDINCL), %s\n", strerror(errno)); close(s); closelog(); return 1; } #endif /* HAVE_AX25_PIDINCL */ /* * Set our AX.25 callsign and AX.25 port callsign accordingly. */ if (bind(s, (struct sockaddr *)&axbind, addrlen) != 0) { syslog(LOG_ERR, "cannot bind AX.25 socket, %s\n", strerror(errno)); close(s); closelog(); return 1; } /* * If no response in 60 seconds, go away. */ alarm(60); signal(SIGALRM, alarm_handler); /* * Lets try and connect to the far end. */ if (connect(s, (struct sockaddr *)&axconnect, addrlen) != 0) { switch (errno) { case ECONNREFUSED: strcpy(buffer, "*** Connection refused\r"); break; case ENETUNREACH: strcpy(buffer, "*** No known route\r"); break; case EINTR: strcpy(buffer, "*** Connection timed out\r"); break; default: sprintf(buffer, "ERROR: cannot connect to AX.25 callsign, %s\r", strerror(errno)); break; } close(s); write(STDOUT_FILENO, buffer, strlen(buffer)); sleep(20); return 0; } /* * We got there. */ alarm(0); strcpy(buffer, "*** Connected\r"); write(STDOUT_FILENO, buffer, strlen(buffer)); /* * Loop until one end of the connection goes away. */ for (;;) { FD_ZERO(&read_fd); FD_SET(STDIN_FILENO, &read_fd); FD_SET(s, &read_fd); select(s + 1, &read_fd, NULL, NULL, NULL); if (FD_ISSET(s, &read_fd)) { n = read(s, buffer + 2, sizeof(buffer) - 2); if (n == -1) break; if (buffer[2] == 0xF0) { buffer[2] = 0; write(STDOUT_FILENO, buffer + 2, n); } else { buffer[0] = 1; /* Set Q Bit on */ buffer[1] = 0x7F; /* Q Bit escape */ write(STDOUT_FILENO, buffer, n + 2); } } if (FD_ISSET(STDIN_FILENO, &read_fd)) { n = read(STDIN_FILENO, buffer, 512); if (n == -1) { close(s); break; } if (buffer[0] == 0) { /* Q Bit not set */ buffer[0] = (char) 0xF0; write(s, buffer, n); } else { /* Lose the leading 0x7F */ write(s, buffer + 2, n - 2); } } } closelog(); return 0; } rose/rsmemsiz.c000066400000000000000000000121651442776067000140650ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "../pathnames.h" enum meminfo_row { meminfo_main = 0, meminfo_swap }; enum meminfo_col { meminfo_total = 0, meminfo_used, meminfo_free, meminfo_shared, meminfo_buffers, meminfo_cached }; unsigned read_total_main(void); static int getuptime(double *uptime_secs) { struct sysinfo si; if (sysinfo(&si) < 0) return -1; *uptime_secs = si.uptime; return 0; } /* The following /proc/meminfo parsing routine assumes the following format: [