From 17287576555a5c46fa23549e2e5f073660dccb70 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 21 Apr 1999 09:51:03 +0200 Subject: Import ax25-tools 0.0.1 from tarball --- ax25/Makefile.am | 38 ++ ax25/Makefile.in | 563 ++++++++++++++++++++++++ ax25/ax25.4 | 77 ++++ ax25/ax25.profile | 2 + ax25/ax25d.8 | 46 ++ ax25/ax25d.c | 1195 +++++++++++++++++++++++++++++++++++++++++++++++++++ ax25/ax25d.conf | 45 ++ ax25/ax25d.conf.5 | 269 ++++++++++++ ax25/axctl.8 | 67 +++ ax25/axctl.c | 92 ++++ ax25/axparms.8 | 119 +++++ ax25/axparms.c | 442 +++++++++++++++++++ ax25/axports.5 | 62 +++ ax25/axspawn.8 | 52 +++ ax25/axspawn.c | 911 +++++++++++++++++++++++++++++++++++++++ ax25/axspawn.conf | 25 ++ ax25/axspawn.conf.5 | 75 ++++ ax25/beacon.8 | 51 +++ ax25/beacon.c | 163 +++++++ ax25/bpqparms.8 | 37 ++ ax25/bpqparms.c | 146 +++++++ ax25/mheard.1 | 74 ++++ ax25/mheard.c | 374 ++++++++++++++++ ax25/mheard.dat | 0 ax25/mheardd.8 | 42 ++ ax25/mheardd.c | 436 +++++++++++++++++++ ax25/rxecho.8 | 38 ++ ax25/rxecho.c | 376 ++++++++++++++++ ax25/rxecho.conf | 12 + ax25/rxecho.conf.5 | 22 + 30 files changed, 5851 insertions(+) create mode 100644 ax25/Makefile.am create mode 100644 ax25/Makefile.in create mode 100644 ax25/ax25.4 create mode 100644 ax25/ax25.profile create mode 100644 ax25/ax25d.8 create mode 100644 ax25/ax25d.c create mode 100644 ax25/ax25d.conf create mode 100644 ax25/ax25d.conf.5 create mode 100644 ax25/axctl.8 create mode 100644 ax25/axctl.c create mode 100644 ax25/axparms.8 create mode 100644 ax25/axparms.c create mode 100644 ax25/axports.5 create mode 100644 ax25/axspawn.8 create mode 100644 ax25/axspawn.c create mode 100644 ax25/axspawn.conf create mode 100644 ax25/axspawn.conf.5 create mode 100644 ax25/beacon.8 create mode 100644 ax25/beacon.c create mode 100644 ax25/bpqparms.8 create mode 100644 ax25/bpqparms.c create mode 100644 ax25/mheard.1 create mode 100644 ax25/mheard.c create mode 100644 ax25/mheard.dat create mode 100644 ax25/mheardd.8 create mode 100644 ax25/mheardd.c create mode 100644 ax25/rxecho.8 create mode 100644 ax25/rxecho.c create mode 100644 ax25/rxecho.conf create mode 100644 ax25/rxecho.conf.5 (limited to 'ax25') 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 +.br +Jonathan Naylor G4KLX +.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 +.br +Jonathan Naylor G4KLX 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "../pathnames.h" + +/* Maximum number of command line arguments for the application we run */ +#define MAX_ARGS 32 + +#define FLAG_VALIDCALL 0x01 /* NOTUSED */ +#define FLAG_NOLOGGING 0x02 /* Don't log this connection */ +#define FLAG_CHKNRN 0x04 /* Check NetRom Neighbour - NOTUSED */ +#define FLAG_NODIGIS 0x08 /* Disallow digipeated uplinks */ +#define FLAG_LOCKOUT 0x10 /* Disallow connection */ + + +struct axlist { /* Have used same struct for quickness */ + struct axlist *next; /* Port list */ + + char *port; /* Port call, only set across the port list */ + int fd; /* The listening socket fd */ + + int af_type; /* AF_AX25, AF_NETROM or AF_ROSE port */ + + struct axlist *ents; /* Exec line entries */ + char *call; /* Call in listing entries */ + char *node; /* Node call in listing entries */ + uid_t uid; /* UID to run program as */ + gid_t gid; /* GID to run program as */ + char *exec; /* Real exec */ + char *shell; /* Command line. With possible escapes. */ + + unsigned int window; /* Set window to... */ + unsigned long t1; /* Set T1 to... (Retrans timer) */ + unsigned long t2; /* Set T2 to... (Ack delay) */ + unsigned long t3; /* Set T3 to... (Idle Poll timer) */ + unsigned long idle; /* Set T4 to... (Link Drop timer) */ + unsigned long n2; /* Set N2 to... (Retries) */ + unsigned long flags; /* FLAG_ values ORed... */ +}; + +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 '<'. +# +# +#NOCALL * * * * * * L +#default * * * * * * - root /usr/local/sbin/ttylinkd ttylinkd +# + +NOCALL * * * * * * L +default * * * * * * - root /usr/sbin/node node +# +# +#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. +.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 +.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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +int main(int argc, char **argv) +{ + struct ax25_ctl_struct ax25_ctl; + char *addr; + int s; + + if (argc == 2 && strncmp(argv[1], "-v", 2) == 0) { + printf("axctl: %s\n", 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 +.br +.B axparms -assoc 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 +.br +.B axparms -forward delete +.fi +.LP +Any packets to be transmitted on port portfrom will be transmitted on port +portto. This will stay in force until the second form of the command is +issued which will remove the association. +.SH "-route Argument" +.LP +This option allows the internal AX.25 routing table to be manipulated. This +table is available for reading in /proc/net/ax25_route, and will be built up +dynamically by stations heard. However it is possible to add, delete and list +entries via this option. +.LP +The formats of this option are: +.LP +.nf +.B axparms -route add [] [-ipmode V|D] +.br +.B axparms -route del +.fi +.B axparms -route list +.fi +.LP +Routes added via this command will not be removed from the internal routing +table when they are \(lqold\(rq as normal entries are. The +.B -ipmode +option sets mode vc or mode datagram for this destination. +.LP +If the argument is set to \(lqdefault\(rq then this will set the +default route for all outgoing AX.25 connections which will be used when there +is no specific route to the required destination. +.SH "-setcall Argument" +.LP +The format of this option is: +.LP +.B axparms -setcall +.LP +This changes the callsign associated 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 +.br +Jonathan Naylor G4KLX +.br +Joerg Reuter DL1BKE +.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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#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 \n"); + fprintf(stderr, "usage: axparms -forward 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "../pathnames.h" + +#define 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 ] [-d ] [-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 +.br +Jonathan Naylor G4KLX +.br +David Brooke G6GZH +.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 +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +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 ] [-d ] [-l] [-m] [-s] [-t interval] [-v] \n"); + return 1; + } + } + + signal(SIGTERM, terminate); + + if (optind == argc || optind == argc - 1) { + fprintf(stderr, "usage: beacon [-c ] [-d ] [-l] [-m] [-s] [-t interval] [-v] \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 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include /* xlz - dammit, we need this again */ + +#include + +#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", + ð[0], ð[1], ð[2], ð[3], ð[4], ð[5]); + + if (n < 6) + return 1; + + for (n = 0; n < ETH_ALEN; n++) + k[n] = eth[n]; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int fd; + int cmd, flag; + struct ifreq ifr; + char dev[40]; + struct bpq_ethaddr addr; + + 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 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../pathnames.h" + +struct PortRecord { + struct mheard_struct entry; + struct PortRecord *Next; +}; + +static char *types[] = { + "SABM", + "SABME", + "DISC", + "UA", + "DM", + "RR", + "RNR", + "REJ", + "FRMR", + "I", + "UI", + "????"}; + +static struct PortRecord *PortList = 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "../pathnames.h" + +#define KISS_MASK 0x0F +#define KISS_DATA 0x00 + +#define PID_SEGMENT 0x08 +#define PID_ARP 0xCD +#define PID_NETROM 0xCF +#define PID_IP 0xCC +#define PID_ROSE 0x01 +#define PID_TEXNET 0xC3 +#define PID_FLEXNET 0xCE +#define PID_TEXT 0xF0 +#define PID_PSATFT 0xBB +#define PID_PSATPB 0xBD + +#define I 0x00 +#define S 0x01 +#define RR 0x01 +#define RNR 0x05 +#define REJ 0x09 +#define U 0x03 +#define SABM 0x2F +#define SABME 0x6F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define UI 0x03 + +#define PF 0x10 +#define EPF 0x01 + +#define MMASK 7 + +#define HDLCAEB 0x01 +#define SSID 0x1E +#define SSSID_SPARE 0x40 +#define ESSID_SPARE 0x20 + +#define ALEN 6 +#define AXLEN 7 + +struct mheard_list_struct { + int in_use; + struct mheard_struct entry; + long position; +}; + +static struct mheard_list_struct *mheard_list; +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 diff --git a/ax25/rxecho.c b/ax25/rxecho.c new file mode 100644 index 0000000..31334aa --- /dev/null +++ b/ax25/rxecho.c @@ -0,0 +1,376 @@ +/* + * rxecho.c - Copies AX.25 packets from an interface to another interface. + * Reads CONFIGFILE (see below) and uses that information to + * decide what packets should be copied and where. + * + * CONFIGFILE format is: + * + * # this is a comment + * 144 kiss0 oh2bns-1,oh2bns-2 + * kiss0 144 * + * + * This means that packets received on port 144 are copied to port + * kiss0 if they are destined to oh2bns-1 or oh2bns-2. Packets + * from port kiss0 are all copied to port 144. + * + * There may be empty lines and an arbirary amount of white + * space around the tokens but the callsign field must not + * have any spaces in it. There can be up to MAXCALLS call- + * signs in the callsign field (see below). + * + * Copyright (C) 1996 by Tomi Manninen, OH2BNS, . + * + * *** Modified 9/9/96 by Heikki Hannikainen, OH7LZB, : + * + * One port can actually be echoed to multiple ports (with a + * different recipient callsign, of course). The old behaviour was + * to give up on the first matching port, even if the recipient + * callsign didn't match (and the frame wasn't echoed anywhere). + * + * *** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../pathnames.h" + +#define MAXCALLS 8 + +struct config { + char from[14]; /* sockaddr.sa_data is 14 bytes */ + char to[14]; + ax25_address calls[MAXCALLS];/* list of calls to echo */ + int ncalls; /* number of calls to echo */ + + struct config *next; +}; + +static int logging = FALSE; + +static void terminate(int sig) +{ + if (logging) { + syslog(LOG_INFO, "terminating on SIGTERM\n"); + closelog(); + } + + exit(0); +} + +/* + * Read string "call1,call2,call3,..." into p. + */ +static int read_calls(struct config *p, char *s) +{ + char *cp, *cp1; + + if (p == NULL || s == NULL) + return -1; + + p->ncalls = 0; + + if (strcmp(s, "*") == 0) + return 0; + + cp = s; + + while ((cp1 = strchr(cp, ',')) != NULL && p->ncalls < MAXCALLS) { + *cp1 = 0; + + if (ax25_aton_entry(cp, p->calls[p->ncalls].ax25_call) == -1) + return -1; + + p->ncalls++; + cp = ++cp1; + } + + if (p->ncalls < MAXCALLS) { + if (ax25_aton_entry(cp, p->calls[p->ncalls].ax25_call) == -1) + return -1; + + p->ncalls++; + } + + return p->ncalls; +} + +static struct config *readconfig(void) +{ + FILE *fp; + char line[80], *cp, *dev; + struct config *p, *list = NULL; + + if ((fp = fopen(CONF_RXECHO_FILE, "r")) == NULL) { + fprintf(stderr, "rxecho: cannot open config file\n"); + return NULL; + } + + while (fgets(line, 80, fp) != NULL) { + cp = strtok(line, " \t\r\n"); + + if (cp == NULL || cp[0] == '#') + continue; + + if ((p = calloc(1, sizeof(struct config))) == NULL) { + perror("rxecho: malloc"); + return NULL; + } + + if ((dev = ax25_config_get_dev(cp)) == NULL) { + fprintf(stderr, "rxecho: invalid port name - %s\n", cp); + return NULL; + } + + strcpy(p->from, dev); + + if ((cp = strtok(NULL, " \t\r\n")) == NULL) { + fprintf(stderr, "rxecho: config file error.\n"); + return NULL; + } + + if ((dev = ax25_config_get_dev(cp)) == NULL) { + fprintf(stderr, "rxecho: invalid port name - %s\n", cp); + return NULL; + } + + strcpy(p->to, dev); + + if (read_calls(p, strtok(NULL, " \t\r\n")) == -1) { + fprintf(stderr, "rxecho: config file error.\n"); + return NULL; + } + + p->next = list; + list = p; + } + + fclose(fp); + + if (list == NULL) + fprintf(stderr, "rxecho: Empty config file!\n"); + + return list; +} + +/* + * Slightly modified from linux/include/net/ax25.h and + * linux/net/ax25/ax25_subr.c: + */ + +#if 0 +#define C_COMMAND 1 +#define C_RESPONSE 2 +#define LAPB_C 0x80 +#endif +#define LAPB_E 0x01 +#define AX25_ADDR_LEN 7 +#define AX25_REPEATED 0x80 + +typedef struct { + ax25_address calls[AX25_MAX_DIGIS]; + unsigned char repeated[AX25_MAX_DIGIS]; + char ndigi; + char lastrepeat; +} ax25_digi; + +/* + * Given an AX.25 address pull of to, from, digi list, and the start of data. + */ +static unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi) +{ + int d = 0; + + if (len < 14) return NULL; + +#if 0 + if (flags != NULL) { + *flags = 0; + + if (buf[6] & LAPB_C) { + *flags = C_COMMAND; + } + + if (buf[13] & LAPB_C) { + *flags = C_RESPONSE; + } + } + + if (dama != NULL) + *dama = ~buf[13] & DAMA_FLAG; +#endif + + /* Copy to, from */ + if (dest != NULL) + memcpy(dest, buf + 0, AX25_ADDR_LEN); + + if (src != NULL) + memcpy(src, buf + 7, AX25_ADDR_LEN); + + buf += 2 * AX25_ADDR_LEN; + len -= 2 * AX25_ADDR_LEN; + + digi->lastrepeat = -1; + digi->ndigi = 0; + + while (!(buf[-1] & LAPB_E)) { + if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ + if (len < 7) return NULL; /* Short packet */ + + if (digi != NULL) { + memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); + digi->ndigi = d + 1; + + if (buf[6] & AX25_REPEATED) { + digi->repeated[d] = 1; + digi->lastrepeat = d; + } else { + digi->repeated[d] = 0; + } + } + + buf += AX25_ADDR_LEN; + len -= AX25_ADDR_LEN; + d++; + } + + return buf; +} + +/* + * Check if frame should be echoed. Return 0 if it should and -1 if not. + */ +static int check_calls(struct config *cfg, unsigned char *buf, int len) +{ + ax25_address dest; + ax25_digi digi; + ax25_address *axp; + int i; + + if ((buf[0] & 0x0F) != 0) + return -1; /* don't echo non-data */ + + if (cfg->ncalls == 0) + return 0; /* copy everything */ + + if (ax25_parse_addr(++buf, --len, NULL, &dest, &digi) == NULL) + return -1; /* invalid ax.25 header */ + + /* + * If there are no digis or all digis are already repeated + * use destination address. Else use first non-repeated digi. + */ + if (digi.ndigi == 0 || digi.ndigi == digi.lastrepeat + 1) + axp = &dest; + else + axp = &digi.calls[digi.lastrepeat + 1]; + + for (i = 0; i < cfg->ncalls; i++) + if (ax25_cmp(&cfg->calls[i], axp) == 0) + return 0; + + return -1; +} + +int main(int argc, char **argv) +{ + struct sockaddr sa; + int s, size, alen; + unsigned char buf[1500]; + struct config *p, *list; + + while ((s = getopt(argc, argv, "lv")) != -1) { + switch (s) { + case 'l': + logging = TRUE; + break; + case 'v': + printf("rxecho: %s\n", VERSION); + return 0; + default: + fprintf(stderr, "usage: rxecho [-l] [-v]\n"); + return 1; + } + } + + signal(SIGTERM, terminate); + + if (ax25_config_load_ports() == 0) { + fprintf(stderr, "rxecho: no AX.25 port data configured\n"); + return 1; + } + + if ((list = readconfig()) == NULL) + return 1; + + if ((s = socket(AF_INET, SOCK_PACKET, htons(ETH_P_AX25))) == -1) { + perror("rxecho: socket:"); + return 1; + } + + if (!daemon_start(FALSE)) { + fprintf(stderr, "rxecho: cannot become a daemon\n"); + close(s); + return 1; + } + + if (logging) { + openlog("rxecho", LOG_PID, LOG_DAEMON); + syslog(LOG_INFO, "starting"); + } + + for (;;) { + alen = sizeof(sa); + + if ((size = recvfrom(s, buf, 1500, 0, &sa, &alen)) == -1) { + if (logging) { + syslog(LOG_ERR, "recvfrom: %m"); + closelog(); + } + return 1; + } + + for (p = list; p != NULL; p = p->next) + if ((strcmp(p->from, sa.sa_data) == 0) && (check_calls(p, buf, size) == 0)) { + strcpy(sa.sa_data, p->to); + if (sendto(s, buf, size, 0, &sa, alen) == -1) { + if (logging) { + syslog(LOG_ERR, "sendto: %m"); + closelog(); + } + } + } + } +} 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). -- cgit v1.2.3