summaryrefslogtreecommitdiffstats
path: root/ax25
diff options
context:
space:
mode:
Diffstat (limited to 'ax25')
-rw-r--r--ax25/Makefile.am38
-rw-r--r--ax25/Makefile.in563
-rw-r--r--ax25/ax25.477
-rw-r--r--ax25/ax25.profile2
-rw-r--r--ax25/ax25d.846
-rw-r--r--ax25/ax25d.c1195
-rw-r--r--ax25/ax25d.conf45
-rw-r--r--ax25/ax25d.conf.5269
-rw-r--r--ax25/axctl.867
-rw-r--r--ax25/axctl.c92
-rw-r--r--ax25/axparms.8119
-rw-r--r--ax25/axparms.c442
-rw-r--r--ax25/axports.562
-rw-r--r--ax25/axspawn.852
-rw-r--r--ax25/axspawn.c911
-rw-r--r--ax25/axspawn.conf25
-rw-r--r--ax25/axspawn.conf.575
-rw-r--r--ax25/beacon.851
-rw-r--r--ax25/beacon.c163
-rw-r--r--ax25/bpqparms.837
-rw-r--r--ax25/bpqparms.c146
-rw-r--r--ax25/mheard.174
-rw-r--r--ax25/mheard.c374
-rw-r--r--ax25/mheard.dat0
-rw-r--r--ax25/mheardd.842
-rw-r--r--ax25/mheardd.c436
-rw-r--r--ax25/rxecho.838
-rw-r--r--ax25/rxecho.c376
-rw-r--r--ax25/rxecho.conf12
-rw-r--r--ax25/rxecho.conf.522
30 files changed, 5851 insertions, 0 deletions
diff --git a/ax25/Makefile.am b/ax25/Makefile.am
new file mode 100644
index 0000000..55dc33d
--- /dev/null
+++ b/ax25/Makefile.am
@@ -0,0 +1,38 @@
+
+etcfiles = ax25.profile ax25d.conf axspawn.conf rxecho.conf
+varfiles = mheard.dat
+
+installconf:
+ $(mkinstalldirs) $(DESTDIR)$(etcdir)
+ @list='$(etcfiles)'; for p in $$list; do \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p; \
+ done
+ $(mkinstalldirs) $(DESTDIR)$(vardir)
+ @list='$(varfiles)'; for p in $$list; do \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(vardir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(vardir)/$$p; \
+ done
+
+
+sbin_PROGRAMS = ax25d axctl axparms axspawn beacon bpqparms mheardd rxecho
+
+bin_PROGRAMS = mheard
+
+man_MANS = ax25.4 ax25d.conf.5 axports.5 axspawn.conf.5 rxecho.conf.5 \
+ ax25d.8 axctl.8 axparms.8 axspawn.8 beacon.8 bpqparms.8 \
+ mheard.1 mheardd.8 rxecho.8
+
+
+
+EXTRA_DIST = $(man_MANS) $(etcfiles) $(varfiles)
+
+ax25d_SOURCES = ax25d.c
+axctl_SOURCES = axctl.c
+axparms_SOURCES = axparms.c
+axspawn_SOURCES = axspawn.c
+beacon_SOURCES = beacon.c
+bpqparms_SOURCES = bpqparms.c
+mheard_SOURCES = mheard.c
+mheardd_SOURCES = mheardd.c
+rxecho_SOURCES = rxecho.c
diff --git a/ax25/Makefile.in b/ax25/Makefile.in
new file mode 100644
index 0000000..ef14c64
--- /dev/null
+++ b/ax25/Makefile.in
@@ -0,0 +1,563 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+AWK = @AWK@
+CC = @CC@
+MAKEINFO = @MAKEINFO@
+NCURSES_LIB = @NCURSES_LIB@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+etcfiles = ax25.profile ax25d.conf axspawn.conf rxecho.conf
+varfiles = mheard.dat
+
+sbin_PROGRAMS = ax25d axctl axparms axspawn beacon bpqparms mheardd rxecho
+
+bin_PROGRAMS = mheard
+
+man_MANS = ax25.4 ax25d.conf.5 axports.5 axspawn.conf.5 rxecho.conf.5 ax25d.8 axctl.8 axparms.8 axspawn.8 beacon.8 bpqparms.8 mheard.1 mheardd.8 rxecho.8
+
+
+EXTRA_DIST = $(man_MANS) $(etcfiles) $(varfiles)
+
+ax25d_SOURCES = ax25d.c
+axctl_SOURCES = axctl.c
+axparms_SOURCES = axparms.c
+axspawn_SOURCES = axspawn.c
+beacon_SOURCES = beacon.c
+bpqparms_SOURCES = bpqparms.c
+mheard_SOURCES = mheard.c
+mheardd_SOURCES = mheardd.c
+rxecho_SOURCES = rxecho.c
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I..
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+X_CFLAGS = @X_CFLAGS@
+X_LIBS = @X_LIBS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+mheard_OBJECTS = mheard.o
+mheard_LDADD = $(LDADD)
+mheard_DEPENDENCIES =
+mheard_LDFLAGS =
+ax25d_OBJECTS = ax25d.o
+ax25d_LDADD = $(LDADD)
+ax25d_DEPENDENCIES =
+ax25d_LDFLAGS =
+axctl_OBJECTS = axctl.o
+axctl_LDADD = $(LDADD)
+axctl_DEPENDENCIES =
+axctl_LDFLAGS =
+axparms_OBJECTS = axparms.o
+axparms_LDADD = $(LDADD)
+axparms_DEPENDENCIES =
+axparms_LDFLAGS =
+axspawn_OBJECTS = axspawn.o
+axspawn_LDADD = $(LDADD)
+axspawn_DEPENDENCIES =
+axspawn_LDFLAGS =
+beacon_OBJECTS = beacon.o
+beacon_LDADD = $(LDADD)
+beacon_DEPENDENCIES =
+beacon_LDFLAGS =
+bpqparms_OBJECTS = bpqparms.o
+bpqparms_LDADD = $(LDADD)
+bpqparms_DEPENDENCIES =
+bpqparms_LDFLAGS =
+mheardd_OBJECTS = mheardd.o
+mheardd_LDADD = $(LDADD)
+mheardd_DEPENDENCIES =
+mheardd_LDFLAGS =
+rxecho_OBJECTS = rxecho.o
+rxecho_LDADD = $(LDADD)
+rxecho_DEPENDENCIES =
+rxecho_LDFLAGS =
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+man1dir = $(mandir)/man1
+man4dir = $(mandir)/man4
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+MANS = $(man_MANS)
+
+NROFF = nroff
+DIST_COMMON = Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = tar
+GZIP_ENV = --best
+SOURCES = $(mheard_SOURCES) $(ax25d_SOURCES) $(axctl_SOURCES) $(axparms_SOURCES) $(axspawn_SOURCES) $(beacon_SOURCES) $(bpqparms_SOURCES) $(mheardd_SOURCES) $(rxecho_SOURCES)
+OBJECTS = $(mheard_OBJECTS) $(ax25d_OBJECTS) $(axctl_OBJECTS) $(axparms_OBJECTS) $(axspawn_OBJECTS) $(beacon_OBJECTS) $(bpqparms_OBJECTS) $(mheardd_OBJECTS) $(rxecho_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps ax25/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-binPROGRAMS:
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+distclean-binPROGRAMS:
+
+maintainer-clean-binPROGRAMS:
+
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(bin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+mostlyclean-sbinPROGRAMS:
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+
+distclean-sbinPROGRAMS:
+
+maintainer-clean-sbinPROGRAMS:
+
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sbindir)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+.c.o:
+ $(COMPILE) -c $<
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+mheard: $(mheard_OBJECTS) $(mheard_DEPENDENCIES)
+ @rm -f mheard
+ $(LINK) $(mheard_LDFLAGS) $(mheard_OBJECTS) $(mheard_LDADD) $(LIBS)
+
+ax25d: $(ax25d_OBJECTS) $(ax25d_DEPENDENCIES)
+ @rm -f ax25d
+ $(LINK) $(ax25d_LDFLAGS) $(ax25d_OBJECTS) $(ax25d_LDADD) $(LIBS)
+
+axctl: $(axctl_OBJECTS) $(axctl_DEPENDENCIES)
+ @rm -f axctl
+ $(LINK) $(axctl_LDFLAGS) $(axctl_OBJECTS) $(axctl_LDADD) $(LIBS)
+
+axparms: $(axparms_OBJECTS) $(axparms_DEPENDENCIES)
+ @rm -f axparms
+ $(LINK) $(axparms_LDFLAGS) $(axparms_OBJECTS) $(axparms_LDADD) $(LIBS)
+
+axspawn: $(axspawn_OBJECTS) $(axspawn_DEPENDENCIES)
+ @rm -f axspawn
+ $(LINK) $(axspawn_LDFLAGS) $(axspawn_OBJECTS) $(axspawn_LDADD) $(LIBS)
+
+beacon: $(beacon_OBJECTS) $(beacon_DEPENDENCIES)
+ @rm -f beacon
+ $(LINK) $(beacon_LDFLAGS) $(beacon_OBJECTS) $(beacon_LDADD) $(LIBS)
+
+bpqparms: $(bpqparms_OBJECTS) $(bpqparms_DEPENDENCIES)
+ @rm -f bpqparms
+ $(LINK) $(bpqparms_LDFLAGS) $(bpqparms_OBJECTS) $(bpqparms_LDADD) $(LIBS)
+
+mheardd: $(mheardd_OBJECTS) $(mheardd_DEPENDENCIES)
+ @rm -f mheardd
+ $(LINK) $(mheardd_LDFLAGS) $(mheardd_OBJECTS) $(mheardd_LDADD) $(LIBS)
+
+rxecho: $(rxecho_OBJECTS) $(rxecho_DEPENDENCIES)
+ @rm -f rxecho
+ $(LINK) $(rxecho_LDFLAGS) $(rxecho_OBJECTS) $(rxecho_LDADD) $(LIBS)
+
+install-man1:
+ $(mkinstalldirs) $(DESTDIR)$(man1dir)
+ @list='$(man1_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \
+ done
+
+uninstall-man1:
+ @list='$(man1_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man1dir)/$$inst; \
+ done
+
+install-man4:
+ $(mkinstalldirs) $(DESTDIR)$(man4dir)
+ @list='$(man4_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.4*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man4dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man4dir)/$$inst; \
+ done
+
+uninstall-man4:
+ @list='$(man4_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.4*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man4dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man4dir)/$$inst; \
+ done
+
+install-man5:
+ $(mkinstalldirs) $(DESTDIR)$(man5dir)
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst; \
+ done
+
+uninstall-man5:
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man5dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man5dir)/$$inst; \
+ done
+
+install-man8:
+ $(mkinstalldirs) $(DESTDIR)$(man8dir)
+ @list='$(man8_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \
+ done
+
+uninstall-man8:
+ @list='$(man8_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man8dir)/$$inst; \
+ done
+install-man: $(MANS)
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-man1 install-man4 install-man5 \
+ install-man8
+uninstall-man:
+ @$(NORMAL_UNINSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-man1 uninstall-man4 uninstall-man5 \
+ uninstall-man8
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = ax25
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+ax25d.o: ax25d.c ../config.h ../pathnames.h
+axctl.o: axctl.c ../config.h
+axparms.o: axparms.c ../config.h ../pathnames.h
+axspawn.o: axspawn.c ../pathnames.h
+beacon.o: beacon.c ../config.h
+bpqparms.o: bpqparms.c ../config.h
+mheard.o: mheard.c ../config.h ../pathnames.h
+mheardd.o: mheardd.c ../config.h ../pathnames.h
+rxecho.o: rxecho.c ../config.h ../pathnames.h
+
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-binPROGRAMS install-sbinPROGRAMS
+install-exec: install-exec-am
+
+install-data-am: install-man
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-binPROGRAMS uninstall-sbinPROGRAMS uninstall-man
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) \
+ $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(mandir)/man4 \
+ $(DESTDIR)$(mandir)/man5 $(DESTDIR)$(mandir)/man8
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-sbinPROGRAMS \
+ mostlyclean-compile mostlyclean-tags \
+ mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-binPROGRAMS clean-sbinPROGRAMS clean-compile clean-tags \
+ clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-binPROGRAMS distclean-sbinPROGRAMS \
+ distclean-compile distclean-tags distclean-generic \
+ clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-binPROGRAMS \
+ maintainer-clean-sbinPROGRAMS maintainer-clean-compile \
+ maintainer-clean-tags maintainer-clean-generic \
+ distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
+maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
+mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS clean-sbinPROGRAMS \
+maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \
+install-sbinPROGRAMS mostlyclean-compile distclean-compile \
+clean-compile maintainer-clean-compile install-man1 uninstall-man1 \
+install-man4 uninstall-man4 install-man5 uninstall-man5 install-man8 \
+uninstall-man8 install-man uninstall-man tags mostlyclean-tags \
+distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
+dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+installconf:
+ $(mkinstalldirs) $(DESTDIR)$(etcdir)
+ @list='$(etcfiles)'; for p in $$list; do \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(etcdir)/$$p; \
+ done
+ $(mkinstalldirs) $(DESTDIR)$(vardir)
+ @list='$(varfiles)'; for p in $$list; do \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(vardir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(vardir)/$$p; \
+ done
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/ax25/ax25.4 b/ax25/ax25.4
new file mode 100644
index 0000000..6456971
--- /dev/null
+++ b/ax25/ax25.4
@@ -0,0 +1,77 @@
+.TH AX25 4 "15 October 1996" 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 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
+SOCK_DGRAM gives access to AX.25 UI frames. For access to special frames (of
+any form) SOCK_RAW can be used. There is no SOCK_PACKET support under AX.25.
+Instead an AF_INET socket of type SOCK_PACKET should be used.
+.LP
+AX.25 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. 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 arbitary 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 SOL_AX25. AX25_T1 is the T1
+timer in 1/10ths of a second, AX25_T2 is the T2 timer in 1/10ths of a
+second, AX25_T3 is the T3 timer. The window is settable with AX25_WINDOW.
+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 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 AX25_EXTSEQ. It is possible to have the complete AX.25 header returned
+to the application by setting 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 axparms (5),
+.BR axassociate (8),
+.BR axparms (8),
+.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 <alan@cymru.net>
+.br
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
+.fi
diff --git a/ax25/ax25.profile b/ax25/ax25.profile
new file mode 100644
index 0000000..dbb2c5c
--- /dev/null
+++ b/ax25/ax25.profile
@@ -0,0 +1,2 @@
+#! /bin/bash
+echo "/char ibmpc ibmpc" >.conversrc
diff --git a/ax25/ax25d.8 b/ax25/ax25d.8
new file mode 100644
index 0000000..96ceec4
--- /dev/null
+++ b/ax25/ax25d.8
@@ -0,0 +1,46 @@
+.TH AX25D 8 "27 August 1996" 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 netrom (4),
+.BR rose (4),
+.BR ax25d.conf (5).
+.SH AUTHOR
+Darryl Miles G7LED <dlm@frink.demon.co.uk>
+.br
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
diff --git a/ax25/ax25d.c b/ax25/ax25d.c
new file mode 100644
index 0000000..8d45713
--- /dev/null
+++ b/ax25/ax25d.c
@@ -0,0 +1,1195 @@
+/*
+ * This is my version of axl.c, written for the LBBS code to make it
+ * compatable 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., 675 Mass Ave, Cambridge, MA 02139, 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.
+ *
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netax25/ax25.h>
+#include <netrom/netrom.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <netax25/nrconfig.h>
+#include <netax25/rsconfig.h>
+#include <netax25/daemon.h>
+
+#include <config.h>
+
+#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... */
+};
+
+static struct axlist *AXL = NULL;
+static char *ConfigFile = CONF_AX25D_FILE;
+static char User[10]; /* Room for 'GB9ZZZ-15\0' */
+static char Node[11]; /* Room for 'GB9ZZZ-15\0' */
+static char *Port;
+static sig_atomic_t Update = TRUE; /* Cause update on bootup */
+static int Logging = FALSE;
+
+static void SignalHUP(int);
+static void SignalTERM(int);
+static void WorkoutArgs(int, char *, int *, char **);
+static void SetupOptions(int, struct axlist *);
+static int ReadConfig(void);
+static unsigned long ParseFlags(const char *, int);
+static struct axlist *ClearList(struct axlist *);
+static char *stripssid(const char *);
+
+
+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;
+ int maxfd = -1;
+ fd_set fdread;
+ int addrlen;
+ int cnt;
+
+ 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", VERSION);
+ 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);
+
+ for (;;) {
+ if (Update) {
+ if (ReadConfig() < 0) {
+ if (AXL == NULL) {
+ if (Logging)
+ syslog(LOG_ERR, "config file reload error, exiting");
+ return 1;
+ } else {
+ if (Logging)
+ syslog(LOG_INFO, "config file reload error, continuing with original");
+ }
+ } else {
+ if (Logging)
+ syslog(LOG_INFO, "new config file loaded successfuly");
+ }
+
+ Update = FALSE;
+ }
+
+ FD_ZERO(&fdread);
+
+ for (paxl = AXL; paxl != NULL && paxl->fd >= 0; paxl = paxl->next) {
+ FD_SET(paxl->fd, &fdread);
+
+ if (paxl->fd > maxfd)
+ maxfd = paxl->fd;
+ }
+
+ if (select(maxfd + 1, &fdread, NULL, NULL, NULL) <= 0)
+ continue;
+
+ for (paxl = AXL; paxl != NULL; paxl = paxl->next) {
+ if (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;
+ 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:
+ 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() */
+ setgroups(0, grps); /* Strip any supplementary gid's */
+ setgid(raxl->gid);
+ setuid(raxl->uid);
+ execve(raxl->exec, argv, NULL);
+ return 1;
+
+ default:
+ close(new);
+ break;
+ }
+ }
+ }
+ }
+
+ /* NOT REACHED */
+ return 0;
+}
+
+static void SignalHUP(int code)
+{
+ Update = TRUE; /* Schedule an update */
+}
+
+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 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;
+ int addrlen;
+ int af_type = 0; /* Keep GCC happy */
+ int line = 0;
+ int hunt = TRUE, error = FALSE;
+ int iamdigi = FALSE, yes;
+ int parameters = 0;
+
+ memset(&axl_defaults, 0, sizeof(axl_defaults));
+
+ if ((fp = fopen(ConfigFile, "r")) == NULL)
+ return -1;
+
+ while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+ line++;
+
+ if ((s = strchr(buffer, '\n')) != NULL)
+ *s = '\0';
+ if ((s = strchr(buffer, '\r')) != NULL)
+ *s = '\0';
+
+ if (buffer[0] == '#')
+ continue;
+
+ switch (buffer[0]) {
+ case '[': /* AX25 port call */
+ af_type = AF_AX25;
+ hunt = TRUE;
+ error = FALSE;
+ iamdigi = FALSE;
+ break;
+
+ case '<': /* NETROM iface call */
+ af_type = AF_NETROM;
+ hunt = TRUE;
+ error = FALSE;
+ iamdigi = FALSE;
+ break;
+
+ case '{': /* ROSE iface call */
+ af_type = AF_ROSE;
+ hunt = TRUE;
+ error = FALSE;
+ iamdigi = 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:
+ if ((s = strchr(buffer, ']')) == NULL)
+ goto BadLine;
+ *s = '\0';
+ if ((s = strtok(buffer + 1, " \t")) == NULL)
+ goto BadLine;
+ port = s;
+ call = NULL;
+ if ((s = strtok(NULL, " \t")) != NULL) {
+ if (strcasecmp(s, "VIA") == 0 || strcasecmp(s, "V") == 0) {
+ if ((s = strtok(NULL, " \t")) == NULL)
+ goto BadLine;
+ }
+
+ call = port;
+ port = s;
+
+ if ((s = strchr(call, '*')) != NULL) {
+ iamdigi = TRUE;
+ *s = '\0';
+ }
+ }
+ if (strcmp(port, "*") == 0 && call == NULL) {
+ fprintf(stderr, "ax25d: invalid port name\n");
+ continue;
+ }
+ if (strcmp(port, "*") != 0) {
+ if ((addr = ax25_config_get_addr(port)) == 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:
+ if ((s = strchr(buffer, '>')) == NULL)
+ goto BadLine;
+ *s = '\0';
+ port = buffer + 1;
+ if ((addr = nr_config_get_addr(port)) == 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:
+ if ((s = strchr(buffer, '}')) == NULL)
+ goto BadLine;
+ *s = '\0';
+ if ((s = strtok(buffer + 1, " \t")) == NULL)
+ goto BadLine;
+ call = s;
+ if ((s = strtok(NULL, " \t")) == NULL)
+ goto BadLine;
+ if (strcasecmp(s, "VIA") == 0 || strcasecmp(s, "V") == 0) {
+ if ((s = strtok(NULL, " \t")) == NULL)
+ goto BadLine;
+ }
+ port = s;
+ if ((addr = rs_config_get_addr(port)) == 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);
+ }
+
+ if ((axl_port = calloc(1, sizeof(*axl_port))) == NULL) {
+ fprintf(stderr, "ax25d: out of memory\n");
+ goto Error;
+ }
+
+ axl_port->port = strdup(port);
+ axl_port->af_type = af_type;
+
+ if ((axl_port->fd = socket(axl_port->af_type, SOCK_SEQPACKET, 0)) < 0) {
+ fprintf(stderr, "ax25d: socket: %s\n", strerror(errno));
+ free(axl_port->port);
+ free(axl_port);
+ error = TRUE;
+ continue;
+ }
+ /* xlz - have to nuke this as this option is gone
+ * what should be here?
+ if (iamdigi) {
+ yes = 1;
+ setsockopt(axl_port->fd, SOL_AX25, AX25_IAMDIGI, &yes, sizeof(yes));
+ }
+ */
+
+ 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;
+ 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;
+ 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 */
+ if ((axl_ent = calloc(1, sizeof(*axl_ent))) == NULL) {
+ fprintf(stderr, "ax25d: out of memory\n");
+ goto Error;
+ }
+
+ axl_ent->af_type = axl_port->af_type; /* Inherit this */
+
+ if ((call = strtok(buffer, " \t")) == NULL) {
+ free(axl_ent);
+ continue;
+ }
+
+ strupr(call);
+
+ if (axl_ent->af_type == AF_NETROM) {
+ if ((s = strchr(call, '@')) != 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 */
+ }
+ }
+
+ parameters = FALSE;
+
+ if (strcasecmp("parameters", call) == 0)
+ parameters = TRUE;
+ else if (strcasecmp("default", call) != 0)
+ axl_ent->call = strdup(call);
+
+ /* Window */
+ if ((s = strtok(NULL, " \t")) == 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 */
+ if ((s = strtok(NULL, " \t")) == 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 */
+ if ((s = strtok(NULL, " \t")) == 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 */
+ if ((s = strtok(NULL, " \t")) == 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 */
+ if ((s = strtok(NULL, " \t")) == 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 */
+ if ((s = strtok(NULL, " \t")) == 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 */
+ if ((s = strtok(NULL, " \t")) == NULL)
+ goto BadArgsFree;
+
+ axl_ent->flags = ParseFlags(s, line);
+
+ if (!(axl_ent->flags & FLAG_LOCKOUT)) {
+ /* Get username */
+ if ((s = strtok(NULL, " \t")) == NULL)
+ goto BadArgsFree;
+
+ if ((pwd = getpwnam(s)) == 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 */
+ if ((s = strtok(NULL, " \t")) == NULL)
+ goto BadArgsFree;
+
+ axl_ent->exec = strdup(s);
+
+ /* Get command line */
+ if ((s = strtok(NULL, "")) == 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 */
+
+ return 0;
+
+Error:
+ axl_build = ClearList(axl_build);
+ return -1;
+}
+
+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 char *stripssid(const char *call)
+{
+ static char newcall[10];
+ char *s;
+
+ strcpy(newcall, call);
+
+ if ((s = strchr(newcall, '-')) != NULL)
+ *s = '\0';
+
+ return newcall;
+}
diff --git a/ax25/ax25d.conf b/ax25/ax25d.conf
new file mode 100644
index 0000000..c55f7ba
--- /dev/null
+++ b/ax25/ax25d.conf
@@ -0,0 +1,45 @@
+# /etc/ax25/ax25d.conf
+#
+# ax25d Configuration File.
+#
+# AX.25 Ports begin with a '['.
+#
+[OH2BNS VIA 1]
+NOCALL * * * * * * L
+default * * * * * * - root /usr/local/sbin/ttylinkd ttylinkd
+#
+[OH2BNS-2 VIA 1]
+NOCALL * * * * * * L
+default * * * * * * - root /usr/sbin/node node
+#
+#
+[OH2BNS VIA 2]
+NOCALL * * * * * * L
+default * * * * * * - root /usr/local/sbin/ttylinkd ttylinkd
+#
+[OH2BNS-2 VIA 2]
+NOCALL * * * * * * L
+default * * * * * * - root /usr/sbin/node node
+#
+[OH2BNS-3 VIA 2]
+NOCALL * * * * * * L
+default * * * * * * - root /usr/local/sbin/axwrapper axwrapper /usr/bin/finger finger
+#
+#[OH2BNS-9]
+#NOCALL * * * * * * L
+#default * * * * * * - root /usr/sbin/node node
+#
+# NET/ROM Ports begin with a '<'.
+#
+#<netrom>
+#NOCALL * * * * * * L
+#default * * * * * * - root /usr/local/sbin/ttylinkd ttylinkd
+#
+<netrom>
+NOCALL * * * * * * L
+default * * * * * * - root /usr/sbin/node node
+#
+#<netrom>
+#NOCALL * * * * * * L
+#default * * * * * * - root /usr/sbin/node node
+#
diff --git a/ax25/ax25d.conf.5 b/ax25/ax25d.conf.5
new file mode 100644
index 0000000..446c0e3
--- /dev/null
+++ b/ax25/ax25d.conf.5
@@ -0,0 +1,269 @@
+.TH AX25D.CONF 5 "17 January 1997" 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. <NET/ROM Port Name>
+.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 digi-peaters. 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 dependant 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.
+.SH FILES
+.LP
+/etc/ax25/ax25d.conf
+.SH "SEE ALSO"
+.BR ax25 (4),
+.BR netrom (4),
+.BR rose (4),
+.BR axports (5),
+.BR nrports (5),
+.BR rsports (5),
+.BR ax25d (8).
diff --git a/ax25/axctl.8 b/ax25/axctl.8
new file mode 100644
index 0000000..3189aa8
--- /dev/null
+++ b/ax25/axctl.8
@@ -0,0 +1,67 @@
+.TH AXCTL 8 "2 August 1996" 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
+invokation 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 <jreuter@poboxes.com>
+.fi
diff --git a/ax25/axctl.c b/ax25/axctl.c
new file mode 100644
index 0000000..84422fd
--- /dev/null
+++ b/ax25/axctl.c
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+
+#include <config.h>
+
+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", VERSION);
+ 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;
+ }
+
+ if ((addr = ax25_config_get_addr(argv[1])) == 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;
+
+ if ((s = socket(AF_AX25, SOCK_SEQPACKET, 0)) < 0) {
+ perror("axctl: socket");
+ return 1;
+ }
+
+ if (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)
+ ax25_ctl.cmd = AX25_T1;
+ else if (strcmp(argv[4], "-t2") == 0)
+ ax25_ctl.cmd = AX25_T2;
+ else if (strcmp(argv[4], "-t3") == 0)
+ ax25_ctl.cmd = AX25_T3;
+ else if (strcmp(argv[4], "-idle") == 0)
+ ax25_ctl.cmd = AX25_IDLE;
+ else if (strcmp(argv[4], "-n2") == 0)
+ ax25_ctl.cmd = AX25_N2;
+ else if (strcmp(argv[4], "-window") == 0)
+ ax25_ctl.cmd = AX25_WINDOW;
+ else if (strcmp(argv[4], "-paclen") == 0)
+ ax25_ctl.cmd = AX25_PACLEN;
+ }
+
+ if (ioctl(s, SIOCAX25CTLCON, &ax25_ctl) != 0) {
+ perror("axctl: SIOAX25CTLCON");
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git a/ax25/axparms.8 b/ax25/axparms.8
new file mode 100644
index 0000000..627adbf
--- /dev/null
+++ b/ax25/axparms.8
@@ -0,0 +1,119 @@
+.TH AXPARMS 8 "25 July 1997" 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. It includes the
+functionality of
+.B axassociate
+and
+.B axsetcall
+which this command superceedes. The different modes of the command are
+chosen by the first argument. Sunsequent 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 <callsign> <username>
+.br
+.B axparms -assoc <callsign> delete
+.br
+.B axparms -assoc policy [default|deny]
+.br
+.B axparms -assoc show
+.fi
+.LP
+This option mainpulates 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 <portfrom> <portto>
+.br
+.B axparms -forward <portfrom> 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 <port> <callsign> [<digis>] [-ipmode V|D]
+.br
+.B axparms -route del <port> <callsign>
+.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 <callsign> 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 <serial-device> <callsign>
+.LP
+This changes the callsign associated with the given serial device in KISS mode.
+The change is permanent until the link is downed or another \(lqaxparms
+-setcall\(rq is made.
+.LP
+The interface must already have been attached with
+.B kissattach
+to use this option.
+.SH "-version Argument"
+.LP
+This option displays the version of the AX.25 utilities that
+.B axparms
+belongs to.
+.SH FILES
+.LP
+/proc/net/ax25_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 <alan@cymru.net>
+.br
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
+.br
+Joerg Reuter DL1BKE <jreuter@poboxes.com>
+.fi
diff --git a/ax25/axparms.c b/ax25/axparms.c
new file mode 100644
index 0000000..5683d46
--- /dev/null
+++ b/ax25/axparms.c
@@ -0,0 +1,442 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <config.h>
+
+#include "../pathnames.h"
+
+void usage(void)
+{
+ fprintf(stderr, "usage: axparms -assoc|-forward|-route|-setcall|-version ...\n");
+}
+
+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");
+}
+
+void usageforward(void)
+{
+ fprintf(stderr, "usage: axparms -forward <portfrom> <portto>\n");
+ fprintf(stderr, "usage: axparms -forward <portfrom> delete\n");
+}
+
+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");
+}
+
+void usagesetcall(void)
+{
+ fprintf(stderr, "usage: axparms -setcall interface callsign\n");
+}
+
+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", 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) {
+ if ((fp=fopen(PROC_AX25_ROUTE_FILE,"r")) == NULL) {
+ fprintf(stderr, "axparms: route: cannot open %s\n",
+PROC_AX25_ROUTE_FILE);
+ return 1;
+ }
+ while (fgets(routebuf,80,fp))
+ printf(routebuf);
+ puts("");
+ }
+
+ return 0;
+}
+
+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;
+}
+
+int associate(int s, int argc, char *argv[])
+{
+ char buffer[80], *u, *c;
+ struct sockaddr_ax25 sax25;
+ struct passwd *pw;
+ int opt;
+ FILE *fp;
+
+
+ if (strcmp(argv[2], "show") == 0) {
+ if (argc < 3) {
+ usageassoc();
+ exit(1);
+ }
+
+ if ((fp = fopen(PROC_AX25_CALLS_FILE, "r")) == 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");
+ if ((pw = getpwuid(atoi(u))) != 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;
+ }
+
+ if ((pw = getpwnam(argv[3])) == 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;
+}
+
+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;
+ }
+
+ if ((addr = ax25_config_get_addr(argv[2])) == 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;
+ }
+
+ if ((addr = ax25_config_get_addr(argv[3])) == 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", 2) == 0) {
+ printf("axparms: %s\n", VERSION);
+ return 0;
+ }
+
+ if (strncmp(argv[1], "-a", 2) == 0) {
+
+ if (argc < 3) {
+ usageassoc();
+ return 1;
+ }
+
+ if ((s = socket(AF_AX25, SOCK_SEQPACKET, 0)) < 0) {
+ perror("axparms: socket");
+ return 1;
+ }
+
+ n = associate(s, argc, argv);
+
+ close(s);
+
+ return n;
+ }
+
+ if (strncmp(argv[1], "-f", 2) == 0) {
+ if (argc == 2) {
+ usageforward();
+ return 1;
+ }
+
+ if ((s = socket(AF_AX25, SOCK_SEQPACKET, 0)) < 0) {
+ perror("axparms: socket");
+ return 1;
+ }
+
+ n = forward(s, argc, argv);
+
+ close(s);
+
+ return n;
+ }
+
+ if (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;
+ }
+
+ if ((addr = ax25_config_get_addr(argv[3])) == NULL) {
+ fprintf(stderr, "axparms: invalid port name - %s\n", argv[3]);
+ return 1;
+ }
+
+ if (ax25_aton_entry(addr, callsign.ax25_call) == -1)
+ return 1;
+
+ if ((s = socket(AF_AX25, SOCK_SEQPACKET, 0)) < 0) {
+ perror("axparms: socket");
+ return 1;
+ }
+
+ n = routes(s, argc, argv, &callsign);
+
+ close(s);
+
+ return n;
+ }
+
+ if (strncmp(argv[1], "-s", 2) == 0) {
+ if (argc != 4) {
+ usagesetcall();
+ return 1;
+ }
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("axparms: socket");
+ return 1;
+ }
+
+ n = setifcall(s, argv[2], argv[3]);
+
+ close(s);
+
+ return n;
+ }
+
+ usage();
+
+ return 1;
+}
diff --git a/ax25/axports.5 b/ax25/axports.5
new file mode 100644
index 0000000..fc61a47
--- /dev/null
+++ b/ax25/axports.5
@@ -0,0 +1,62 @@
+.TH AXPORTS 5 "15 October 1996" Linux "Linux Programmer's Manual"
+.SH NAME
+axports \- AX.25 port configuration file.
+.SH DESCRIPTION
+.LP
+.B Axports
+is an ASCII file that contains information about each of the physical AX.25
+ports that are to be used. When dealing with an AX.25 utility such as
+.B call,
+it takes an optional argument that is the port name. This port name is a
+reference to the line within
+.B axports,
+which has that name as its first argument. The information on each line
+contains enough information to bind the command to a particular physical AX.25
+interface, this binding is done by matching the callsign on the line in
+.B axports
+with the callsign of the port set by
+.B kissattach.
+.LP
+The lines within
+.B axports
+must either be a comment line, which starts with a # in the first column, or
+a port description in the following format, each field being delimited by
+white space:
+.sp
+.RS
+name callsign speed paclen window description
+.RE
+.sp
+The field descriptions are:
+.sp
+.RS
+.TP 14
+.B name
+is the unique identifier of the port. This is the name given as the port
+argument of many of the AX.25 support programs.
+.TP 14
+.B callsign
+the callsign of the physical interface to bind to.
+.TP 14
+.B speed
+this is the speed of interface, a value of zero means that no speed will be
+set by kissattach(8).
+.TP 14
+.B paclen
+is the default maximum packet size for this interface.
+.TP 14
+.B window
+the default window size for this interface.
+.TP 14
+.B description
+a free format description of this interface, this field extends to the end
+of the line. This field may contain spaces.
+.RE
+.SH FILES
+.LP
+/etc/ax25/axports
+.SH "SEE ALSO"
+.BR call (1),
+.BR ax25 (4),
+.BR axparms (8),
+.BR kissattach (8).
diff --git a/ax25/axspawn.8 b/ax25/axspawn.8
new file mode 100644
index 0000000..ccb6934
--- /dev/null
+++ b/ax25/axspawn.8
@@ -0,0 +1,52 @@
+.TH AXSPAWN 8 "25 August 1996" Linux "Linux System Managers Manual"
+.SH NAME
+axspawn \- Allow automatic login to a Linux system.
+.SH SYNOPSIS
+.B axspawn [--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.
+.SH OPTIONS
+.TP 5
+.B -w, --wait
+Disables the prompting for a password if the password entry in /etc/passwd
+is either a \(lq+\(rq or blank.
+.SH FILES
+.nf
+/etc/passwd
+.br
+/etc/ax25/ax25.profile
+.br
+/etc/ax25/axspawn.conf
+.fi
+.SH "SEE ALSO"
+.BR axspawn.conf (5),
+.BR ax25d (8).
+.SH AUTHOR
+Joerg Reuter DL1BKE <jreuter@poboxes.com>
diff --git a/ax25/axspawn.c b/ax25/axspawn.c
new file mode 100644
index 0000000..88381cf
--- /dev/null
+++ b/ax25/axspawn.c
@@ -0,0 +1,911 @@
+/*
+ *
+ * $Id: axspawn.c,v 1.6 1996/08/24 22:33:05 jreuter Exp jreuter $
+ *
+ * axspawn.c - run a program from ax25d.
+ *
+ * Copyright (c) 1996 Jörg 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
+ * #
+ * # 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/ax25
+ * #
+ * # 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).
+ *
+ * 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.
+ *
+ * It uses the forkpty from libbsd.a (found after analyzing logind)
+ * which has no prototype in any of my .h files.
+ *
+ */
+
+/* removed -h <protocol> from login command as it was causing hostname lookups
+ with new login/libc - Terry, vk2ktj. */
+
+
+#define QUEUE_DELAY 400 /* 400 msec */
+#define USERPROFILE ".profile"
+#define PASSWDFILE "/etc/passwd"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <utmp.h>
+#include <paths.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include <sys/socket.h>
+
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+#include <netax25/axlib.h>
+
+#include "../pathnames.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 EXITDELAY 10
+
+char policy_add_user = 1;
+char policy_guest = 1;
+char policy_associate = 0;
+
+gid_t user_gid = 400;
+char *user_shell = "/bin/bash";
+char *start_home = "/home/funk";
+char *guest = "guest";
+int start_uid = 400;
+int end_uid = 65535;
+int paclen = ROSE_PACLEN; /* Its the shortest ie safest */
+
+struct write_queue {
+ struct write_queue *next;
+ char *data;
+ int len;
+};
+
+struct write_queue *wqueue_head = NULL;
+struct write_queue *wqueue_tail = NULL;
+long wqueue_length = 0;
+
+/* This one is in /usr/lib/libbsd.a, but not in bsd.h and fellows... weird. */
+/* (found in logind.c) */
+
+pid_t forkpty(int *, char *, void *, struct winsize *);
+
+int _write_ax25(const char *s, int len)
+{
+ int k, m;
+ char *p;
+
+ p = (char *) malloc(len+1);
+
+ if (p == NULL)
+ return 0;
+
+ m = 0;
+ for (k = 0; k < len; k++)
+ {
+ if ( (s[k] == '\r') && ((k+1) < len) && (s[k+1] == '\n') )
+ continue;
+ else if (s[k] == '\n')
+ p[m++] = '\r';
+ else
+ p[m++] = s[k];
+ }
+
+ if (m)
+ write(1, p, m);
+
+ free(p);
+ return len;
+}
+
+int read_ax25(char *s, int size)
+{
+ int len = read(0, s, size);
+ int k;
+
+ for (k = 0; k < len; k++)
+ if (s[k] == '\r') s[k] = '\n';
+
+ 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?
+ */
+
+void kick_wqueue(int dummy)
+{
+ char *s, *p;
+ struct write_queue *buf;
+
+
+ if (wqueue_length == 0)
+ return;
+
+ s = (char *) malloc(wqueue_length);
+
+ p = s;
+
+ while (wqueue_head)
+ {
+ buf = wqueue_head;
+ wqueue_head = buf->next;
+
+ memcpy(p, buf->data, buf->len);
+ p += buf->len;
+ free(buf->data);
+ free(buf);
+ }
+
+ _write_ax25(s, wqueue_length);
+ free(s);
+ wqueue_tail=NULL;
+ wqueue_length=0;
+}
+
+int write_ax25(const char *s, int len)
+{
+ struct itimerval itv, oitv;
+ struct write_queue * buf;
+
+ signal(SIGALRM, SIG_IGN);
+
+ buf = (struct write_queue *) malloc(sizeof(struct write_queue));
+ if (buf == NULL)
+ return 0;
+
+ buf->data = (char *) malloc(len);
+ if (buf->data == NULL)
+ 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 >= paclen)
+ {
+ 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;
+}
+
+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);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+void cleanup(char *tty)
+{
+ struct utmp ut, *ut_line;
+ FILE *fp;
+
+ setutent();
+ ut.ut_type = LOGIN_PROCESS;
+ strncpy(ut.ut_id, tty + 3, sizeof(ut.ut_line));
+ 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';
+ time(&ut_line->ut_time);
+ pututline(ut_line);
+ if ((fp = fopen(_PATH_WTMP, "r+")) != 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();
+}
+
+
+/*
+ * add a new user to /etc/passwd and do some init
+ */
+
+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;
+ unsigned char *p, *q;
+ struct stat fst;
+ int fd_a, fd_b, fd_l;
+
+ /*
+ * 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)
+ return;
+
+ /*
+ * 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)
+ {
+ 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, S_IRUSR|S_IWUSR|S_IXUSR);
+ }
+ }
+ else
+ return;
+ }
+
+ if (chdir(p) < 0)
+ return;
+ p = q;
+ }
+
+ /*
+ * add the user now
+ */
+
+ fp = fopen(PASSWDFILE, "a+");
+ if (fp == NULL)
+ return;
+
+ pw.pw_name = newuser;
+ pw.pw_passwd = "+";
+ 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)
+ return;
+
+ flock(fd_l, LOCK_UN);
+ fclose(fp);
+
+ /*
+ * copy ax25.profile
+ */
+
+ fd_a = open(CONF_AXSPAWN_PROF_FILE, O_RDONLY);
+
+ if (fd_a > 0)
+ {
+ fd_b = open(USERPROFILE, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IXUSR);
+
+ if (fd_b < 0)
+ return;
+
+ while ( (cnt = read(fd_a, &buf, sizeof(buf))) > 0 )
+ write(fd_b, &buf, cnt);
+ close(fd_b);
+ close(fd_a);
+ chown(USERPROFILE, uid, user_gid);
+ }
+}
+
+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='\0';
+
+ if (buf[0] != '\0')
+ {
+ sscanf(buf, "%s %s", cmd, param);
+
+ if (!strncmp(cmd, "create", 5))
+ {
+ policy_add_user = (param[0] == 'y');
+ } else
+ if (!strncmp(cmd, "guest", 5))
+ {
+ if (!strcmp(param, "no"))
+ {
+ policy_guest = 0;
+ } else {
+ policy_guest = 1;
+ guest = (char *) 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 = (char *) malloc(strlen(param)+1);
+ strcpy(start_home, param);
+ } 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 = (char *) malloc(strlen(param)+1);
+ strcpy(user_shell, param);
+ } else
+ {
+ printf("error in config: ->%s %s<-\n", cmd, param);
+ }
+ }
+ }
+
+ fclose(fp);
+}
+
+char ptyslave[20];
+int child_pid;
+
+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], real_user[20];
+ char buf[2048];
+ int k, cnt, digits, letters, invalid, ssid, ssidcnt, addrlen;
+ int fdmaster;
+ pid_t pid = -1;
+ char *p;
+ fd_set fds_read, fds_err;
+ struct passwd *pw;
+ int chargc;
+ char *chargv[20];
+ int envc;
+ char *envp[20];
+ char wait_for_tcp;
+ 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;
+
+ digits = letters = invalid = ssid = ssidcnt = 0;
+
+ if (argc > 1 && (!strcmp(argv[1],"-w") || !strcmp(argv[1],"--wait")))
+ wait_for_tcp = 1;
+ else
+ wait_for_tcp = 0;
+
+ read_config();
+
+ 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(MSG_NOCALL, sizeof(MSG_NOCALL));
+ syslog(LOG_NOTICE, "%s is not an Amateur Radio callsign\n", call);
+ sleep(EXITDELAY);
+ return 1;
+ }
+
+ strcpy(user, call);
+ strlwr(user);
+ p = strchr(user, '-');
+ if (p) *p = '\0';
+ strcpy(real_user, user);
+
+ if (wait_for_tcp)
+ read_ax25(buf, sizeof(buf)); /* incoming TCP/IP connection? */
+
+ pw = getpwnam(user);
+
+ if (pw == NULL)
+ {
+ if (policy_add_user)
+ {
+ new_user(user);
+ pw = getpwnam(user);
+ }
+
+ if (pw == NULL && policy_guest)
+ {
+ strcpy(real_user,guest);
+ pw = getpwnam(guest);
+
+ if (! (pw && pw->pw_uid && pw->pw_gid) )
+ {
+ write_ax25(MSG_NOTINDBF, sizeof(MSG_NOTINDBF));
+ syslog(LOG_NOTICE, "%s (callsign: %s) not found in /etc/passwd\n", user, call);
+ sleep(EXITDELAY);
+ return 1;
+ }
+ }
+ }
+
+ endpwent();
+
+ if (pw->pw_uid == 0 || pw->pw_gid == 0)
+ {
+ write_ax25(MSG_NOCALL, sizeof(MSG_NOCALL));
+ syslog(LOG_NOTICE, "root login of %s (callsign: %s) denied\n", user, call);
+ sleep(EXITDELAY);
+ return 1;
+ }
+
+ /*
+ * 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;
+
+ memset((char *) &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;
+ 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));
+ time(&ut_line.ut_time);
+ ut_line.ut_addr = 0;
+ pututline(&ut_line);
+ endutent();
+
+ chargc = 0;
+ chargv[chargc++] = "/bin/login";
+ chargv[chargc++] = "-p";
+ if (!strcmp(pw->pw_passwd, "+"))
+ chargv[chargc++] = "-f";
+ chargv[chargc++] = real_user;
+ chargv[chargc] = NULL;
+
+ envc = 0;
+ envp[envc] = (char *) malloc(30);
+ sprintf(envp[envc++], "AXCALL=%s", call);
+ envp[envc] = (char *) malloc(30);
+ sprintf(envp[envc++], "CALL=%s", user);
+ envp[envc] = (char *) malloc(30);
+ sprintf(envp[envc++], "PROTOCOL=%s", protocol);
+ envp[envc] = NULL;
+
+ execve(chargv[0], chargv, envp);
+ }
+ 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);
+ 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))
+ {
+ kill(pid, SIGHUP);
+ cleanup(ptyslave+5);
+ return 1;
+ }
+
+ if (FD_ISSET(fdmaster, &fds_err))
+ {
+ 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, sizeof(buf));
+ if (cnt < 0)
+ {
+ cleanup(ptyslave+5);
+ return 1; /* Child died */
+ }
+ write_ax25(buf, cnt);
+ }
+ } else
+ if (k < 0 && errno != EINTR)
+ {
+ kill(pid, SIGHUP); /* just in case... */
+ cleanup(ptyslave+5);
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ syslog(LOG_ERR, "cannot fork %m, closing connection to %s\n", call);
+ write_ax25(MSG_CANNOTFORK, sizeof(MSG_CANNOTFORK));
+ sleep(EXITDELAY);
+ return 1;
+ }
+
+ sleep(EXITDELAY);
+
+ return 0;
+}
diff --git a/ax25/axspawn.conf b/ax25/axspawn.conf
new file mode 100644
index 0000000..1f0bac4
--- /dev/null
+++ b/ax25/axspawn.conf
@@ -0,0 +1,25 @@
+# /etc/ax25/axspawn.conf
+#
+# allow automatic creation of user accounts
+create yes
+#
+# 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/ax25
+#
+# user shell
+shell /bin/bash
+#
+# bind user id to callsign for outgoing connects.
+associate no
diff --git a/ax25/axspawn.conf.5 b/ax25/axspawn.conf.5
new file mode 100644
index 0000000..416ccdf
--- /dev/null
+++ b/ax25/axspawn.conf.5
@@ -0,0 +1,75 @@
+.TH AXSPAWN.CONF 5 "2 August 1996" 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
+#
+.br
+# guest user if above is 'no' or everything else
+.br
+# fails. Disable with "no"
+.br
+guest ax25
+.br
+#
+.br
+# group id or name for autoaccount
+.br
+group ax25
+.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/ax25
+.br
+#
+.br
+# user's shell
+.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).
diff --git a/ax25/beacon.8 b/ax25/beacon.8
new file mode 100644
index 0000000..9b38db7
--- /dev/null
+++ b/ax25/beacon.8
@@ -0,0 +1,51 @@
+.TH BEACON 8 "10 February 1997" Linux "Linux System Managers Manual"
+.SH NAME
+beacon \- transmit periodic messages on an AX.25 port.
+.SH SYNOPSIS
+.B beacon [-c <src_call>] [-d <dest_call>] [-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.
+.TP 16
+.BI \-l
+Enables the logging of errors to the system log, the default is off.
+.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 <alan@cymru.net>
+.br
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
+.br
+David Brooke G6GZH <db@fusk.demon.co.uk>
+.fi
diff --git a/ax25/beacon.c b/ax25/beacon.c
new file mode 100644
index 0000000..c9b989e
--- /dev/null
+++ b/ax25/beacon.c
@@ -0,0 +1,163 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <netax25/daemon.h>
+
+#include <config.h>
+
+static int logging = FALSE;
+static int mail = FALSE;
+static int single = FALSE;
+
+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, interval = 30;
+ char addr[20], *port, *message, *portcall;
+ char *srccall = NULL, *destcall = NULL;
+
+ while ((n = getopt(argc, argv, "c:d:lmst:v")) != -1) {
+ switch (n) {
+ case 'c':
+ srccall = optarg;
+ break;
+ case 'd':
+ destcall = optarg;
+ break;
+ case 'l':
+ logging = TRUE;
+ break;
+ case 'm':
+ mail = TRUE;
+ /* falls through */
+ case 's':
+ single = TRUE;
+ break;
+ case 't':
+ interval = 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", VERSION);
+ return 0;
+ case '?':
+ case ':':
+ fprintf(stderr, "usage: beacon [-c <src_call>] [-d <dest_call>] [-l] [-m] [-s] [-t interval] [-v] <port> <message>\n");
+ return 1;
+ }
+ }
+
+ signal(SIGTERM, terminate);
+
+ if (optind == argc || optind == argc - 1) {
+ fprintf(stderr, "usage: beacon [-c <src_call>] [-d <dest_call>] [-l] [-m] [-s] [-t interval] [-v] <port> <message>\n");
+ 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;
+ }
+
+ if ((portcall = ax25_config_get_addr(port)) == NULL) {
+ fprintf(stderr, "beacon: invalid AX.25 port setting - %s\n", port);
+ return 1;
+ }
+
+ if (mail)
+ strcpy(addr, "MAIL");
+ else if (destcall != NULL)
+ strcpy(addr, destcall);
+ else
+ strcpy(addr, "IDENT");
+
+ if ((dlen = ax25_aton(addr, &dest)) == -1) {
+ fprintf(stderr, "beacon: unable to convert callsign '%s'\n", addr);
+ return 1;
+ }
+
+ if (srccall != NULL && strcmp(srccall, portcall) != 0)
+ sprintf(addr, "%s %s", srccall, portcall);
+ else
+ strcpy(addr, portcall);
+
+ if ((len = ax25_aton(addr, &src)) == -1) {
+ fprintf(stderr, "beacon: unable to convert callsign '%s'\n", addr);
+ return 1;
+ }
+
+ if (!single) {
+ 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");
+ }
+
+ for (;;) {
+ if ((s = socket(AF_AX25, SOCK_DGRAM, 0)) == -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)
+ sleep(interval * 60);
+ else
+ break;
+ }
+
+ return 0;
+}
diff --git a/ax25/bpqparms.8 b/ax25/bpqparms.8
new file mode 100644
index 0000000..7a8b450
--- /dev/null
+++ b/ax25/bpqparms.8
@@ -0,0 +1,37 @@
+.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 <jreuter@poboxes.com>
diff --git a/ax25/bpqparms.c b/ax25/bpqparms.c
new file mode 100644
index 0000000..1a88fd6
--- /dev/null
+++ b/ax25/bpqparms.c
@@ -0,0 +1,146 @@
+/*
+ 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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <linux/timer.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <linux/bpqether.h> /* xlz - dammit, we need this again */
+
+#include <config.h>
+
+#define RCS_ID "$Id:$"
+
+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);
+}
+
+char *Version = "$Revision:$";
+
+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",
+ &eth[0], &eth[1], &eth[2], &eth[3], &eth[4], &eth[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;
+
+ strcpy(dev, argv[1]);
+
+ 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 %s\n", Version);
+ 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", VERSION);
+ 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;
+}
diff --git a/ax25/mheard.1 b/ax25/mheard.1
new file mode 100644
index 0000000..b300aaf
--- /dev/null
+++ b/ax25/mheard.1
@@ -0,0 +1,74 @@
+.TH MHEARD 1 "19 August 1996" Linux "Linux Programmer's Manual"
+.SH NAME
+mheard \- display AX.25 calls recently heard.
+.SH SYNOPSIS
+.B mheard [-d cmns] [-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 cmns"
+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.
+.RE
+.TP 13
+.BI \-n
+Supress 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 <g4klx@g4klx.demon.co.uk>
diff --git a/ax25/mheard.c b/ax25/mheard.c
new file mode 100644
index 0000000..2ab582c
--- /dev/null
+++ b/ax25/mheard.c
@@ -0,0 +1,374 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+#include <netax25/axlib.h>
+#include <netax25/mheard.h>
+
+#include <config.h>
+
+#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 = NULL;
+
+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;
+ }
+}
+
+static void PrintPortEntry(struct PortRecord *pr, int data)
+{
+ char lh[30], fh[30], *call, *s;
+ char buffer[80];
+ int i;
+
+ switch (data) {
+ case 0:
+ strcpy(lh, ctime(&pr->entry.last_heard));
+ lh[19] = 0;
+ call = ax25_ntoa(&pr->entry.from_call);
+ if ((s = strstr(call, "-0")) != NULL)
+ *s = '\0';
+ printf("%-10s %-5s %5d %s\n",
+ call, pr->entry.portname, pr->entry.count, lh);
+ break;
+ case 1:
+ buffer[0] = '\0';
+ call = ax25_ntoa(&pr->entry.from_call);
+ if ((s = strstr(call, "-0")) != NULL)
+ *s = '\0';
+ strcat(buffer, call);
+ call = ax25_ntoa(&pr->entry.to_call);
+ if ((s = strstr(call, "-0")) != 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]);
+ if ((s = strstr(call, "-0")) != NULL)
+ *s = '\0';
+ strcat(buffer, call);
+ }
+ if (pr->entry.ndigis >= 4)
+ strcat(buffer, ",...");
+ printf("%-70s %-5s\n",
+ buffer, pr->entry.portname);
+ break;
+ case 2:
+ strcpy(lh, ctime(&pr->entry.last_heard));
+ lh[19] = 0;
+ strcpy(fh, ctime(&pr->entry.first_heard));
+ fh[19] = 0;
+ call = ax25_ntoa(&pr->entry.from_call);
+ if ((s = strstr(call, "-0")) != NULL)
+ *s = '\0';
+ printf("%-10s %-5s %5d %5d %5d %s %s\n",
+ call, pr->entry.portname, pr->entry.iframes, pr->entry.sframes, pr->entry.uframes, fh, lh);
+ break;
+ case 3:
+ call = ax25_ntoa(&pr->entry.from_call);
+ if ((s = strstr(call, "-0")) != NULL)
+ *s = '\0';
+ printf("%-10s %-5s %5d %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;
+ }
+}
+
+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;
+
+ if ((fp = fopen(DATA_MHEARD_FILE, "r")) == 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;
+ 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;
+ 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", VERSION);
+ return 0;
+ case '?':
+ case ':':
+ fprintf(stderr, "Usage: %s [-d cmns] [-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;
+}
diff --git a/ax25/mheard.dat b/ax25/mheard.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ax25/mheard.dat
diff --git a/ax25/mheardd.8 b/ax25/mheardd.8
new file mode 100644
index 0000000..63cda36
--- /dev/null
+++ b/ax25/mheardd.8
@@ -0,0 +1,42 @@
+.TH MHEARDD 8 "27 August 1996" Linux "Linux Programmer's Manual"
+.SH NAME
+mheardd \- collect information about packet activity
+.SH SYNOPSIS
+.B mheardd [-f] [-l] [-n number] [-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 40. The
+minimum value allowed is 10 and the maximum is 100.
+.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 <g4klx@g4klx.demon.co.uk>
diff --git a/ax25/mheardd.c b/ax25/mheardd.c
new file mode 100644
index 0000000..2d98fad
--- /dev/null
+++ b/ax25/mheardd.c
@@ -0,0 +1,436 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <netax25/daemon.h>
+#include <netax25/mheard.h>
+
+#include <config.h>
+
+#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;
+static int mheard_list_size = 40;
+static int logging = FALSE;
+
+static int ftype(unsigned 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;
+ unsigned char buffer[1500], *data;
+ int size, s;
+ char *port = NULL;
+ struct sockaddr sa;
+ int asize;
+ long position;
+ int ctlen, type, end, extseq, flush = FALSE;
+ FILE *fp;
+
+ while ((s = getopt(argc, argv, "fln: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 > 100) {
+ fprintf(stderr, "mheardd: list size must be between 10 and 100\n");
+ return 1;
+ }
+ break;
+ case 'v':
+ printf("mheardd: %s\n", VERSION);
+ return 0;
+ case ':':
+ fprintf(stderr, "mheardd: option -n needs an argument\n");
+ return 1;
+ case '?':
+ fprintf(stderr, "Usage: mheardd [-f] [-l] [-n number] [-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;
+ }
+
+ if ((mheard_list = calloc(mheard_list_size, sizeof(struct mheard_list_struct))) == 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 {
+ if ((fp = fopen(DATA_MHEARD_FILE, "w")) != NULL)
+ fclose(fp);
+ }
+
+ if ((s = socket(AF_INET, SOCK_PACKET, htons(ETH_P_AX25))) == -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);
+
+ if ((size = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &asize)) == -1) {
+ if (logging) {
+ syslog(LOG_ERR, "recv: %m");
+ closelog();
+ }
+ return 1;
+ }
+
+ if ((port = ax25_config_get_name(sa.sa_data)) == NULL) {
+ if (logging)
+ syslog(LOG_WARNING, "unknown port '%s'\n", sa.sa_data);
+ 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) {
+ switch (*data) {
+ 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);
+
+ if ((fp = fopen(DATA_MHEARD_FILE, "r+")) == 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(unsigned 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;
+}
diff --git a/ax25/rxecho.8 b/ax25/rxecho.8
new file mode 100644
index 0000000..7a9ed80
--- /dev/null
+++ b/ax25/rxecho.8
@@ -0,0 +1,38 @@
+.TH RXECHO 8 "15 October 1996" 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 <tpmannin@cc.hut.fi>
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, <tomi.manninen@hut.fi>.
+ *
+ * *** Modified 9/9/96 by Heikki Hannikainen, OH7LZB, <hessu@pspt.fi>:
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <netax25/daemon.h>
+
+#include <config.h>
+
+#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();
+ }
+ }
+ }
+ }
+}
diff --git a/ax25/rxecho.conf b/ax25/rxecho.conf
new file mode 100644
index 0000000..0bb498c
--- /dev/null
+++ b/ax25/rxecho.conf
@@ -0,0 +1,12 @@
+# /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 *
diff --git a/ax25/rxecho.conf.5 b/ax25/rxecho.conf.5
new file mode 100644
index 0000000..105c45b
--- /dev/null
+++ b/ax25/rxecho.conf.5
@@ -0,0 +1,22 @@
+.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).