From 17287576555a5c46fa23549e2e5f073660dccb70 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 21 Apr 1999 09:51:03 +0200 Subject: Import ax25-tools 0.0.1 from tarball --- ax25/rxecho.c | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 ax25/rxecho.c (limited to 'ax25/rxecho.c') diff --git a/ax25/rxecho.c b/ax25/rxecho.c new file mode 100644 index 0000000..31334aa --- /dev/null +++ b/ax25/rxecho.c @@ -0,0 +1,376 @@ +/* + * 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). + * + * *** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../pathnames.h" + +#define MAXCALLS 8 + +struct config { + char from[14]; /* sockaddr.sa_data is 14 bytes */ + char to[14]; + 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; + + if ((fp = fopen(CONF_RXECHO_FILE, "r")) == 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; + + if ((p = calloc(1, sizeof(struct config))) == NULL) { + perror("rxecho: malloc"); + return NULL; + } + + if ((dev = ax25_config_get_dev(cp)) == NULL) { + fprintf(stderr, "rxecho: invalid port name - %s\n", cp); + return NULL; + } + + strcpy(p->from, dev); + + if ((cp = strtok(NULL, " \t\r\n")) == NULL) { + fprintf(stderr, "rxecho: config file error.\n"); + return NULL; + } + + if ((dev = ax25_config_get_dev(cp)) == NULL) { + fprintf(stderr, "rxecho: invalid port name - %s\n", cp); + return NULL; + } + + strcpy(p->to, dev); + + 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 sa; + int s, size, 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", VERSION); + 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; + } + + if ((list = readconfig()) == NULL) + return 1; + + if ((s = socket(AF_INET, SOCK_PACKET, htons(ETH_P_AX25))) == -1) { + perror("rxecho: socket:"); + return 1; + } + + 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 = sizeof(sa); + + if ((size = recvfrom(s, buf, 1500, 0, &sa, &alen)) == -1) { + if (logging) { + syslog(LOG_ERR, "recvfrom: %m"); + closelog(); + } + return 1; + } + + for (p = list; p != NULL; p = p->next) + if ((strcmp(p->from, sa.sa_data) == 0) && (check_calls(p, buf, size) == 0)) { + strcpy(sa.sa_data, p->to); + if (sendto(s, buf, size, 0, &sa, alen) == -1) { + if (logging) { + syslog(LOG_ERR, "sendto: %m"); + closelog(); + } + } + } + } +} -- cgit v1.2.3