summaryrefslogtreecommitdiffstats
path: root/netrom
diff options
context:
space:
mode:
Diffstat (limited to 'netrom')
-rw-r--r--netrom/Makefile.am26
-rw-r--r--netrom/Makefile.in452
-rw-r--r--netrom/netrom.451
-rw-r--r--netrom/netromd.897
-rw-r--r--netrom/netromd.c254
-rw-r--r--netrom/netromd.h41
-rw-r--r--netrom/netromr.c164
-rw-r--r--netrom/netromt.c268
-rw-r--r--netrom/nodesave.831
-rw-r--r--netrom/nodesave.c101
-rw-r--r--netrom/nrattach.839
-rw-r--r--netrom/nrattach.c240
-rw-r--r--netrom/nrbroadcast8
-rw-r--r--netrom/nrbroadcast.549
-rw-r--r--netrom/nrparms.876
-rw-r--r--netrom/nrparms.c239
-rw-r--r--netrom/nrports7
-rw-r--r--netrom/nrports.556
-rw-r--r--netrom/nrsdrv.844
-rw-r--r--netrom/nrsdrv.c457
20 files changed, 2700 insertions, 0 deletions
diff --git a/netrom/Makefile.am b/netrom/Makefile.am
new file mode 100644
index 0000000..03f0c0d
--- /dev/null
+++ b/netrom/Makefile.am
@@ -0,0 +1,26 @@
+
+etcfiles = nrbroadcast nrports
+etcdir = $(sysconfdir)/ax25
+
+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
+
+
+sbin_PROGRAMS = netromd nodesave nrattach nrparms nrsdrv
+
+man_MANS = netrom.4 nrports.5 nrbroadcast.5 netromd.8 nodesave.8 \
+ nrattach.8 nrparms.8 nrsdrv.8
+
+EXTRA_DIST = $(man_MANS) $(etcfiles)
+
+netromd_SOURCES = \
+ netromd.c \
+ netromd.h \
+ netromr.c \
+ netromt.c
+
+
diff --git a/netrom/Makefile.in b/netrom/Makefile.in
new file mode 100644
index 0000000..80c3c9c
--- /dev/null
+++ b/netrom/Makefile.in
@@ -0,0 +1,452 @@
+# 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 = nrbroadcast nrports
+etcdir = $(sysconfdir)/ax25
+
+sbin_PROGRAMS = netromd nodesave nrattach nrparms nrsdrv
+
+man_MANS = netrom.4 nrports.5 nrbroadcast.5 netromd.8 nodesave.8 nrattach.8 nrparms.8 nrsdrv.8
+
+
+EXTRA_DIST = $(man_MANS) $(etcfiles)
+
+netromd_SOURCES = netromd.c netromd.h netromr.c netromt.c
+
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES =
+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@
+netromd_OBJECTS = netromd.o netromr.o netromt.o
+netromd_LDADD = $(LDADD)
+netromd_DEPENDENCIES =
+netromd_LDFLAGS =
+nodesave_SOURCES = nodesave.c
+nodesave_OBJECTS = nodesave.o
+nodesave_LDADD = $(LDADD)
+nodesave_DEPENDENCIES =
+nodesave_LDFLAGS =
+nrattach_SOURCES = nrattach.c
+nrattach_OBJECTS = nrattach.o
+nrattach_LDADD = $(LDADD)
+nrattach_DEPENDENCIES =
+nrattach_LDFLAGS =
+nrparms_SOURCES = nrparms.c
+nrparms_OBJECTS = nrparms.o
+nrparms_LDADD = $(LDADD)
+nrparms_DEPENDENCIES =
+nrparms_LDFLAGS =
+nrsdrv_SOURCES = nrsdrv.c
+nrsdrv_OBJECTS = nrsdrv.o
+nrsdrv_LDADD = $(LDADD)
+nrsdrv_DEPENDENCIES =
+nrsdrv_LDFLAGS =
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+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 = $(netromd_SOURCES) nodesave.c nrattach.c nrparms.c nrsdrv.c
+OBJECTS = $(netromd_OBJECTS) nodesave.o nrattach.o nrparms.o nrsdrv.o
+
+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 netrom/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+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:
+
+netromd: $(netromd_OBJECTS) $(netromd_DEPENDENCIES)
+ @rm -f netromd
+ $(LINK) $(netromd_LDFLAGS) $(netromd_OBJECTS) $(netromd_LDADD) $(LIBS)
+
+nodesave: $(nodesave_OBJECTS) $(nodesave_DEPENDENCIES)
+ @rm -f nodesave
+ $(LINK) $(nodesave_LDFLAGS) $(nodesave_OBJECTS) $(nodesave_LDADD) $(LIBS)
+
+nrattach: $(nrattach_OBJECTS) $(nrattach_DEPENDENCIES)
+ @rm -f nrattach
+ $(LINK) $(nrattach_LDFLAGS) $(nrattach_OBJECTS) $(nrattach_LDADD) $(LIBS)
+
+nrparms: $(nrparms_OBJECTS) $(nrparms_DEPENDENCIES)
+ @rm -f nrparms
+ $(LINK) $(nrparms_LDFLAGS) $(nrparms_OBJECTS) $(nrparms_LDADD) $(LIBS)
+
+nrsdrv: $(nrsdrv_OBJECTS) $(nrsdrv_DEPENDENCIES)
+ @rm -f nrsdrv
+ $(LINK) $(nrsdrv_LDFLAGS) $(nrsdrv_OBJECTS) $(nrsdrv_LDADD) $(LIBS)
+
+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-man4 install-man5 install-man8
+uninstall-man:
+ @$(NORMAL_UNINSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) 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 = netrom
+
+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
+netromd.o: netromd.c ../config.h ../pathnames.h netromd.h
+netromr.o: netromr.c ../pathnames.h netromd.h
+netromt.o: netromt.c ../pathnames.h netromd.h
+nodesave.o: nodesave.c
+nrattach.o: nrattach.c ../config.h ../pathnames.h
+nrparms.o: nrparms.c ../config.h
+nrsdrv.o: nrsdrv.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-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-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)$(sbindir) $(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-sbinPROGRAMS mostlyclean-compile \
+ mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-sbinPROGRAMS clean-compile clean-tags clean-generic \
+ mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-sbinPROGRAMS distclean-compile distclean-tags \
+ distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: 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-sbinPROGRAMS distclean-sbinPROGRAMS \
+clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \
+install-sbinPROGRAMS mostlyclean-compile distclean-compile \
+clean-compile maintainer-clean-compile 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
+
+# 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/netrom/netrom.4 b/netrom/netrom.4
new file mode 100644
index 0000000..67b159a
--- /dev/null
+++ b/netrom/netrom.4
@@ -0,0 +1,51 @@
+.TH NETROM 4 "25 July 1996" Linux "Linux Programmer's Manual"
+.SH NAME
+AF_NETROM \- NET/ROM amateur packet radio protocol family
+.SH DESCRIPTION
+.LP
+.B NET/ROM
+is a protocol used extensively by radio amateurs. The Linux
+NET/ROM protocol family permits access to these protocols via
+the standard networking
+.B socket
+metaphor.
+.LP
+The NET/ROM protocol layer only supports connected mode. IP traffic may be
+stacked on top of NET/ROM frames using a non-standard extension to the
+NET/ROM protocol.
+.LP
+The only mode of operation is connected mode which is the mode used for a
+socket of type SOCK_SEQPACKET (stream sockets are not available in NET/ROM).
+This requires that the user ensures output data is suitably packetised, and
+that input data is read a packet at a time into a buffer of suitable size.
+.LP
+NET/ROM addresses consist of 6 ascii characters and a number called the
+SSID. These are encoded into a sockaddr_ax25 structure which is provided to
+the relevant system calls.
+.LP
+NET/ROM has some unusual properties. Notably in a multi-user system an AX.25
+address is often associated with a user, and some users may not have such an
+association. a set of ioctl calls are provided to manage an association
+table.
+.LP
+NET/ROM supports the following socket options for SOL_NETROM. NETROM_T1 is
+the T1 timer in 1/10ths of a second, NETROM_T2 is the T2 timer in 1/10ths of
+a second. NETROM_N2, the retry counter is also configurable. There is no 'infinite
+retry' option supported however. It is possible for an application
+to request that the NET/ROM layer return the NET/ROM header as well as the
+application data, this is done via the NETROM_HDRINCL socket option.
+.SH "SEE ALSO"
+.BR call (1),
+.BR socket (2),
+.BR setsockopt(2),
+.BR getsockopt(2),
+.BR nrbroadcast (5),
+.BR nrports (5),
+.BR netromd (8),
+.BR noderest (8),
+.BR nodesave (8),
+.BR nrctl (8),
+.BR nrparms (8).
+.LP
+.SH AUTHOR
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
diff --git a/netrom/netromd.8 b/netrom/netromd.8
new file mode 100644
index 0000000..ef1860e
--- /dev/null
+++ b/netrom/netromd.8
@@ -0,0 +1,97 @@
+.TH NETROMD 8 "20 August 1996" Linux "Linux System Managers Manual"
+.SH NAME
+netromd \- Send and receive NET/ROM routing messages
+.SH SYNOPSIS
+.B netromd [-c] [-d] [-i] [-l] [-p pause] [-q quality] [-t interval] [-v]
+.SH DESCRIPTION
+.LP
+For a NET/ROM based network to operate correctly, a periodic broadcast of
+routing information needs to occur. Typically this occurs once every hour on
+every port which is expected to carry NET/ROM traffic. The purpose of
+.B netromd
+is to send and receive NET/ROM routing broadcasts. To operate correctly a
+set of parameters that corresponds to each AX.25 port needs to be passed to
+the program. This information is encoded in a configuration file, by default
+which is /etc/ax25/nrbroadcast with each line representing one
+port, see the manual page for
+.BR nrbroadcast (5).
+.LP
+To cut down the length of these routing broadcasts, only the information
+about the highest quality neighbour for a particular node is transmitted.
+The transmission is also limited to those node that have a certain minimum
+value in their obsolesence count, this value is decremented every time a
+routing broadcast is transmitted, and is refreshed by receiving a routing
+broadcast which contains that particular node.
+.LP
+The value of the default quality is traditionally assigned a value that
+represents the quality of the radio links on that port. A higher number
+representing better radio links with 255 (the maximum) reserved for wire
+connections. The practise in the UK is to set the default quality to a low
+value, typically 10, and manually set up the trusted neighbouring nodes in
+the neighbour list manually. The worst quality for auto-updates value is a
+way to filter out low quality (ie distant) nodes.
+.LP
+The verbose flag may be either 0 or 1, representing no and yes. By
+specifying no, the program will only generate a routing message containing
+information about the node on which it is running, by specifying the yes
+option, all the information in the nodes routing tables will be transmitted.
+The quality advertised for the other node callsigns on this machine may be
+set using the \-q option.
+.LP
+Between each transmission
+.B netromd
+pauses for five seconds (default) in order to avoid flooding the channels
+that it must broadcast on. The value of this delay is settable with the \-p
+option.
+.SH OPTIONS
+.TP 16
+.BI \-c
+Forces strict compliance to Software 2000 specifications. At present this
+only determines how node mnemonics with lower case characters will be handled.
+With compliance enabled mixed case node mnemonics will be ignored. The default
+is to accept node mnemonics of mixed case.
+.TP 16
+.BI \-d
+Switches on debugging messages, the default is off. Logging must be enabled
+for them to be output.
+.TP 16
+.BI \-i
+Transmit a routing broadcast immediately, the default is to wait for the
+time interval to elapse before transmitting the first routing broadcast.
+.TP 16
+.BI \-l
+Enables logging of errors and debug messages to the system log. The default
+is off.
+.TP 16
+.BI "\-p pause"
+Sets the delay between transmissions of individual routing broadcast
+packets. The default is five seconds.
+.TP 16
+.BI "\-q quality"
+Sets the quality of the subsidiary nodes relative to the main node. The
+default is 255.
+.TP 16
+.BI "\-t interval"
+The time interval between routing broadcasts, in minutes. The default is 60
+minutes.
+.TP 16
+.BI \-v
+Display the version.
+.SH FILES
+.nf
+/proc/net/nr_neigh
+.br
+/proc/net/nr_nodes
+.br
+/etc/ax25/axports
+.br
+/etc/ax25/nrbroadcast
+.fi
+.SH "SEE ALSO"
+.BR ax25 (4),
+.BR axports (5),
+.BR nrbroadcast (5),
+.BR netrom (4),
+.BR nrparms (8).
+.SH AUTHOR
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
diff --git a/netrom/netromd.c b/netrom/netromd.c
new file mode 100644
index 0000000..fa80c9b
--- /dev/null
+++ b/netrom/netromd.c
@@ -0,0 +1,254 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <netax25/ax25.h>
+#include <netrom/netrom.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <netax25/daemon.h>
+#include <netax25/nrconfig.h>
+
+#include <config.h>
+
+#include "../pathnames.h"
+#include "netromd.h"
+
+struct port_struct port_list[20];
+
+int port_count = FALSE;
+int compliant = FALSE;
+int debug = FALSE;
+int logging = FALSE;
+
+ax25_address my_call;
+ax25_address node_call;
+
+static void terminate(int sig)
+{
+ if (logging) {
+ syslog(LOG_INFO, "terminating on SIGTERM\n");
+ closelog();
+ }
+
+ exit(0);
+}
+
+static int bcast_config_load_ports(void)
+{
+ char buffer[255], port[32], *s;
+ FILE *fp;
+
+ if ((fp = fopen(CONF_NETROMD_FILE, "r")) == NULL) {
+ fprintf(stderr, "netromd: cannot open config file\n");
+ return -1;
+ }
+
+ while (fgets(buffer, 255, fp) != NULL) {
+ if ((s = strchr(buffer, '\n')) != NULL)
+ *s = '\0';
+
+ if (strlen(buffer) == 0 || buffer[0] == '#')
+ continue;
+
+ if (sscanf(buffer, "%s %d %d %d %d",
+ port,
+ &port_list[port_count].minimum_obs,
+ &port_list[port_count].default_qual,
+ &port_list[port_count].worst_qual,
+ &port_list[port_count].verbose) == -1) {
+ fprintf(stderr, "netromd: unable to parse: %s", buffer);
+ return -1;
+ }
+
+ if (ax25_config_get_addr(port) == NULL) {
+ fprintf(stderr, "netromd: invalid port name - %s\n", port);
+ return -1;
+ }
+
+ port_list[port_count].port = strdup(port);
+ port_list[port_count].device = strdup(ax25_config_get_dev(port_list[port_count].port));
+
+ if (port_list[port_count].minimum_obs < 0 || port_list[port_count].minimum_obs > 6) {
+ fprintf(stderr, "netromd: invalid minimum obsolescence\n");
+ return -1;
+ }
+
+ if (port_list[port_count].default_qual < 0 || port_list[port_count].default_qual > 255) {
+ fprintf(stderr, "netromd: invalid default quality\n");
+ return -1;
+ }
+
+ if (port_list[port_count].worst_qual < 0 || port_list[port_count].worst_qual > 255) {
+ fprintf(stderr, "netromd: invalid worst quality\n");
+ return -1;
+ }
+
+ if (port_list[port_count].verbose != 0 && port_list[port_count].verbose != 1) {
+ fprintf(stderr, "netromd: invalid verbose setting\n");
+ return -1;
+ }
+
+ port_count++;
+ }
+
+ fclose(fp);
+
+ if (port_count == 0)
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ unsigned char buffer[512];
+ int size, s, i;
+ struct sockaddr sa;
+ int asize;
+ struct timeval timeout;
+ time_t timenow, timelast;
+ int interval = 3600;
+ int localval = 255;
+ int pause = 5;
+ fd_set fdset;
+
+ time(&timelast);
+
+ while ((i = getopt(argc, argv, "cdilp:q:t:v")) != -1) {
+ switch (i) {
+ case 'c':
+ compliant = TRUE;
+ break;
+ case 'd':
+ debug = TRUE;
+ break;
+ case 'i':
+ timelast = 0;
+ break;
+ case 'l':
+ logging = TRUE;
+ break;
+ case 'p':
+ pause = atoi(optarg);
+ if (pause < 1 || pause > 120) {
+ fprintf(stderr, "netromd: invalid pause value\n");
+ return 1;
+ }
+ break;
+ case 'q':
+ localval = atoi(optarg);
+ if (localval < 10 || localval > 255) {
+ fprintf(stderr, "netromd: invalid local quality\n");
+ return 1;
+ }
+ break;
+ case 't':
+ interval = atoi(optarg) * 60;
+ if (interval < 60 || interval > 7200) {
+ fprintf(stderr, "netromd: invalid time interval\n");
+ return 1;
+ }
+ break;
+ case 'v':
+ printf("netromd: %s\n", VERSION);
+ return 0;
+ case '?':
+ case ':':
+ fprintf(stderr, "usage: netromd [-d] [-i] [-l] [-q quality] [-t interval] [-v]\n");
+ return 1;
+ }
+ }
+
+ signal(SIGTERM, terminate);
+
+ if (ax25_config_load_ports() == 0) {
+ fprintf(stderr, "netromd: no AX.25 ports defined\n");
+ return 1;
+ }
+
+ if (nr_config_load_ports() == 0) {
+ fprintf(stderr, "netromd: no NET/ROM ports defined\n");
+ return 1;
+ }
+
+ if (bcast_config_load_ports() == -1) {
+ fprintf(stderr, "netromd: no NET/ROM broadcast ports defined\n");
+ return 1;
+ }
+
+ ax25_aton_entry(nr_config_get_addr(NULL), (char *)&my_call);
+ ax25_aton_entry("NODES", (char *)&node_call);
+
+ if ((s = socket(AF_INET, SOCK_PACKET, htons(ETH_P_AX25))) == -1) {
+ perror("netromd: socket");
+ return 1;
+ }
+
+ if (!daemon_start(TRUE)) {
+ fprintf(stderr, "netromd: cannot become a daemon\n");
+ return 1;
+ }
+
+ if (logging) {
+ openlog("netromd", LOG_PID, LOG_DAEMON);
+ syslog(LOG_INFO, "starting");
+ }
+
+ for (;;) {
+ FD_ZERO(&fdset);
+ FD_SET(s, &fdset);
+
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+
+ if (select(s + 1, &fdset, NULL, NULL, &timeout) == -1)
+ continue; /* Signal received ? */
+
+ if (FD_ISSET(s, &fdset)) {
+ asize = sizeof(sa);
+
+ if ((size = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &asize)) == -1) {
+ if (logging) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ closelog();
+ }
+ return 1;
+ }
+
+ if (ax25_cmp((ax25_address *)(buffer + 1), &node_call) == 0 &&
+ ax25_cmp((ax25_address *)(buffer + 8), &my_call) != 0 &&
+ buffer[16] == NETROM_PID && buffer[17] == NODES_SIG) {
+ for (i = 0; i < port_count; i++) {
+ if (strcmp(port_list[i].device, sa.sa_data) == 0) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, "receiving NODES broadcast on port %s from %s\n", port_list[i].port, ax25_ntoa((ax25_address *)(buffer + 8)));
+ receive_nodes(buffer + 18, size - 18, (ax25_address *)(buffer + 8), i);
+ break;
+ }
+ }
+ }
+ }
+
+ time(&timenow);
+
+ if ((timenow - timelast) > interval) {
+ timelast = timenow;
+ transmit_nodes(localval, pause);
+ }
+ }
+}
diff --git a/netrom/netromd.h b/netrom/netromd.h
new file mode 100644
index 0000000..3007b02
--- /dev/null
+++ b/netrom/netromd.h
@@ -0,0 +1,41 @@
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NETROM_PID 0xCF
+
+#define NODES_SIG 0xFF
+
+#define CALLSIGN_LEN 7
+#define MNEMONIC_LEN 6
+#define QUALITY_LEN 1
+
+#define NODES_PACLEN 256
+#define ROUTE_LEN (CALLSIGN_LEN+MNEMONIC_LEN+CALLSIGN_LEN+QUALITY_LEN)
+
+struct port_struct {
+ char *device;
+ char *port;
+ int minimum_obs;
+ int default_qual;
+ int worst_qual;
+ int verbose;
+};
+
+extern struct port_struct port_list[];
+
+extern int port_count;
+extern int debug;
+extern int logging;
+
+extern ax25_address my_call;
+extern ax25_address node_call;
+
+/* In netromt.c */
+extern void transmit_nodes(int, int);
+
+/* In netromr.c */
+extern void receive_nodes(unsigned char *, int, ax25_address *, int);
diff --git a/netrom/netromr.c b/netrom/netromr.c
new file mode 100644
index 0000000..eafd225
--- /dev/null
+++ b/netrom/netromr.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+
+#include <netax25/ax25.h>
+#include <netrom/netrom.h>
+#include <netrose/rose.h>
+#include <netax25/axlib.h>
+#include "../pathnames.h"
+
+#include "netromd.h"
+
+extern int compliant;
+
+static int validmnemonic(char *mnemonic)
+{
+ if (compliant) {
+ if (strspn(mnemonic, "#_&-/ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") == strlen(mnemonic))
+ return TRUE;
+ } else {
+ if (strspn(mnemonic, "#_&-/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") == strlen(mnemonic))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void receive_nodes(unsigned char *buffer, int length, ax25_address *neighbour, int index)
+{
+ struct nr_route_struct nr_node;
+ char neigh_buffer[90], *portcall, *p;
+ FILE *fp;
+ int s;
+ int quality, obs_count, qual, lock;
+ char *addr, *callsign, *device;
+
+ nr_node.type = NETROM_NODE;
+ /*nr_node.ndigis = 0;*/
+
+ sprintf(neigh_buffer, "%s/obsolescence_count_initialiser", PROC_NR_SYSCTL_DIR);
+
+ if ((fp = fopen(neigh_buffer, "r")) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "netromr: cannot open %s\n", neigh_buffer);
+ return;
+ }
+
+ fgets(neigh_buffer, 90, fp);
+
+ obs_count = atoi(neigh_buffer);
+
+ fclose(fp);
+
+ if ((s = socket(AF_NETROM, SOCK_SEQPACKET, 0)) < 0) {
+ if (logging)
+ syslog(LOG_ERR, "netromr: socket: %m");
+ return;
+ }
+
+ if ((fp = fopen(PROC_NR_NEIGH_FILE, "r")) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "netromr: cannot open %s\n", PROC_NR_NEIGH_FILE);
+ close(s);
+ return;
+ }
+
+ fgets(neigh_buffer, 90, fp);
+
+ portcall = ax25_ntoa(neighbour);
+
+ quality = port_list[index].default_qual;
+
+ while (fgets(neigh_buffer, 90, fp) != NULL) {
+ addr = strtok(neigh_buffer, " ");
+ callsign = strtok(NULL, " ");
+ device = strtok(NULL, " ");
+ qual = atoi(strtok(NULL, " "));
+ lock = atoi(strtok(NULL, " "));
+
+ if (strcmp(callsign, portcall) == 0 &&
+ strcmp(device, port_list[index].device) == 0 &&
+ lock == 1) {
+ quality = qual;
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ nr_node.callsign = *neighbour;
+ memcpy(nr_node.mnemonic, buffer, MNEMONIC_LEN);
+ nr_node.mnemonic[MNEMONIC_LEN] = '\0';
+
+ if ((p = strchr(nr_node.mnemonic, ' ')) != NULL)
+ *p = '\0';
+
+ if (!validmnemonic(nr_node.mnemonic)) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, "rejecting route, invalid mnemonic - %s\n", nr_node.mnemonic);
+ } else {
+ nr_node.neighbour = *neighbour;
+ strcpy(nr_node.device, port_list[index].device);
+ nr_node.quality = quality;
+ nr_node.obs_count = obs_count;
+
+ if (ioctl(s, SIOCADDRT, &nr_node) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromr: SIOCADDRT: %m");
+ close(s);
+ return;
+ }
+ }
+
+ buffer += MNEMONIC_LEN;
+ length -= MNEMONIC_LEN;
+
+ while (length >= ROUTE_LEN) {
+ /*
+ * Ensure that a) the route is not via me, and
+ * b) it is better than my minimum acceptable quality
+ */
+ if (ax25_cmp(&my_call, (ax25_address *)(buffer + 13)) != 0 &&
+ buffer[20] > port_list[index].worst_qual) {
+ memcpy(&nr_node.callsign, buffer + 0, CALLSIGN_LEN);
+ memcpy(nr_node.mnemonic, buffer + 7, MNEMONIC_LEN);
+ nr_node.mnemonic[MNEMONIC_LEN] = '\0';
+
+ if ((p = strchr(nr_node.mnemonic, ' ')) != NULL)
+ *p = '\0';
+
+ if (!validmnemonic(nr_node.mnemonic)) {
+ if (debug && logging)
+ syslog(LOG_DEBUG, "rejecting route, invalid mnemonic - %s\n", nr_node.mnemonic);
+ } else {
+ nr_node.neighbour = *neighbour;
+ strcpy(nr_node.device, port_list[index].device);
+ nr_node.quality = ((quality * buffer[20]) + 128) / 256;
+ nr_node.obs_count = obs_count;
+
+ if (ioctl(s, SIOCADDRT, &nr_node) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromr: SIOCADDRT: %m");
+ close(s);
+ return;
+ }
+ }
+ }
+
+ buffer += ROUTE_LEN;
+ length -= ROUTE_LEN;
+ }
+
+ close(s);
+}
diff --git a/netrom/netromt.c b/netrom/netromt.c
new file mode 100644
index 0000000..d22caf1
--- /dev/null
+++ b/netrom/netromt.c
@@ -0,0 +1,268 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netax25/ax25.h>
+#include <netrom/netrom.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <netax25/nrconfig.h>
+
+#include "../pathnames.h"
+#include "netromd.h"
+
+static int build_header(unsigned char *message)
+{
+ message[0] = NODES_SIG;
+
+ strcpy(message + 1, nr_config_get_alias(NULL));
+ strncat(message + 1, " ", MNEMONIC_LEN - strlen(message + 1));
+
+ return 7;
+}
+
+static void build_mine(int s, struct full_sockaddr_ax25 *dest, int dlen, int localval, int pause)
+{
+ unsigned char message[100];
+ char buffer[255], *port, *p;
+ FILE *fp;
+ int len;
+
+ len = build_header(message);
+
+ if ((fp = fopen(CONF_NRPORTS_FILE, "r")) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: cannot open nrports file\n");
+ return;
+ }
+
+ while (fgets(buffer, 255, fp) != NULL) {
+ if ((p = strchr(buffer, '\n')) != NULL)
+ *p = '\0';
+
+ if (strlen(buffer) == 0 || buffer[0] == '#')
+ continue;
+
+ port = strtok(buffer, " \t");
+
+ if (nr_config_get_addr(port) == NULL)
+ continue;
+
+ if (strcmp(nr_config_get_addr(port), nr_config_get_addr(NULL)) == 0)
+ continue;
+
+ if (ax25_aton_entry(nr_config_get_addr(port), message + len) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: invalid callsign in nrports\n");
+ fclose(fp);
+ return;
+ }
+ len += CALLSIGN_LEN;
+
+ strcpy(message + len, nr_config_get_alias(port));
+ strncat(message + len, " ", MNEMONIC_LEN - strlen(message + len));
+ len += MNEMONIC_LEN;
+
+ ax25_aton_entry(nr_config_get_addr(NULL), message + len);
+ len += CALLSIGN_LEN;
+
+ message[len] = localval;
+ len += QUALITY_LEN;
+ }
+
+ fclose(fp);
+
+ if (sendto(s, message, len, 0, (struct sockaddr *)dest, dlen) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: sendto: %m");
+ }
+
+ sleep(pause);
+}
+
+static void build_others(int s, int min_obs, struct full_sockaddr_ax25 *dest, int dlen, int port, int pause)
+{
+ unsigned char message[300];
+ FILE *fpnodes, *fpneigh;
+ char nodes_buffer[90];
+ char neigh_buffer[90];
+ char *callsign, *mnemonic, *neighbour;
+ int which, number, quality, neigh_no, obs_count;
+ int olen, len;
+
+ if ((fpnodes = fopen(PROC_NR_NODES_FILE, "r")) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: cannot open %s\n", PROC_NR_NODES_FILE);
+ return;
+ }
+
+ if ((fpneigh = fopen(PROC_NR_NEIGH_FILE, "r")) == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: cannot open %s\n", PROC_NR_NEIGH_FILE);
+ fclose(fpnodes);
+ return;
+ }
+
+ fgets(nodes_buffer, 90, fpnodes);
+
+ do {
+ len = olen = build_header(message);
+
+ while (fgets(nodes_buffer, 90, fpnodes) != NULL) {
+ callsign = strtok(nodes_buffer, " ");
+ mnemonic = strtok(NULL, " ");
+ which = atoi(strtok(NULL, " "));
+ number = atoi(strtok(NULL, " "));
+ quality = atoi(strtok(NULL, " "));
+ obs_count = atoi(strtok(NULL, " "));
+ neigh_no = atoi(strtok(NULL, " "));
+ neighbour = NULL;
+
+ if (obs_count < min_obs || quality == 0) continue;
+
+ /* "Blank" mnemonic */
+ if (strcmp(mnemonic, "*") == 0)
+ mnemonic = "";
+
+ fseek(fpneigh, 0L, SEEK_SET);
+
+ fgets(neigh_buffer, 90, fpneigh);
+
+ while (fgets(neigh_buffer, 90, fpneigh) != NULL) {
+ if (atoi(strtok(neigh_buffer, " ")) == neigh_no) {
+ neighbour = strtok(NULL, " ");
+ break;
+ }
+ }
+
+ if (neighbour == NULL) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: corruption in nodes/neighbour matching\n");
+ continue;
+ }
+
+ if (ax25_aton_entry(callsign, message + len) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: invalid callsign '%s' in /proc/net/nr_nodes\n", callsign);
+ continue;
+ }
+ len += CALLSIGN_LEN;
+
+ strcpy(message + len, mnemonic);
+ strncat(message + len, " ", MNEMONIC_LEN - strlen(message + len));
+ len += MNEMONIC_LEN;
+
+ if (ax25_aton_entry(neighbour, message + len) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: invalid callsign '%s' in /proc/net/nr_neigh\n", neighbour);
+ len -= (CALLSIGN_LEN + MNEMONIC_LEN);
+ continue;
+ }
+ len += CALLSIGN_LEN;
+
+ message[len] = quality;
+ len += QUALITY_LEN;
+
+ /* No room for another entry? */
+ if (len + ROUTE_LEN > NODES_PACLEN)
+ break;
+ }
+
+ /* Only send it if there is more that just the header */
+ if (len > olen) {
+ if (sendto(s, message, len, 0, (struct sockaddr *)dest, dlen) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: sendto: %m");
+ }
+
+ sleep(pause);
+ }
+
+ /* If the packet was not full then we are done */
+ } while (len + ROUTE_LEN > NODES_PACLEN);
+
+ fclose(fpnodes);
+ fclose(fpneigh);
+}
+
+void transmit_nodes(int localval, int pause)
+{
+ struct full_sockaddr_ax25 dest;
+ struct full_sockaddr_ax25 src;
+ int s, dlen, slen;
+ char path[25], *addr;
+ int i;
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ if (logging)
+ syslog(LOG_ERR, "netromt: fork: %m\n");
+ return;
+ default:
+ return;
+ }
+
+ dlen = ax25_aton("NODES", &dest);
+
+ for (i = 0; i < port_count; i++) {
+
+ addr = ax25_config_get_addr(port_list[i].port);
+
+ if (addr == NULL) continue;
+
+ if (debug && logging)
+ syslog(LOG_DEBUG, "transmitting NODES broadcast on port %s\n", port_list[i].port);
+
+ sprintf(path, "%s %s", nr_config_get_addr(NULL), addr);
+
+ ax25_aton(path, &src);
+ slen = sizeof(struct full_sockaddr_ax25);
+
+ if ((s = socket(AF_AX25, SOCK_DGRAM, NETROM_PID)) < 0) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: socket: %m");
+ continue;
+ }
+
+ if (bind(s, (struct sockaddr *)&src, slen) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: bind: %m");
+ close(s);
+ continue;
+ }
+
+ build_mine(s, &dest, dlen, localval, pause);
+
+ if (port_list[i].verbose)
+ build_others(s, port_list[i].minimum_obs, &dest, dlen, i, pause);
+
+ close(s);
+ }
+
+ if ((s = socket(AF_NETROM, SOCK_SEQPACKET, 0)) < 0) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: socket: %m");
+ exit(1);
+ }
+
+ if (ioctl(s, SIOCNRDECOBS, &i) == -1) {
+ if (logging)
+ syslog(LOG_ERR, "netromt: SIOCNRDECOBS: %m");
+ exit(1);
+ }
+
+ close(s);
+
+ exit(0);
+}
diff --git a/netrom/nodesave.8 b/netrom/nodesave.8
new file mode 100644
index 0000000..626778b
--- /dev/null
+++ b/netrom/nodesave.8
@@ -0,0 +1,31 @@
+.TH NODESAVE 8 "21 May 1996" Linux "Linux System Managers Manual"
+.SH NAME
+nodesave \- Saves NET/ROM routing information
+.SH SYNOPSIS
+.B nodesave [filename]
+.SH DESCRIPTION
+.LP
+.B Nodesave
+saves the contents of the /proc filesystem entries for the NET/ROM routing
+tables. The output of the program is in the form of a Bourne shell script
+that calls nrparms(8) to recreate the NET/ROM routing information. If no
+filename is given on the command line, the program writes the script to
+stdout.
+.LP
+Typically
+.B nodesave
+would be used when taking a system off-air so that the NET/ROM routing may
+be quickly restored when the system is brought back on-line.
+.SH FILES
+.nf
+/proc/net/nr_neigh
+.br
+/proc/net/nr_nodes
+.fi
+.SH "SEE ALSO"
+.BR netrom (4),
+.BR netromd (8),
+.BR nrparms (8).
+.LP
+.SH AUTHOR
+Tomi Manninen OH2BNS <tpmannin@cc.hut.fi>
diff --git a/netrom/nodesave.c b/netrom/nodesave.c
new file mode 100644
index 0000000..3eab61b
--- /dev/null
+++ b/netrom/nodesave.c
@@ -0,0 +1,101 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <netax25/ax25.h>
+
+#include <netax25/axconfig.h>
+
+#include <netax25/procutils.h>
+
+int main(int argc, char **argv)
+{
+ FILE *fp = stdout;
+ struct proc_nr_nodes *nodes, *nop;
+ struct proc_nr_neigh *neighs, *nep;
+
+ if (ax25_config_load_ports() == 0) {
+ fprintf(stderr, "nodesave: no AX.25 port data configured\n");
+ return 1;
+ }
+
+ if (argc > 1) {
+ if ((fp = fopen(argv[1], "w")) == NULL) {
+ fprintf(stderr, "nodesave: cannot open file %s\n", argv[1]);
+ return 1;
+ }
+ }
+
+ if ((neighs = read_proc_nr_neigh()) == NULL && errno != 0) {
+ perror("nodesave: read_proc_nr_neigh");
+ fclose(fp);
+ return 1;
+ }
+
+ if ((nodes = read_proc_nr_nodes()) == NULL && errno != 0) {
+ perror("nodesave: read_proc_nr_nodes");
+ free_proc_nr_neigh(neighs);
+ fclose(fp);
+ return 1;
+ }
+
+ fprintf(fp, "#! /bin/sh\n#\n# Locked routes:\n#\n");
+
+ for (nep = neighs; nep != NULL; nep = nep->next) {
+ if (nep->lock) {
+ fprintf(fp, "nrparms -routes \"%s\" %s + %d\n",
+ ax25_config_get_name(nep->dev),
+ nep->call,
+ nep->qual);
+ }
+ }
+
+ fprintf(fp, "#\n# Nodes:\n#\n");
+
+ for (nop = nodes; nop != NULL; nop = nop->next) {
+ if ((nep = find_neigh(nop->addr1, neighs)) != NULL) {
+ fprintf(fp, "nrparms -nodes %s + \"%s\" %d %d \"%s\" %s\n",
+ nop->call,
+ nop->alias,
+ nop->qual1,
+ nop->obs1,
+ ax25_config_get_name(nep->dev),
+ nep->call);
+ }
+
+ if (nop->n > 1 && (nep = find_neigh(nop->addr2, neighs)) != NULL) {
+ fprintf(fp, "nrparms -nodes %s + \"%s\" %d %d \"%s\" %s\n",
+ nop->call,
+ nop->alias,
+ nop->qual2,
+ nop->obs2,
+ ax25_config_get_name(nep->dev),
+ nep->call);
+ }
+
+ if (nop->n > 2 && (nep = find_neigh(nop->addr3, neighs)) != NULL) {
+ fprintf(fp, "nrparms -nodes %s + \"%s\" %d %d \"%s\" %s\n",
+ nop->call,
+ nop->alias,
+ nop->qual3,
+ nop->obs3,
+ ax25_config_get_name(nep->dev),
+ nep->call);
+ }
+ }
+
+ free_proc_nr_neigh(neighs);
+ free_proc_nr_nodes(nodes);
+
+ fclose(fp);
+
+ if (argc > 1) {
+ chmod(argv[1], S_IEXEC);
+ }
+
+ return 0;
+}
diff --git a/netrom/nrattach.8 b/netrom/nrattach.8
new file mode 100644
index 0000000..692e3f2
--- /dev/null
+++ b/netrom/nrattach.8
@@ -0,0 +1,39 @@
+.TH NRATTACH 8 "21 May 1996" Linux "Linux System Managers Manual"
+.SH NAME
+nrattach \- Start a NET/ROM interface
+.SH SYNOPSIS
+.B nrattach [-i inetaddr] [-m mtu] [-v] port
+.SH DESCRIPTION
+.LP
+.B Nrattach
+takes many of the parameters for the port from the nrports(5) file. The
+paclen parameter is used for the device mtu unless overridden by a value on
+the command line. The port argument is the name of a port as given in the
+nrports(5) file.
+.LP
+.B Nrattach
+tries to find the first free NET/ROM device in the system. The devices
+checked are nr0, nr1, nr2 and nr3 in that order. If no free NET/ROM device
+is available an error is generated and the program terminates.
+.SH OPTIONS
+.TP 16
+.BI "\-i inetaddr"
+Set the internet address of the interface. This address may either be a
+dotted decimal address or a host name.
+.TP 16
+.BI "\-m mtu"
+Sets the mtu of the interface. If this value is not given then the value is
+taken from the paclen parameter in nrports.
+.TP 16
+.BI \-v
+Display the version.
+.SH "SEE ALSO"
+.BR netrom (4),
+.BR nrparms (4),
+.BR nrports (5),
+.BR ifconfig (8).
+.SH BUGS
+The program can be run many times with the same arguments creating many
+instances of the same attributes on different devices. Not a good idea.
+.SH AUTHOR
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
diff --git a/netrom/nrattach.c b/netrom/nrattach.c
new file mode 100644
index 0000000..aee0936
--- /dev/null
+++ b/netrom/nrattach.c
@@ -0,0 +1,240 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <ctype.h>
+#include <netdb.h>
+
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+#include <netax25/axlib.h>
+
+#include <config.h>
+
+#include "../pathnames.h"
+
+char *callsign;
+int mtu = 0;
+
+int readconfig(char *port)
+{
+ FILE *fp;
+ char buffer[90], *s;
+ int n = 0;
+
+ if ((fp = fopen(CONF_NRPORTS_FILE, "r")) == NULL) {
+ fprintf(stderr, "nrattach: cannot open nrports file\n");
+ return FALSE;
+ }
+
+ while (fgets(buffer, 90, fp) != NULL) {
+ n++;
+
+ if ((s = strchr(buffer, '\n')) != NULL)
+ *s = '\0';
+
+ if (strlen(buffer) > 0 && *buffer == '#')
+ continue;
+
+ if ((s = strtok(buffer, " \t\r\n")) == NULL) {
+ fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n);
+ return FALSE;
+ }
+
+ if (strcmp(s, port) != 0)
+ continue;
+
+ if ((s = strtok(NULL, " \t\r\n")) == NULL) {
+ fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n);
+ return FALSE;
+ }
+
+ callsign = strdup(s);
+
+ if ((s = strtok(NULL, " \t\r\n")) == NULL) {
+ fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n);
+ return FALSE;
+ }
+
+ if ((s = strtok(NULL, " \t\r\n")) == NULL) {
+ fprintf(stderr, "nrattach: unable to parse line %d of the nrports file\n", n);
+ return FALSE;
+ }
+
+ if (mtu == 0) {
+ if ((mtu = atoi(s)) <= 0) {
+ fprintf(stderr, "nrattach: invalid paclen setting\n");
+ return FALSE;
+ }
+ }
+
+ fclose(fp);
+
+ return TRUE;
+ }
+
+ fclose(fp);
+
+ fprintf(stderr, "nrattach: cannot find port %s in nrports\n", port);
+
+ return FALSE;
+}
+
+int getfreedev(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+ int i;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("nrattach: socket");
+ return FALSE;
+ }
+
+ for (i = 0; i < 4; i++) {
+ sprintf(dev, "nr%d", i);
+ strcpy(ifr.ifr_name, dev);
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ perror("nrattach: SIOCGIFFLAGS");
+ return FALSE;
+ }
+
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ close(fd);
+ return TRUE;
+ }
+ }
+
+ close(fd);
+
+ return FALSE;
+}
+
+int startiface(char *dev, struct hostent *hp)
+{
+ struct ifreq ifr;
+ char call[7];
+ int fd;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("nrattach: socket");
+ return FALSE;
+ }
+
+ strcpy(ifr.ifr_name, dev);
+
+ if (hp != NULL) {
+ ifr.ifr_addr.sa_family = AF_INET;
+
+ ifr.ifr_addr.sa_data[0] = 0;
+ ifr.ifr_addr.sa_data[1] = 0;
+ ifr.ifr_addr.sa_data[2] = hp->h_addr_list[0][0];
+ ifr.ifr_addr.sa_data[3] = hp->h_addr_list[0][1];
+ ifr.ifr_addr.sa_data[4] = hp->h_addr_list[0][2];
+ ifr.ifr_addr.sa_data[5] = hp->h_addr_list[0][3];
+ ifr.ifr_addr.sa_data[6] = 0;
+
+ if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) {
+ perror("nrattach: SIOCSIFADDR");
+ return FALSE;
+ }
+ }
+
+ if (ax25_aton_entry(callsign, call) == -1)
+ return FALSE;
+
+ ifr.ifr_hwaddr.sa_family = ARPHRD_NETROM;
+ memcpy(ifr.ifr_hwaddr.sa_data, call, 7);
+
+ if (ioctl(fd, SIOCSIFHWADDR, &ifr) != 0) {
+ perror("nrattach: SIOCSIFHWADDR");
+ return FALSE;
+ }
+
+ ifr.ifr_mtu = mtu;
+
+ if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) {
+ perror("nrattach: SIOCSIFMTU");
+ return FALSE;
+ }
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ perror("nrattach: SIOCGIFFLAGS");
+ return FALSE;
+ }
+
+ ifr.ifr_flags &= IFF_NOARP;
+ ifr.ifr_flags |= IFF_UP;
+ ifr.ifr_flags |= IFF_RUNNING;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
+ perror("nrattach: SIOCSIFFLAGS");
+ return FALSE;
+ }
+
+ close(fd);
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ char dev[64];
+ struct hostent *hp = NULL;
+
+ while ((fd = getopt(argc, argv, "i:m:v")) != -1) {
+ switch (fd) {
+ case 'i':
+ if ((hp = gethostbyname(optarg)) == NULL) {
+ fprintf(stderr, "nrattach: invalid internet name/address - %s\n", optarg);
+ return 1;
+ }
+ break;
+ case 'm':
+ if ((mtu = atoi(optarg)) <= 0) {
+ fprintf(stderr, "nrattach: invalid mtu size - %s\n", optarg);
+ return 1;
+ }
+ break;
+ case 'v':
+ printf("nrattach: %s\n", VERSION);
+ return 0;
+ case ':':
+ case '?':
+ fprintf(stderr, "usage: nrattach [-i inetaddr] [-m mtu] [-v] port\n");
+ return 1;
+ }
+ }
+
+ if ((argc - optind) != 1) {
+ fprintf(stderr, "usage: nrattach [-i inetaddr] [-m mtu] [-v] port\n");
+ return 1;
+ }
+
+ if (!readconfig(argv[optind]))
+ return 1;
+
+ if (!getfreedev(dev)) {
+ fprintf(stderr, "nrattach: cannot find free NET/ROM device\n");
+ return 1;
+ }
+
+ if (!startiface(dev, hp))
+ return 1;
+
+ printf("NET/ROM port %s bound to device %s\n", argv[optind], dev);
+
+ return 0;
+}
diff --git a/netrom/nrbroadcast b/netrom/nrbroadcast
new file mode 100644
index 0000000..8584b22
--- /dev/null
+++ b/netrom/nrbroadcast
@@ -0,0 +1,8 @@
+# /etc/ax25/nrbroadcast
+#
+# The format of this file is:
+#
+# ax25_name min_obs def_qual worst_qual verbose
+#
+1 5 192 100 0
+2 5 255 100 1
diff --git a/netrom/nrbroadcast.5 b/netrom/nrbroadcast.5
new file mode 100644
index 0000000..7258bc7
--- /dev/null
+++ b/netrom/nrbroadcast.5
@@ -0,0 +1,49 @@
+.TH NRBROADCAST 5 "2 August 1996" Linux "Linux Programmer's Manual"
+.SH NAME
+nrbroadcast \- NET/ROM routing broadcast configuration file.
+.SH DESCRIPTION
+.LP
+.B Nrbroadcast
+is an ASCII file that contains information about each of the physical AX.25
+ports that are to have NET/ROM routing broadcasts transmitted from them.
+.LP
+Each line has the following format, each field being delimited by white space:
+.sp
+.RS
+port minobs defqual worstqual verbose
+.RE
+.sp
+The field descriptions are:
+.sp
+.RS
+.TP 14
+.B port
+the port name of the AX.25 port to broadcast on.
+.TP
+.B minobs
+this is the minimum obsolescence count of a routing table entry to be
+broadcast on this port.
+.TP
+.B defqual
+this is the default quality of an incoming routing broadcast from an unknown
+neighbour.
+.TP
+.B worstqual
+this is the worst quality node received from a routing broadcast that will
+be added to our routing table.
+.TP
+.B verbose
+whether the routes to all of my nodes in my routing table, or just the nodes
+resident on my machine are to be broadcast.
+.RE
+.SH FILES
+.LP
+/etc/ax25/nrbroadcast
+.br
+/etc/ax25/axports
+.SH "SEE ALSO"
+.BR call (1),
+.BR netrom (4),
+.BR axports (5),
+.BR netromd (8),
+.BR nrparms (8).
diff --git a/netrom/nrparms.8 b/netrom/nrparms.8
new file mode 100644
index 0000000..38d3dd8
--- /dev/null
+++ b/netrom/nrparms.8
@@ -0,0 +1,76 @@
+.TH NRPARMS 8 "25 January 1997" Linux "Linux System Managers Manual"
+.SH NAME
+nrparms \- Configure the NET/ROM interface.
+.SH SYNOPSIS
+.B nrparms -nodes node +|- ident quality count port neighbour [digicall...]
+.LP
+.B nrparms -routes port nodecall [digicall...] +|- quality
+.LP
+.B nrparms -version
+.SH DESCRIPTION
+.LP
+This program is used to manipulate the routing tables of the NET/ROM network
+layer, or to get and set many of the network and transport layer parameters
+for the NET/ROM protocol. The program has three basic modes of operation,
+node setting, neighbour setting and general parameter setting. The syntax
+for the node and neighbour setting is taken from the original NET/ROM manual
+and is therefore not very UNIXy but should be familiar to those familiar
+with NET/ROMs or TheNet.
+.LP
+To set up a new route to a NET/ROM node in the routing tables you must use
+the nodes option. All of the parameters are needed to add the node. It is
+probably best to illustrate with an example:
+.LP
+.B nrparms -nodes GB7BPQ + NMCLUS 120 6 vhf G8ROU-3
+.LP
+This creates a new route to a distant node with the callsign GB7BPQ and the
+alias NMCLUS, it has a quality of 120 (out of 255) and has an obsolescence
+count of six and packets for that node should be sent on the AX.25 port named
+vhf to my immediate neighbour G8ROU-3. The callsigns of the node and the
+neighbour may be the same. For example to set up the node G8ROU-3 which is
+also my immediate neighbour, I would use:
+.LP
+.B nrparms -nodes G8ROU-3 + MATLCK 200 6 vhf G8ROU-3
+.LP
+If the ident of the remote node is not known, it is possible to add a
+node with a blank ident. To do this an ident of '*' must be entered on
+the command line. Because of the command line expansion that shells do, the *
+must be escaped by enclosing it in quotes.
+.LP
+It is also possible to remove a route to a distant node with the same
+command except that the + is replaced by a -. The other parameters must also
+be present. If the node has not other routes then the node will be deleted,
+and the neighbour node that the connections go via may also be deleted if no
+other node route uses it, and it is not a locked neighbour entry.
+.LP
+When setting up a new node, a new neighbour entry may also be created. This
+will have a default value. For that neighbour to be meaningful in the
+automatic routing process, it must have a more reasonable entry in the
+neighbours list. To do this the routes option of the command must be used.
+An example:
+.LP
+.B nrparms -routes ax0 G8ROU-3 + 120
+.LP
+This will create (or update) the neighbour entry for G8ROU-3 with a quality
+of 120 and it will be locked, it will not create a node entry for the
+neighbour. This quality will be used by the
+.BR netromd (8)
+program when calculating route qualities via this neighbour. Normally once a
+neighbour has zero node routes going via it, it will be deleted. Locking a
+neighbour prevents the deletion from occurring. To unlock a neighbour entry,
+the same command is used but with the + replaced by a -.
+.SH FILES
+.LP
+/etc/ax25/axports
+.br
+/etc/ax25/nrports
+.SH "SEE ALSO"
+.BR call (1),
+.BR netrom (4),
+.BR nrports (5),
+.BR axparms (8),
+.BR netromd (8),
+.BR nrctl (8),
+.BR nrparms (8).
+.SH AUTHOR
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
diff --git a/netrom/nrparms.c b/netrom/nrparms.c
new file mode 100644
index 0000000..7e89ff9
--- /dev/null
+++ b/netrom/nrparms.c
@@ -0,0 +1,239 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netax25/ax25.h>
+#include <netrom/netrom.h>
+#include <netrose/rose.h>
+
+#include <netax25/axlib.h>
+#include <netax25/axconfig.h>
+#include <netax25/nrconfig.h>
+
+#include <config.h>
+
+char nodes_usage[] = "usage: nrparms -nodes nodecall +|- ident quality count port neighbour [digicall...]\n";
+char routes_usage[] = "usage: nrparms -routes port nodecall [digicall...] +|- pathquality\n";
+
+void nodes(int s, char *nodecall, char *op, char *ident, int quality, int count, char *port, char *neighbour, char *digis[])
+{
+ struct nr_route_struct nr_node;
+ char *p, *q, *dev;
+ int i;
+
+ if (ax25_config_load_ports() == 0) {
+ fprintf(stderr, "nrparms: nodes: no AX.25 ports configured\n");
+ exit(1);
+ }
+
+ nr_node.type = NETROM_NODE;
+ /*nr_node.ndigis = 0;*/
+
+ if (op[0] != '+' && op[0] != '-') {
+ fprintf(stderr, "nrparms: nodes: invalid operation %s\n", op);
+ close(s);
+ exit(1);
+ }
+
+ if (quality < 1 || quality > 255) {
+ fprintf(stderr, "nrparms: nodes: invalid quality %d\n", quality);
+ close(s);
+ exit(1);
+ }
+
+ if (count < 1 || count > 6) {
+ fprintf(stderr, "nrparms: nodes: invalid obs count %d\n", count);
+ close(s);
+ exit(1);
+ }
+
+ if (ax25_aton_entry(nodecall, nr_node.callsign.ax25_call) != 0) {
+ fprintf(stderr, "nrparms: nodes: invalid callsign %s\n", nodecall);
+ close(s);
+ exit(1);
+ }
+
+ if (strlen(ident) > 7) {
+ fprintf(stderr, "nrparms: nodes: invalid length of mnemonic %s\n", ident);
+ close(s);
+ exit(1);
+ }
+
+ if (strcmp(ident, "*") != 0) {
+ for (p = ident, q = nr_node.mnemonic; *p != '\0'; p++, q++)
+ *q = toupper(*p);
+ *q = '\0';
+
+ if (strspn(nr_node.mnemonic, "&#-_/ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") != strlen(nr_node.mnemonic)) {
+ fprintf(stderr, "nrparms: nodes: invalid ident %s\n", ident);
+ close(s);
+ exit(1);
+ }
+ } else {
+ strcpy(nr_node.mnemonic, "");
+ }
+
+ if (ax25_aton_entry(neighbour, nr_node.neighbour.ax25_call) != 0) {
+ fprintf(stderr, "nrparms: nodes: invalid callsign %s\n", neighbour);
+ close(s);
+ exit(1);
+ }
+ /*
+ for (i = 0; i < AX25_MAX_DIGIS && digis[i] != NULL; i++) {
+ if (ax25_aton_entry(digis[i], nr_node.digipeaters[i].ax25_call) != 0) {
+ fprintf(stderr, "nrparms: invalid callsign %s\n", digis[i]);
+ close(s);
+ exit(1);
+ }
+ nr_node.ndigis++;
+ } */
+
+ if ((dev = ax25_config_get_dev(port)) == NULL) {
+ fprintf(stderr, "nrparms: nodes: invalid port name - %s\n", port);
+ close(s);
+ exit(1);
+ }
+
+ strcpy(nr_node.device, dev);
+
+ nr_node.quality = quality;
+ nr_node.obs_count = count;
+
+ if (op[0] == '+') {
+ if (ioctl(s, SIOCADDRT, &nr_node) == -1) {
+ perror("nrparms: SIOCADDRT");
+ close(s);
+ exit(1);
+ }
+ } else {
+ if (ioctl(s, SIOCDELRT, &nr_node) == -1) {
+ perror("nrparms: SIOCDELRT");
+ close(s);
+ exit(1);
+ }
+ }
+}
+
+void routes(int s, char *port, char *nodecall, char *rest[])
+{
+ struct nr_route_struct nr_neigh;
+ char *dev, *op;
+ int i, quality;
+
+ if (ax25_config_load_ports() == 0) {
+ fprintf(stderr, "nrparms: routes: no AX.25 ports configured\n");
+ exit(1);
+ }
+
+ nr_neigh.type = NETROM_NEIGH;
+ /*nr_neigh.ndigis = 0;
+
+ for (i = 0; i < AX25_MAX_DIGIS && rest[i][0] != '-' && rest[i][0] != '+'; i++) {
+ if (ax25_aton_entry(rest[i], nr_neigh.digipeaters[i].ax25_call) != 0) {
+ fprintf(stderr, "nrparms: routes: invalid callsign %s\n", rest[i]);
+ close(s);
+ exit(1);
+ }
+ nr_neigh.ndigis++;
+ }
+ */
+
+ op = rest[i + 0];
+ quality = atoi(rest[i + 1]);
+
+ if (op[0] != '+' && op[0] != '-') {
+ fprintf(stderr, "nrparms: routes: invalid operation %s\n", op);
+ close(s);
+ exit(1);
+ }
+
+ if (quality < 1 || quality > 255) {
+ fprintf(stderr, "nrparms: routes: invalid quality %d\n", quality);
+ close(s);
+ exit(1);
+ }
+
+ if ((dev = ax25_config_get_dev(port)) == NULL) {
+ fprintf(stderr, "nrparms: routes: invalid port name - %s\n", port);
+ close(s);
+ exit(1);
+ }
+
+ strcpy(nr_neigh.device, dev);
+
+ if (ax25_aton_entry(nodecall, nr_neigh.callsign.ax25_call) != 0) {
+ fprintf(stderr, "nrparms: routes: invalid callsign %s\n", nodecall);
+ close(s);
+ exit(1);
+ }
+
+ nr_neigh.quality = quality;
+
+ if (op[0] == '+') {
+ if (ioctl(s, SIOCADDRT, &nr_neigh) == -1) {
+ perror("nrparms: SIOCADDRT");
+ close(s);
+ exit(1);
+ }
+ } else {
+ if (ioctl(s, SIOCDELRT, &nr_neigh) == -1) {
+ perror("nrparms: SIOCDELRT");
+ close(s);
+ exit(1);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int s;
+
+ if (argc == 1) {
+ fprintf(stderr, "usage: nrparms -nodes|-routes|-version ...\n");
+ return 1;
+ }
+
+ if (strncmp(argv[1], "-v", 2) == 0) {
+ printf("nrparms: %s\n", VERSION);
+ return 0;
+ }
+
+ if ((s = socket(AF_NETROM, SOCK_SEQPACKET, 0)) < 0) {
+ perror("nrparms: socket");
+ return 1;
+ }
+
+ if (strncmp(argv[1], "-n", 2) == 0) {
+ if (argc < 9) {
+ fprintf(stderr, nodes_usage);
+ close(s);
+ return 1;
+ }
+ nodes(s, argv[2], argv[3], argv[4], atoi(argv[5]), atoi(argv[6]), argv[7], argv[8], argv + 9);
+ close(s);
+ return 0;
+ }
+
+ if (strncmp(argv[1], "-r", 2) == 0) {
+ if (argc < 6) {
+ fprintf(stderr, routes_usage);
+ close(s);
+ return 1;
+ }
+ routes(s, argv[2], argv[3], argv + 4);
+ close(s);
+ return 0;
+ }
+
+ fprintf(stderr, "usage: nrparms -nodes|-routes|-version ...\n");
+
+ close(s);
+
+ return 1;
+}
diff --git a/netrom/nrports b/netrom/nrports
new file mode 100644
index 0000000..4faa359
--- /dev/null
+++ b/netrom/nrports
@@ -0,0 +1,7 @@
+# /etc/ax25/nrports
+#
+# The format of this file is:
+#
+# name callsign alias paclen description
+#
+netrom OH2BNS-10 #LNODE 235 Switch Port
diff --git a/netrom/nrports.5 b/netrom/nrports.5
new file mode 100644
index 0000000..3449113
--- /dev/null
+++ b/netrom/nrports.5
@@ -0,0 +1,56 @@
+.TH NRPORTS 5 "2 August 1996" Linux "Linux Programmer's Manual"
+.SH NAME
+nrports \- NET/ROM port configuration file.
+.SH DESCRIPTION
+.LP
+.B Nrports
+is an ASCII file that contains information about each of the NET/ROM
+ports that are to be used. When dealing with an NET/ROM 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 nrports,
+which has the same name. The information on each line contains
+enough information to bind the command to a particular NET/ROM
+interface, this binding is done by matching the callsign on the line in
+.B nrports
+with the callsign of the port set by
+.B ifconfig.
+.LP
+The
+.B nrports
+file may contain comments that begin 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 alias paclen description
+.RE
+.sp
+The field descriptions are:
+.sp
+.RS
+.TP 14
+.B name
+this is the unique NET/ROM port identifier.
+.TP 14
+.B callsign
+the callsign of the NET/ROM interface to bind to.
+.TP 14
+.B alias
+this is the alias of the NET/ROM port.
+.TP 14
+.B paclen
+is the default packet size for this interface.
+.TP 14
+.B description
+a free format description of this interface, this field extends to the end
+of the line. It may contain spaces.
+.RE
+.SH FILES
+.LP
+/etc/ax25/nrports
+.SH "SEE ALSO"
+.BR call (1),
+.BR netrom (4),
+.BR ifconfig (8),
+.BR nrparms (8).
diff --git a/netrom/nrsdrv.8 b/netrom/nrsdrv.8
new file mode 100644
index 0000000..fb27562
--- /dev/null
+++ b/netrom/nrsdrv.8
@@ -0,0 +1,44 @@
+.TH NRSDRV 8 "22 December 1996" Linux "Linux System Managers Manual"
+.SH NAME
+nrsdrv \- KISS to NET/ROM serial converter
+.SH SYNOPSIS
+.B nrsdrv [-f] [-l] [-s speed] [-v] kissdev nrsdev
+.SH DESCRIPTION
+.LP
+.B Nrsdrv
+is a program designed to convert from the KISS protocol to the NET/ROM
+serial protocol used by real NET/ROM's and TheNet's. The protocols are
+fairly similar, although the NET/ROM serial protocol does include a one byte
+checksum which KISS does not.
+.sp 1
+Typically
+.B nrsdrv
+will be attached to one end of a pseudo-tty of which the other end has been
+attached to a KISS capable program. The NET/ROM device will probably be a
+real serial port attached to a TNC or a Hexipus. The full specification of
+the NET/ROM serial protocol can be found in the original Software 2000
+documentation that accompanied NET/ROMs.
+.SH OPTIONS
+.TP 10
+.BI \-f
+Flow control enabled for use with a Hexipus, the default is disabled.
+See the file hexipus.txt in the source distribution for wiring details.
+
+.TP 10
+.BI \-l
+Log messages to the system log, the default is not to.
+.TP 10
+.BI "\-s speed"
+Sets the speed of both interfaces. If no value is specified then no speed
+will be set.
+.TP 10
+.BI \-v
+Display the version.
+.SH BUGS
+None known.
+.SH "SEE ALSO"
+.BR kill (1),
+.BR stty (1),
+.BR ax25 (4).
+.SH AUTHOR
+Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>
diff --git a/netrom/nrsdrv.c b/netrom/nrsdrv.c
new file mode 100644
index 0000000..92b3d6a
--- /dev/null
+++ b/netrom/nrsdrv.c
@@ -0,0 +1,457 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <syslog.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netax25/daemon.h>
+#include <netax25/ttyutils.h>
+
+#include <config.h>
+
+#include "../pathnames.h"
+
+static int kissfd, nrsfd;
+static int logging = FALSE;
+static int debugging = FALSE;
+static int flowcontrol = FALSE;
+static char *kissdev, *nrsdev;
+
+#define NUL 000
+#define STX 002
+#define ETX 003
+#define DLE 020
+
+#define NRS_WAIT 0
+#define NRS_DATA 1
+#define NRS_ESCAPE 2
+#define NRS_CKSUM 3
+static int nrs_state = NRS_WAIT;
+
+static unsigned char nrs_cksum = 0;
+
+static unsigned char nrs_rxbuffer[512];
+static int nrs_rxcount = 0;
+
+#define FEND 0300
+#define FESC 0333
+#define FESCEND 0334
+#define FESCESC 0335
+
+#define KISS_WAIT 0
+#define KISS_CTRL 1
+#define KISS_DATA 2
+#define KISS_ESCAPE 3
+static int kiss_state = KISS_WAIT;
+
+static unsigned char kiss_rxbuffer[512];
+static int kiss_rxcount = 0;
+
+static void terminate(int sig)
+{
+ if (logging) {
+ syslog(LOG_INFO, "terminating on SIGTERM\n");
+ closelog();
+ }
+
+ tty_unlock(kissdev);
+ tty_unlock(nrsdev);
+
+ exit(0);
+}
+
+static void key_rts(int fd)
+{
+ int status;
+
+ if (!flowcontrol)
+ return;
+
+ /* Wait for CTS to be low */
+ while (1) {
+ /* Get CTS status */
+ if (ioctl(fd, TIOCMGET, &status) < 0) {
+ syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n");
+ flowcontrol = 0;
+ return;
+ }
+ if (status & TIOCM_CTS) {
+ if (debugging) {
+ fprintf(stderr,"CTS high: waiting\n");
+ }
+ ioctl(fd, TIOCMIWAIT, &status);
+ } else {
+ break;
+ }
+ }
+
+ if (debugging) {
+ fprintf(stderr,"CTS low: keying RTS\n");
+ }
+ status |= TIOCM_RTS | TIOCM_DTR;
+ if (ioctl(fd, TIOCMSET, &status) < 0) {
+ syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n");
+ flowcontrol = 0;
+ }
+}
+
+static void unkey_rts(int fd)
+{
+ int status;
+
+ if (!flowcontrol)
+ return;
+
+ if (debugging) {
+ fprintf(stderr,"Transmission finished: unkeying RTS\n");
+ }
+ ioctl(fd, TIOCMGET, &status);
+ status &= ~TIOCM_RTS;
+ status |= TIOCM_DTR;
+ if (ioctl(fd, TIOCMSET, &status) < 0) {
+ syslog(LOG_INFO|LOG_ERR, "TIOCMGET failed: flowcontrol disabled (%m)\n");
+ flowcontrol = 0;
+ }
+}
+
+static void nrs_esc(unsigned char *s, int len)
+{
+ static unsigned char buffer[512];
+ unsigned char *ptr = buffer;
+ unsigned char csum = 0;
+ unsigned char c;
+
+ *ptr++ = STX;
+
+ while (len-- > 0) {
+ switch (c = *s++) {
+ case STX:
+ case ETX:
+ case DLE:
+ *ptr++ = DLE;
+ /* Fall through */
+ default:
+ *ptr++ = c;
+ break;
+ }
+
+ csum += c;
+ }
+
+ *ptr++ = ETX;
+ *ptr++ = csum;
+ *ptr++ = NUL;
+ *ptr++ = NUL;
+
+ key_rts(nrsfd);
+ write(nrsfd, buffer, ptr - buffer);
+ unkey_rts(nrsfd);
+}
+
+static void kiss_esc(unsigned char *s, int len)
+{
+ static unsigned char buffer[512];
+ unsigned char *ptr = buffer;
+ unsigned char c;
+
+ *ptr++ = FEND;
+ *ptr++ = 0x00; /* KISS DATA */
+
+ while (len-- > 0) {
+ switch (c = *s++) {
+ case FESC:
+ *ptr++ = FESC;
+ *ptr++ = FESCESC;
+ break;
+ case FEND:
+ *ptr++ = FESC;
+ *ptr++ = FESCEND;
+ break;
+ default:
+ *ptr++ = c;
+ break;
+ }
+ }
+
+ *ptr++ = FEND;
+
+ write(kissfd, buffer, ptr - buffer);
+}
+
+static void nrs_unesc(unsigned char *buffer, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ switch (nrs_state) {
+ case NRS_WAIT:
+ if (buffer[i] == STX) {
+ nrs_state = NRS_DATA;
+ nrs_rxcount = 0;
+ nrs_cksum = 0;
+ }
+ break;
+
+ case NRS_DATA:
+ switch (buffer[i]) {
+ case STX: /* !! */
+ nrs_rxcount = 0;
+ nrs_cksum = 0;
+ break;
+ case DLE:
+ nrs_state = NRS_ESCAPE;
+ break;
+ case ETX:
+ nrs_state = NRS_CKSUM;
+ break;
+ default:
+ if (nrs_rxcount < 512) {
+ nrs_cksum += buffer[i];
+ nrs_rxbuffer[nrs_rxcount++] = buffer[i];
+ }
+ break;
+ }
+ break;
+
+ case NRS_ESCAPE:
+ nrs_state = NRS_DATA;
+ if (nrs_rxcount < 512) {
+ nrs_cksum += buffer[i];
+ nrs_rxbuffer[nrs_rxcount++] = buffer[i];
+ }
+ break;
+
+ case NRS_CKSUM:
+ if (buffer[i] == nrs_cksum)
+ kiss_esc(nrs_rxbuffer, nrs_rxcount);
+ nrs_state = NRS_WAIT;
+ nrs_cksum = 0;
+ nrs_rxcount = 0;
+ break;
+ }
+ }
+}
+
+static void kiss_unesc(unsigned char *buffer, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ switch (kiss_state) {
+ case KISS_WAIT:
+ if (buffer[i] == FEND) {
+ kiss_state = KISS_CTRL;
+ kiss_rxcount = 0;
+ }
+ break;
+
+ case KISS_CTRL:
+ if ((buffer[i] & 0x0F) == 0x00) {
+ kiss_state = KISS_DATA;
+ kiss_rxcount = 0;
+ } else {
+ kiss_state = KISS_WAIT;
+ kiss_rxcount = 0;
+ }
+ break;
+
+ case KISS_DATA:
+ switch (buffer[i]) {
+ case FEND:
+ if (kiss_rxcount > 2)
+ nrs_esc(kiss_rxbuffer, kiss_rxcount);
+ kiss_state = KISS_WAIT;
+ kiss_rxcount = 0;
+ break;
+ case FESC:
+ kiss_state = KISS_ESCAPE;
+ break;
+ default:
+ if (kiss_rxcount < 512)
+ kiss_rxbuffer[kiss_rxcount++] = buffer[i];
+ break;
+ }
+ break;
+
+ case KISS_ESCAPE:
+ kiss_state = KISS_DATA;
+ switch (buffer[i]) {
+ case FESCESC:
+ if (kiss_rxcount < 512)
+ kiss_rxbuffer[kiss_rxcount++] = FESC;
+ break;
+ case FESCEND:
+ if (kiss_rxcount < 512)
+ kiss_rxbuffer[kiss_rxcount++] = FEND;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ static char buffer[512];
+ unsigned int speed = 0;
+ fd_set read_fd;
+ int c, n;
+
+ while ((c = getopt(argc, argv, "dfls:v")) != -1) {
+ switch (c) {
+ case 'd':
+ debugging = TRUE;
+ break;
+ case 'f':
+ flowcontrol = TRUE;
+ break;
+ case 'l':
+ logging = TRUE;
+ break;
+ case 's':
+ if ((speed = atoi(optarg)) <= 0) {
+ fprintf(stderr, "nrsdrv: invalid speed %s\n", optarg);
+ return 1;
+ }
+ break;
+ case 'v':
+ printf("kissattach: %s\n", VERSION);
+ return 0;
+ case ':':
+ case '?':
+ fprintf(stderr, "usage: nrsdrv [-f] [-l] [-s speed] [-v] kisstty nrstty\n");
+ return 1;
+ }
+ }
+
+ if (debugging) {
+ fprintf(stderr,"Flow control %s\n",
+ flowcontrol ? "enabled" : "disabled");
+ }
+
+ if ((argc - optind) != 2) {
+ fprintf(stderr, "usage: nrsdrv [-f] [-l] [-s speed] [-v] kisstty nrstty\n");
+ return 1;
+ }
+
+ kissdev = argv[optind + 0];
+ nrsdev = argv[optind + 1];
+
+ if (tty_is_locked(kissdev)) {
+ fprintf(stderr, "nrsdrv: device %s already in use\n", argv[optind]);
+ return 1;
+ }
+
+ if (tty_is_locked(nrsdev)) {
+ fprintf(stderr, "nrsdrv: device %s already in use\n", argv[optind + 1]);
+ return 1;
+ }
+
+ if ((kissfd = open(kissdev, O_RDWR)) == -1) {
+ perror("nrsdrv: open kiss device");
+ return 1;
+ }
+
+ if ((nrsfd = open(nrsdev, O_RDWR)) == -1) {
+ perror("nrsdrv: open nrs device");
+ return 1;
+ }
+
+ tty_lock(kissdev);
+ tty_lock(nrsdev);
+
+ if (!tty_raw(kissfd, FALSE)) {
+ tty_unlock(kissdev);
+ tty_unlock(nrsdev);
+ return 1;
+ }
+
+ if (!tty_raw(nrsfd, FALSE)) {
+ tty_unlock(kissdev);
+ tty_unlock(nrsdev);
+ return 1;
+ }
+
+ if (speed != 0 && !tty_speed(kissfd, speed)) {
+ tty_unlock(kissdev);
+ tty_unlock(nrsdev);
+ return 1;
+ }
+
+ if (speed != 0 && !tty_speed(nrsfd, speed)) {
+ tty_unlock(kissdev);
+ tty_unlock(nrsdev);
+ return 1;
+ }
+
+ if (logging) {
+ openlog("nrsdrv", LOG_PID, LOG_DAEMON);
+ syslog(LOG_INFO, "KISS device %s connected to NRS device %s\n", argv[optind + 0], argv[optind + 1]);
+ }
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGTERM, terminate);
+
+ /*
+ * Become a daemon if we can.
+ */
+ if (!daemon_start(FALSE)) {
+ fprintf(stderr, "nrsdrv: cannot become a daemon\n");
+ tty_unlock(kissdev);
+ tty_unlock(nrsdev);
+ return 1;
+ }
+
+ if (flowcontrol) {
+ unkey_rts(nrsfd);
+ }
+
+ c = ((kissfd > nrsfd) ? kissfd : nrsfd) + 1;
+
+ for (;;) {
+ FD_ZERO(&read_fd);
+
+ FD_SET(kissfd, &read_fd);
+ FD_SET(nrsfd, &read_fd);
+
+ n = select(c, &read_fd, NULL, NULL, NULL);
+
+ if (FD_ISSET(kissfd, &read_fd)) {
+ if ((n = read(kissfd, buffer, 512)) <= 0) {
+ if (logging) {
+ syslog(LOG_INFO, "terminating on KISS device closure\n");
+ closelog();
+ }
+ break;
+ }
+ kiss_unesc(buffer, n);
+ }
+
+ if (FD_ISSET(nrsfd, &read_fd)) {
+ if ((n = read(nrsfd, buffer, 512)) <= 0) {
+ if (logging) {
+ syslog(LOG_INFO, "terminating on NRS device closure\n");
+ closelog();
+ }
+ break;
+ }
+ nrs_unesc(buffer, n);
+ }
+ }
+
+ tty_unlock(kissdev);
+ tty_unlock(nrsdev);
+
+ return 0;
+}