summaryrefslogtreecommitdiffstats
path: root/net/ax25
diff options
context:
space:
mode:
Diffstat (limited to 'net/ax25')
-rw-r--r--net/ax25/Config.in37
-rw-r--r--net/ax25/Makefile15
-rw-r--r--net/ax25/af_ax25.c1740
-rw-r--r--net/ax25/af_ax25.h21
-rw-r--r--net/ax25/ax25_addr.c303
-rw-r--r--net/ax25/ax25_core.c513
-rw-r--r--net/ax25/ax25_core.h21
-rw-r--r--net/ax25/ax25_ctl.c133
-rw-r--r--net/ax25/ax25_ctl.h6
-rw-r--r--net/ax25/ax25_ddi.c1186
-rw-r--r--net/ax25/ax25_ddi.h39
-rw-r--r--net/ax25/ax25_dev.c213
-rw-r--r--net/ax25/ax25_ds_in.c312
-rw-r--r--net/ax25/ax25_ds_subr.c217
-rw-r--r--net/ax25/ax25_ds_timer.c224
-rw-r--r--net/ax25/ax25_iface.c268
-rw-r--r--net/ax25/ax25_in.c984
-rw-r--r--net/ax25/ax25_in.h20
-rw-r--r--net/ax25/ax25_ip.c211
-rw-r--r--net/ax25/ax25_ipax.c708
-rw-r--r--net/ax25/ax25_lapb.c789
-rw-r--r--net/ax25/ax25_lapb.h11
-rw-r--r--net/ax25/ax25_netlink.c110
-rw-r--r--net/ax25/ax25_netlink.h10
-rw-r--r--net/ax25/ax25_out.c356
-rw-r--r--net/ax25/ax25_route.c594
-rw-r--r--net/ax25/ax25_route.h25
-rw-r--r--net/ax25/ax25_std_in.c467
-rw-r--r--net/ax25/ax25_std_subr.c102
-rw-r--r--net/ax25/ax25_std_timer.c170
-rw-r--r--net/ax25/ax25_subr.c388
-rw-r--r--net/ax25/ax25_subr.h38
-rw-r--r--net/ax25/ax25_timer.c569
-rw-r--r--net/ax25/ax25_timer.h11
-rw-r--r--net/ax25/ax25_uid.c197
-rw-r--r--net/ax25/ax25_vj.c724
-rw-r--r--net/ax25/ax25_vj.h73
-rw-r--r--net/ax25/sysctl_net_ax25.c188
38 files changed, 6859 insertions, 5134 deletions
diff --git a/net/ax25/Config.in b/net/ax25/Config.in
index b8e5d7333..bba299843 100644
--- a/net/ax25/Config.in
+++ b/net/ax25/Config.in
@@ -3,7 +3,7 @@
#
# 19971130 Now in an own category to make correct compilation of the
# AX.25 stuff easier...
-# Joerg Reuter DL1BKE <jreuter@yaina.de>
+# Joerg Reuter DL1BKE <jreuter@poboxes.com>
# 19980129 Moved to net/ax25/Config.in, sourcing device drivers.
mainmenu_option next_comment
@@ -11,26 +11,23 @@ comment 'Amateur Radio support'
bool 'Amateur Radio support' CONFIG_HAMRADIO
if [ "$CONFIG_HAMRADIO" != "n" ]; then
- if [ "$CONFIG_NET" != "n" ]; then
- comment 'Packet Radio protocols'
- tristate ' Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25
- if [ "$CONFIG_AX25" != "n" ]; then
- bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
-# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
- dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25
- dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
- fi
-
- if [ "$CONFIG_AX25" != "n" ]; then
- mainmenu_option next_comment
- comment 'AX.25 network device drivers'
-
- source drivers/net/hamradio/Config.in
-
- endmenu
- fi
- fi
+ if [ "$CONFIG_NET" != "n" ]; then
+ comment 'Packet Radio protocols'
+ tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 $CONFIG_NETLINK
+ if [ "$CONFIG_AX25" != "n" ]; then
+# bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
+ tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25
+ tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
+ fi
+ if [ "$CONFIG_AX25" != "n" ]; then
+ mainmenu_option next_comment
+ comment 'AX.25 network device drivers'
+ source drivers/net/hamradio/Config.in
+ endmenu
+ fi
+ fi
fi
endmenu
diff --git a/net/ax25/Makefile b/net/ax25/Makefile
index 5974031f4..c35f59105 100644
--- a/net/ax25/Makefile
+++ b/net/ax25/Makefile
@@ -6,20 +6,19 @@
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now in the main makefile...
-
+#
O_TARGET := ax25.o
export-objs := af_ax25.o
-obj-y := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \
- ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \
- ax25_subr.o ax25_timer.o ax25_uid.o af_ax25.o
-
-obj-m := $(O_TARGET)
+obj-y := af_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o \
+ ax25_vj.o ax25_ddi.o ax25_uid.o ax25_ctl.o ax25_core.o \
+ ax25_ipax.o ax25_lapb.o ax25_netlink.o sysctl_net_ax25.o
-obj-$(CONFIG_AX25_DAMA_SLAVE) += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o
-obj-$(CONFIG_SYSCTL) += sysctl_net_ax25.o
+obj-m := $(O_TARGET)
include $(TOPDIR)/Rules.make
+tar:
+ tar -cvf /dev/f1 .
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 4a2684ebe..6fa817d65 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -1,107 +1,22 @@
/*
- * AX.25 release 038
+ * af_ax25.c: Network subsystem interface and NEW-AX.25 main functions
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ * Jonathan (G4KLX), Alan Cox (GW4PTS)
*
- * This module:
- * This module 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.
+ * Comment: SOCK_DGRAM support is missing and should be implemented ASAP.
+ * There is currently no clean way for unproto operation. Most
+ * application use AF_PACKET, SOCK_RAW which is seriously broken
+ * because it skips the DDI arbiter etc. etc.
*
- * History
- * AX.25 006 Alan(GW4PTS) Nearly died of shock - it's working 8-)
- * AX.25 007 Alan(GW4PTS) Removed the silliest bugs
- * AX.25 008 Alan(GW4PTS) Cleaned up, fixed a few state machine problems, added callbacks
- * AX.25 009 Alan(GW4PTS) Emergency patch kit to fix memory corruption
- * AX.25 010 Alan(GW4PTS) Added RAW sockets/Digipeat.
- * AX.25 011 Alan(GW4PTS) RAW socket and datagram fixes (thanks) - Raw sendto now gets PID right
- * datagram sendto uses correct target address.
- * AX.25 012 Alan(GW4PTS) Correct incoming connection handling, send DM to failed connects.
- * Use skb->data not skb+1. Support sk->priority correctly.
- * Correct receive on SOCK_DGRAM.
- * AX.25 013 Alan(GW4PTS) Send DM to all unknown frames, missing initialiser fixed
- * Leave spare SSID bits set (DAMA etc) - thanks for bug report,
- * removed device registration (it's not used or needed). Clean up for
- * gcc 2.5.8. PID to AX25_P_
- * AX.25 014 Alan(GW4PTS) Cleanup and NET3 merge
- * AX.25 015 Alan(GW4PTS) Internal test version.
- * AX.25 016 Alan(GW4PTS) Semi Internal version for PI card
- * work.
- * AX.25 017 Alan(GW4PTS) Fixed some small bugs reported by
- * G4KLX
- * AX.25 018 Alan(GW4PTS) Fixed a small error in SOCK_DGRAM
- * AX.25 019 Alan(GW4PTS) Clean ups for the non INET kernel and device ioctls in AX.25
- * AX.25 020 Jonathan(G4KLX) /proc support and other changes.
- * AX.25 021 Alan(GW4PTS) Added AX25_T1, AX25_N2, AX25_T3 as requested.
- * AX.25 022 Jonathan(G4KLX) More work on the ax25 auto router and /proc improved (again)!
- * Alan(GW4PTS) Added TIOCINQ/OUTQ
- * AX.25 023 Alan(GW4PTS) Fixed shutdown bug
- * AX.25 023 Alan(GW4PTS) Linus changed timers
- * AX.25 024 Alan(GW4PTS) Small bug fixes
- * AX.25 025 Alan(GW4PTS) More fixes, Linux 1.1.51 compatibility stuff, timers again!
- * AX.25 026 Alan(GW4PTS) Small state fix.
- * AX.25 027 Alan(GW4PTS) Socket close crash fixes.
- * AX.25 028 Alan(GW4PTS) Callsign control including settings per uid.
- * Small bug fixes.
- * Protocol set by sockets only.
- * Small changes to allow for start of NET/ROM layer.
- * AX.25 028a Jonathan(G4KLX) Changes to state machine.
- * AX.25 028b Jonathan(G4KLX) Extracted ax25 control block
- * from sock structure.
- * AX.25 029 Alan(GW4PTS) Combined 028b and some KA9Q code
- * Jonathan(G4KLX) and removed all the old Berkeley, added IP mode registration.
- * Darryl(G7LED) stuff. Cross-port digipeating. Minor fixes and enhancements.
- * Alan(GW4PTS) Missed suser() on axassociate checks
- * AX.25 030 Alan(GW4PTS) Added variable length headers.
- * Jonathan(G4KLX) Added BPQ Ethernet interface.
- * Steven(GW7RRM) Added digi-peating control ioctl.
- * Added extended AX.25 support.
- * Added AX.25 frame segmentation.
- * Darryl(G7LED) Changed connect(), recvfrom(), sendto() sockaddr/addrlen to
- * fall inline with bind() and new policy.
- * Moved digipeating ctl to new ax25_dev structs.
- * Fixed ax25_release(), set TCP_CLOSE, wakeup app
- * context, THEN make the sock dead.
- * Alan(GW4PTS) Cleaned up for single recvmsg methods.
- * Alan(GW4PTS) Fixed not clearing error on connect failure.
- * AX.25 031 Jonathan(G4KLX) Added binding to any device.
- * Joerg(DL1BKE) Added DAMA support, fixed (?) digipeating, fixed buffer locking
- * for "virtual connect" mode... Result: Probably the
- * "Most Buggiest Code You've Ever Seen" (TM)
- * HaJo(DD8NE) Implementation of a T5 (idle) timer
- * Joerg(DL1BKE) Renamed T5 to IDLE and changed behaviour:
- * the timer gets reloaded on every received or transmitted
- * I frame for IP or NETROM. The idle timer is not active
- * on "vanilla AX.25" connections. Furthermore added PACLEN
- * to provide AX.25-layer based fragmentation (like WAMPES)
- * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout error.
- * ax25_send_frame() limits the number of enqueued
- * datagrams per socket.
- * AX.25 033 Jonathan(G4KLX) Removed auto-router.
- * Hans(PE1AYX) Converted to Module.
- * Joerg(DL1BKE) Moved BPQ Ethernet to separate driver.
- * AX.25 034 Jonathan(G4KLX) 2.1 changes
- * Alan(GW4PTS) Small POSIXisations
- * AX.25 035 Alan(GW4PTS) Started fixing to the new
- * format.
- * Hans(PE1AYX) Fixed interface to IP layer.
- * Alan(GW4PTS) Added asynchronous support.
- * Frederic(F1OAT) Support for pseudo-digipeating.
- * Jonathan(G4KLX) Support for packet forwarding.
- * AX.25 036 Jonathan(G4KLX) Major restructuring.
- * Joerg(DL1BKE) Fixed DAMA Slave.
- * Jonathan(G4KLX) Fix wildcard listen parameter setting.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * AX.25 038 Matthias(DG2FEF) Small fixes to the syscall interface to make kernel
- * independent of AX25_MAX_DIGIS used by applications.
- * Tomi(OH2BNS) Fixed ax25_getname().
- * Joerg(DL1BKE) Starting to phase out the support for full_sockaddr_ax25
- * with only 6 digipeaters and sockaddr_ax25 in ax25_bind(),
- * ax25_connect() and ax25_sendmsg()
- * Joerg(DL1BKE) Added support for SO_BINDTODEVICE
- * Arnaldo C. Melo s/suser/capable(CAP_NET_ADMIN)/, some more cleanups
- * Michal Ostrowski Module initialization cleanup.
+ * Changelog:
+ * 2001-02-06 Joerg Reuter DL1BKE <jreuter@yaina.de>
+ * port to kernel 2.4.1
+ *
+ * License: This module 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.
*/
#include <linux/config.h>
@@ -111,518 +26,327 @@
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
-#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/if.h>
#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
+#include <linux/spinlock.h>
#include <linux/fcntl.h>
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
-#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
-#include <linux/netfilter.h>
-#include <linux/sysctl.h>
#include <linux/init.h>
-#include <net/ip.h>
-#include <net/arp.h>
-
-
-
-ax25_cb *volatile ax25_list;
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
-static struct proto_ops ax25_proto_ops;
+#include <net/sock.h>
+#include <net/ax25.h>
+#include <net/ax25_uid.h>
+#include <net/ax25dev.h>
+
+#include "af_ax25.h"
+#include "ax25_vj.h"
+#include "ax25_ddi.h"
+#include "ax25_route.h"
+#include "ax25_core.h"
+#include "ax25_ctl.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_netlink.h"
+#include "ax25_timer.h"
/*
- * Free an allocated ax25 control block. This is done to centralise
- * the MOD count code.
+ * ------------------------------------------------------------------------
+ * declaration of static functions
+ * ------------------------------------------------------------------------
*/
-void ax25_free_cb(ax25_cb *ax25)
-{
- if (ax25->digipeat != NULL) {
- kfree(ax25->digipeat);
- ax25->digipeat = NULL;
- }
-
- kfree(ax25);
-
- MOD_DEC_USE_COUNT;
-}
-
-static void ax25_free_sock(struct sock *sk)
-{
- ax25_free_cb(sk->protinfo.ax25);
-}
+static int ax25_device_event(struct notifier_block* ,unsigned long, void*);
+static int ax25_setsockopt(struct socket*, int, int, char*, int);
+static int ax25_getsockopt(struct socket*, int, int, char*, int*);
+static int ax25_listen(struct socket*, int);
+static int ax25_shutdown(struct socket*, int);
+static int ax25_create(struct socket*, int);
+static int ax25_release(struct socket *);
+static int ax25_bind(struct socket*, struct sockaddr*, int);
+static int ax25_connect(struct socket*, struct sockaddr*, int, int);
+static int ax25_accept(struct socket*, struct socket*, int);
+static int ax25_getname(struct socket*, struct sockaddr*, int*, int);
+static int ax25_sendmsg(struct socket*, struct msghdr*, int, struct scm_cookie*);
+static int ax25_recvmsg(struct socket*, struct msghdr*, int, int, struct scm_cookie*);
+static int ax25_ioctl(struct socket*, unsigned int, unsigned long);
+static int ax25_print_list(char*, off_t*, off_t, int, off_t*, ax25_cb*, char*);
+static int ax25_get_info(char*, char **, off_t, int);
+static int ax25_gifconf(struct net_device *dev, char *buf, int len);
+
+/* in ax25_ipax.c */
+int ipax_init(void);
+int ipax_cleanup(void);
/*
- * Socket removal during an interrupt is now safe.
+ * ------------------------------------------------------------------------
+ * static variables and structures
+ * ------------------------------------------------------------------------
*/
-static void ax25_remove_socket(ax25_cb *ax25)
-{
- ax25_cb *s;
- unsigned long flags;
-
- save_flags(flags); cli();
-
- if ((s = ax25_list) == ax25) {
- ax25_list = s->next;
- restore_flags(flags);
- return;
- }
-
- while (s != NULL && s->next != NULL) {
- if (s->next == ax25) {
- s->next = ax25->next;
- restore_flags(flags);
- return;
- }
-
- s = s->next;
- }
-
- restore_flags(flags);
-}
/*
- * Kill all bound sockets on a dropped device.
+ * table of exportes symbols
*/
-static void ax25_kill_by_device(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
- ax25_cb *s;
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return;
+EXPORT_SYMBOL(ax25_find_match_for_uid);
+EXPORT_SYMBOL(ax25_find_cb);
+EXPORT_SYMBOL(ax25_linkfail_register);
+EXPORT_SYMBOL(ax25_linkfail_release);
+EXPORT_SYMBOL(ax25_listen_register);
+EXPORT_SYMBOL(ax25_listen_release);
+EXPORT_SYMBOL(ax25_protocol_register);
+EXPORT_SYMBOL(ax25_protocol_release);
+EXPORT_SYMBOL(ax25_send_frame);
+EXPORT_SYMBOL(ax25_uid_policy);
+EXPORT_SYMBOL(ax25cmp);
+EXPORT_SYMBOL(ax2asc);
+EXPORT_SYMBOL(asc2ax);
+EXPORT_SYMBOL(null_ax25_address);
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->ax25_dev == ax25_dev) {
- s->ax25_dev = NULL;
- ax25_disconnect(s, ENETUNREACH);
- }
- }
-}
+/* for debugging */
+EXPORT_SYMBOL(ax25_kill_by_device);
/*
- * Handle device status changes.
+ * This list contains all sockets that are not bound to
+ * a specific device.
*/
-static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr)
-{
- struct net_device *dev = (struct net_device *)ptr;
-
- /* Reject non AX.25 devices */
- if (dev->type != ARPHRD_AX25)
- return NOTIFY_DONE;
-
- switch (event) {
- case NETDEV_UP:
- ax25_dev_device_up(dev);
- break;
- case NETDEV_DOWN:
- ax25_kill_by_device(dev);
- ax25_rt_device_down(dev);
- ax25_dev_device_down(dev);
- break;
- default:
- break;
- }
-
- return NOTIFY_DONE;
-}
+ax25_cb *ax25_list = NULL;
/*
- * Add a socket to the bound sockets list.
+ * Protocol family registration data
*/
-void ax25_insert_socket(ax25_cb *ax25)
+static struct net_proto_family ax25_family_ops =
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- ax25->next = ax25_list;
- ax25_list = ax25;
-
- restore_flags(flags);
-}
+ family: PF_AX25,
+ create: ax25_create,
+};
/*
- * Find a socket that wants to accept the SABM we have just
- * received.
+ * Protocol operations for AF_AX25
*/
-struct sock *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev, int type)
-{
- unsigned long flags;
- ax25_cb *s;
-
- save_flags(flags);
- cli();
+static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = {
+ family: PF_AX25,
- for (s = ax25_list; s != NULL; s = s->next) {
- if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
- continue;
- if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) {
- /* If device is null we match any device */
- if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
- restore_flags(flags);
- return s->sk;
- }
- }
- }
+ release: ax25_release,
+ bind: ax25_bind,
+ connect: ax25_connect,
+ socketpair: sock_no_socketpair,
+ accept: ax25_accept,
+ getname: ax25_getname,
+ poll: datagram_poll,
+ ioctl: ax25_ioctl,
+ listen: ax25_listen,
+ shutdown: ax25_shutdown,
+ setsockopt: ax25_setsockopt,
+ getsockopt: ax25_getsockopt,
+ sendmsg: ax25_sendmsg,
+ recvmsg: ax25_recvmsg,
+ mmap: sock_no_mmap
+};
- restore_flags(flags);
- return NULL;
-}
+SOCKOPS_WRAP(ax25_proto, PF_AX25);
/*
- * Find an AX.25 socket given both ends.
+ * Device up/down notifier block
*/
-struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type)
-{
- ax25_cb *s;
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->sk != NULL && ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->sk->type == type) {
- restore_flags(flags);
- return s->sk;
- }
- }
-
- restore_flags(flags);
+static struct notifier_block ax25_dev_notifier = {
+ ax25_device_event,
+ 0
+};
- return NULL;
-}
+/*
+ * ------------------------------------------------------------------------
+ * Interface implementation
+ * All public functions of this module are defined here
+ * ------------------------------------------------------------------------
+ */
/*
- * Find an AX.25 control block given both ends. It will only pick up
- * floating AX.25 control blocks or non Raw socket bound control blocks.
+ * ------------------------------------------------------------------------
+ * Init functions. called by the kernel on startup
+ * ------------------------------------------------------------------------
*/
-ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct net_device *dev)
+void __init ax25_proto_init(struct net_proto *pro)
{
- ax25_cb *s;
- unsigned long flags;
+ struct net_device *dev;
- save_flags(flags);
- cli();
+ sock_register(&ax25_family_ops);
+ ax25_packet_type.type = htons(ETH_P_AX25);
+ dev_add_pack(&ax25_packet_type);
+ register_gifconf(PF_AX25, ax25_gifconf);
+ register_netdevice_notifier(&ax25_dev_notifier);
+ ax25_register_sysctl();
+ ax25_ddi_init();
+ ax25_netlink_init();
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
- continue;
- if (s->ax25_dev == NULL)
- continue;
- if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
- if (digi != NULL && digi->ndigi != 0) {
- if (s->digipeat == NULL)
- continue;
- if (ax25digicmp(s->digipeat, digi) != 0)
- continue;
- } else {
- if (s->digipeat != NULL && s->digipeat->ndigi != 0)
- continue;
- }
- restore_flags(flags);
- return s;
+ proc_net_create("ax25_route", 0, ax25_rt_get_info);
+ proc_net_create("ax25", 0, ax25_get_info);
+ proc_net_create("ax25_calls", 0, ax25_cs_get_info);
+ proc_net_create("ax25_ports", 0, ax25_dev_get_info);
+ printk(KERN_INFO "NET4: AX.25 for Linux 2.4-NET4 by DG2FEF\n");
+
+#ifdef CONFIG_INET
+ ipax_init();
+#endif
+ for (dev=dev_base; dev!=NULL; dev=dev->next) {
+ if (dev->type == ARPHRD_AX25 && AX25_PTR(dev)) {
+ register_ax25device(dev);
+ if (netif_running(dev)) ax25_dev_device_up(dev);
}
}
-
- restore_flags(flags);
-
- return NULL;
}
-/*
- * Look for any matching address - RAW sockets can bind to arbitrary names
- */
-struct sock *ax25_addr_match(ax25_address *addr)
+void __exit ax25_proto_remove(void)
{
- unsigned long flags;
- ax25_cb *s;
+ int i;
+ struct net_device *dev;
- save_flags(flags);
- cli();
+ proc_net_remove("ax25_route");
+ proc_net_remove("ax25");
+ proc_net_remove("ax25_calls");
+ proc_net_remove("ax25_ports");
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == SOCK_RAW) {
- restore_flags(flags);
- return s->sk;
+ for (i=0; i<AX25_MAX_DEVICES; i++) {
+ if ((dev = ax25_devices[i])) {
+ if (netif_running(dev)) ax25_dev_device_down(dev);
+ unregister_ax25device(dev);
}
}
- restore_flags(flags);
-
- return NULL;
-}
-
-void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
-{
- struct sk_buff *copy;
-
- while (sk != NULL) {
- if (sk->type == SOCK_RAW &&
- sk->protocol == proto &&
- atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) {
- if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
- return;
+ ax25_rt_free();
+#ifdef CONFIG_INET
+ ipax_cleanup();
+#endif
+ ax25_netlink_cleanup();
+ ax25_unregister_sysctl();
- if (sock_queue_rcv_skb(sk, copy) != 0)
- kfree_skb(copy);
- }
+ unregister_netdevice_notifier(&ax25_dev_notifier);
+ register_gifconf(PF_AX25, NULL);
+ dev_remove_pack(&ax25_packet_type);
+ sock_unregister(AF_AX25);
- sk = sk->next;
- }
}
/*
- * Deferred destroy.
+ * ------------------------------------------------------------------------
+ * module registration/unregistration
+ * ------------------------------------------------------------------------
*/
-void ax25_destroy_socket(ax25_cb *);
-/*
- * Handler for deferred kills.
- */
-static void ax25_destroy_timer(unsigned long data)
-{
- ax25_destroy_socket((ax25_cb *)data);
-}
-/*
- * This is called from user mode and the timers. Thus it protects itself against
- * interrupt users but doesn't worry about being called during work.
- * Once it is removed from the queue no interrupt or bottom half will
- * touch it and we are (fairly 8-) ) safe.
- */
-void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer */
+int __init ax25_init_module(void)
{
- struct sk_buff *skb;
- unsigned long flags;
-
- save_flags(flags); cli();
+ ax25_proto_init(NULL);
+ return 0;
+}
- ax25_stop_heartbeat(ax25);
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_stop_t3timer(ax25);
- ax25_stop_idletimer(ax25);
+MODULE_AUTHOR("Matthias Welwarsky, dg2fef@afthd.tu-darmstadt.de, dg2fef@db0ais.ampr.org");
+MODULE_DESCRIPTION("Packet Radio AX.25 Protocol stack");
+module_init(ax25_init_module);
+module_exit(ax25_proto_remove);
- ax25_remove_socket(ax25);
- ax25_clear_queues(ax25); /* Flush the queues */
- if (ax25->sk != NULL) {
- while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) {
- if (skb->sk != ax25->sk) { /* A pending connection */
- skb->sk->dead = 1; /* Queue the unaccepted socket for death */
- ax25_start_heartbeat(skb->sk->protinfo.ax25);
- skb->sk->protinfo.ax25->state = AX25_STATE_0;
- }
+/* ---------------------------------------------------------------------*/
+/*
+ * Find the AX.25 device that matches the hardware address supplied.
+ */
- kfree_skb(skb);
- }
- }
+struct net_device *ax25rtr_get_dev(ax25_address *addr)
+{
+ struct net_device *dev = NULL;
+ int i;
- if (ax25->sk != NULL) {
- if (atomic_read(&ax25->sk->wmem_alloc) != 0 ||
- atomic_read(&ax25->sk->rmem_alloc) != 0) {
- /* Defer: outstanding buffers */
- init_timer(&ax25->timer);
- ax25->timer.expires = jiffies + 10 * HZ;
- ax25->timer.function = ax25_destroy_timer;
- ax25->timer.data = (unsigned long)ax25;
- add_timer(&ax25->timer);
- } else {
- sk_free(ax25->sk);
- }
- } else {
- ax25_free_cb(ax25);
+ read_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ dev = ax25_devices[i];
+ if (dev != NULL && !ax25cmp(addr, (ax25_address *)dev->dev_addr))
+ break;
}
+ read_unlock(&ax25_dev_lock);
- restore_flags(flags);
+ return dev;
}
+/* ---------------------------------------------------------------------*/
/*
- * dl1bke 960311: set parameters for existing AX.25 connections,
- * includes a KILL command to abort any connection.
- * VERY useful for debugging ;-)
+ * Kill all bound sockets on a dropped device.
*/
-static int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
+void ax25_kill_by_device(struct net_device *dev)
{
- struct ax25_ctl_struct ax25_ctl;
- ax25_digi digi;
- ax25_dev *ax25_dev;
- ax25_cb *ax25;
- unsigned int k;
-
- if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))
- return -EFAULT;
-
- if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL)
- return -ENODEV;
-
- if (ax25_ctl.digi_count > AX25_MAX_DIGIS)
- return -EINVAL;
-
- digi.ndigi = ax25_ctl.digi_count;
- for (k = 0; k < digi.ndigi; k++)
- digi.calls[k] = ax25_ctl.digi_addr[k];
-
- if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev)) == NULL)
- return -ENOTCONN;
-
- switch (ax25_ctl.cmd) {
- case AX25_KILL:
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-#ifdef CONFIG_AX25_DAMA_SLAVE
- if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
- ax25_dama_off(ax25);
-#endif
- ax25_disconnect(ax25, ENETRESET);
- break;
+ ax25_cb *s;
+ unsigned long flags;
- case AX25_WINDOW:
- if (ax25->modulus == AX25_MODULUS) {
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
- return -EINVAL;
- } else {
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
- return -EINVAL;
- }
- ax25->window = ax25_ctl.arg;
- break;
-
- case AX25_T1:
- if (ax25_ctl.arg < 1)
- return -EINVAL;
- ax25->rtt = (ax25_ctl.arg * HZ) / 2;
- ax25->t1 = ax25_ctl.arg * HZ;
- break;
-
- case AX25_T2:
- if (ax25_ctl.arg < 1)
- return -EINVAL;
- ax25->t2 = ax25_ctl.arg * HZ;
- break;
-
- case AX25_N2:
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
- return -EINVAL;
- ax25->n2count = 0;
- ax25->n2 = ax25_ctl.arg;
- break;
-
- case AX25_T3:
- if (ax25_ctl.arg < 0)
- return -EINVAL;
- ax25->t3 = ax25_ctl.arg * HZ;
- break;
-
- case AX25_IDLE:
- if (ax25_ctl.arg < 0)
- return -EINVAL;
- ax25->idle = ax25_ctl.arg * 60 * HZ;
- break;
-
- case AX25_PACLEN:
- if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
- return -EINVAL;
- ax25->paclen = ax25_ctl.arg;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
+ printk(KERN_WARNING "ax25_kill_by_device(%s)\n", dev->name);
+ save_flags(flags);
+ cli();
+ for (s = ax25_dev_list(dev); s != NULL; s = ax25_dev_list(dev)) {
+ /*
+ * list structure is being modified by ax25_remove_cb,
+ * so we can not walk along ax25->next path
+ */
+ if (s->peer && s->peer->device != s->device)
+ ax25_destroy_cb(s->peer);
+ if (s->sk) {
+ ax25_remove_cb(s);
+ ax25_disconnect(s, ENETUNREACH);
+ ax25_close_socket(s->sk, ENETUNREACH);
+ } else
+ ax25_destroy_cb(s);
+ }
+ restore_flags(flags);
}
/*
- * Fill in a created AX.25 created control block with the default
- * values for a particular device.
+ * ------------------------------------------------------------------------
+ * End of public area, all private functions of this module are defined
+ * here.
+ * ------------------------------------------------------------------------
*/
-void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev)
-{
- ax25->ax25_dev = ax25_dev;
-
- if (ax25->ax25_dev != NULL) {
- ax25->rtt = ax25_dev->values[AX25_VALUES_T1] / 2;
- ax25->t1 = ax25_dev->values[AX25_VALUES_T1];
- ax25->t2 = ax25_dev->values[AX25_VALUES_T2];
- ax25->t3 = ax25_dev->values[AX25_VALUES_T3];
- ax25->n2 = ax25_dev->values[AX25_VALUES_N2];
- ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN];
- ax25->idle = ax25_dev->values[AX25_VALUES_IDLE];
- ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF];
-
- if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
- }
- } else {
- ax25->rtt = AX25_DEF_T1 / 2;
- ax25->t1 = AX25_DEF_T1;
- ax25->t2 = AX25_DEF_T2;
- ax25->t3 = AX25_DEF_T3;
- ax25->n2 = AX25_DEF_N2;
- ax25->paclen = AX25_DEF_PACLEN;
- ax25->idle = AX25_DEF_IDLE;
- ax25->backoff = AX25_DEF_BACKOFF;
-
- if (AX25_DEF_AXDEFMODE) {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = AX25_DEF_EWINDOW;
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = AX25_DEF_WINDOW;
- }
- }
-}
+/* ---------------------------------------------------------------------*/
/*
- * Create an empty AX.25 control block.
+ * Handle device status changes.
*/
-ax25_cb *ax25_create_cb(void)
+static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr)
{
- ax25_cb *ax25;
-
- if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
- return NULL;
-
- MOD_INC_USE_COUNT;
-
- memset(ax25, 0x00, sizeof(*ax25));
-
- skb_queue_head_init(&ax25->write_queue);
- skb_queue_head_init(&ax25->frag_queue);
- skb_queue_head_init(&ax25->ack_queue);
- skb_queue_head_init(&ax25->reseq_queue);
-
- init_timer(&ax25->timer);
- init_timer(&ax25->t1timer);
- init_timer(&ax25->t2timer);
- init_timer(&ax25->t3timer);
- init_timer(&ax25->idletimer);
-
- ax25_fillin_cb(ax25, NULL);
+ struct net_device *dev = (struct net_device *)ptr;
- ax25->state = AX25_STATE_0;
+ /* Reject non AX.25 devices */
+ if (dev->type != ARPHRD_AX25 || !AX25_PTR(dev))
+ return NOTIFY_DONE;
- return ax25;
+ switch (event) {
+ case NETDEV_UP:
+ ax25_dev_device_up(dev);
+ break;
+ case NETDEV_DOWN:
+ ax25_dev_device_down(dev);
+ break;
+ case NETDEV_REGISTER:
+ register_ax25device(dev);
+ break;
+ case NETDEV_UNREGISTER:
+ unregister_ax25device(dev);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
}
+
+/* ---------------------------------------------------------------------*/
/*
* Handling for system calls applied via the various interfaces to an
* AX25 socket object
@@ -646,7 +370,7 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
switch (optname) {
case AX25_WINDOW:
- if (sk->protinfo.ax25->modulus == AX25_MODULUS) {
+ if (sk->protinfo.ax25->seqmask == AX25_SEQMASK) {
if (opt < 1 || opt > 7)
return -EINVAL;
} else {
@@ -659,14 +383,14 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
case AX25_T1:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->rtt = (opt * HZ) / 2;
- sk->protinfo.ax25->t1 = opt * HZ;
+ sk->protinfo.ax25->t1 = opt;
+ sk->protinfo.ax25->rtt = (opt * AX25_TICS) / 4;
return 0;
case AX25_T2:
- if (opt < 1)
+ if (opt < 0)
return -EINVAL;
- sk->protinfo.ax25->t2 = opt * HZ;
+ sk->protinfo.ax25->t2 = opt;
return 0;
case AX25_N2:
@@ -678,13 +402,13 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
case AX25_T3:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->t3 = opt * HZ;
+ sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ;
return 0;
case AX25_IDLE:
if (opt < 0)
return -EINVAL;
- sk->protinfo.ax25->idle = opt * 60 * HZ;
+ sk->protinfo.ax25->idle = opt * AX25_SLOWHZ;
return 0;
case AX25_BACKOFF:
@@ -694,7 +418,7 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
return 0;
case AX25_EXTSEQ:
- sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
+ sk->protinfo.ax25->seqmask = opt ? AX25_ESEQMASK : AX25_SEQMASK;
return 0;
case AX25_PIDINCL:
@@ -712,19 +436,18 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
return 0;
case SO_BINDTODEVICE:
- if (optlen > IFNAMSIZ) optlen=IFNAMSIZ;
+ if (optlen > IFNAMSIZ) optlen = IFNAMSIZ;
if (copy_from_user(devname, optval, optlen))
return -EFAULT;
dev = dev_get_by_name(devname);
if (dev == NULL) return -ENODEV;
- if (sk->type == SOCK_SEQPACKET &&
+ if (sk->type == SOCK_SEQPACKET &&
(sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
return -EADDRNOTAVAIL;
-
- sk->protinfo.ax25->ax25_dev = ax25_dev_ax25dev(dev);
- ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
+
+ ax25_fillin_cb(sk->protinfo.ax25, dev);
return 0;
default:
@@ -732,10 +455,12 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
}
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
struct sock *sk = sock->sk;
- struct ax25_dev *ax25_dev;
+ struct net_device *dev;
char devname[IFNAMSIZ];
void *valptr;
int val = 0;
@@ -759,11 +484,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case AX25_T1:
- val = sk->protinfo.ax25->t1 / HZ;
+ val = sk->protinfo.ax25->t1;
break;
case AX25_T2:
- val = sk->protinfo.ax25->t2 / HZ;
+ val = sk->protinfo.ax25->t2;
break;
case AX25_N2:
@@ -771,11 +496,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case AX25_T3:
- val = sk->protinfo.ax25->t3 / HZ;
+ val = sk->protinfo.ax25->t3 / AX25_SLOWHZ;
break;
case AX25_IDLE:
- val = sk->protinfo.ax25->idle / (60 * HZ);
+ val = sk->protinfo.ax25->idle / AX25_SLOWHZ;
break;
case AX25_BACKOFF:
@@ -783,7 +508,7 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case AX25_EXTSEQ:
- val = (sk->protinfo.ax25->modulus == AX25_EMODULUS);
+ val = (sk->protinfo.ax25->seqmask == AX25_ESEQMASK);
break;
case AX25_PIDINCL:
@@ -799,11 +524,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case SO_BINDTODEVICE:
- ax25_dev = sk->protinfo.ax25->ax25_dev;
+ dev = sk->protinfo.ax25->device;
- if (ax25_dev != NULL && ax25_dev->dev != NULL) {
- strncpy(devname, ax25_dev->dev->name, IFNAMSIZ);
- length = min(strlen(ax25_dev->dev->name)+1, maxlen);
+ if (dev != NULL) {
+ strncpy(devname, dev->name, IFNAMSIZ);
+ length = min(strlen(dev->name)+1, maxlen);
devname[length-1] = '\0';
} else {
*devname = '\0';
@@ -820,68 +545,84 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
if (put_user(length, optlen))
return -EFAULT;
- return copy_to_user(optval, valptr, length) ? -EFAULT : 0;
+ if (copy_to_user(optval, valptr, length))
+ return -EFAULT;
+
+ return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
- if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) {
+ if (sk->type != SOCK_SEQPACKET)
+ return -EOPNOTSUPP;
+
+ if (sk->state != TCP_LISTEN) {
+ /*
+ * POSIX VIOLATION: according to listen(2) the call can
+ * never return EADDRINUSE. bind(2) should have done this.
+ * However, things are different with AX.25. You are
+ * _required_ to bind() before connect() to set the
+ * source callsign of the outgoing connection. But as you
+ * may open multiple connections at one time with the
+ * same source callsign, you cannot perform this check
+ * within bind(). And as I like to have descriptive errors,
+ * EADDRINUSE is perfect to be returned here.
+ */
+ if (ax25_find_listener(&sk->protinfo.ax25->addr.src, sk->protinfo.ax25->iamdigi, sk->protinfo.ax25->device))
+ return -EADDRINUSE;
+
+ ax25_insert_cb(sk->protinfo.ax25);
sk->max_ack_backlog = backlog;
sk->state = TCP_LISTEN;
return 0;
}
- return -EOPNOTSUPP;
+ return -EINVAL;
}
-int ax25_create(struct socket *sock, int protocol)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_create(struct socket *sock, int protocol)
{
struct sock *sk;
ax25_cb *ax25;
switch (sock->type) {
- case SOCK_DGRAM:
- if (protocol == 0 || protocol == PF_AX25)
- protocol = AX25_P_TEXT;
+ case SOCK_DGRAM:
+ if (protocol == 0 || protocol == PF_AX25)
+ protocol = AX25_P_TEXT;
+ break;
+ case SOCK_SEQPACKET:
+ switch (protocol) {
+ case 0:
+ case PF_AX25: /* For CLX */
+ protocol = AX25_P_TEXT;
break;
- case SOCK_SEQPACKET:
- switch (protocol) {
- case 0:
- case PF_AX25: /* For CLX */
- protocol = AX25_P_TEXT;
- break;
- case AX25_P_SEGMENT:
+ case AX25_P_SEGMENT:
#ifdef CONFIG_INET
- case AX25_P_ARP:
- case AX25_P_IP:
+ case AX25_P_ARP:
#endif
#ifdef CONFIG_NETROM
- case AX25_P_NETROM:
+ case AX25_P_NETROM:
#endif
#ifdef CONFIG_ROSE
- case AX25_P_ROSE:
-#endif
- return -ESOCKTNOSUPPORT;
-#ifdef CONFIG_NETROM_MODULE
- case AX25_P_NETROM:
- if (ax25_protocol_is_registered(AX25_P_NETROM))
- return -ESOCKTNOSUPPORT;
+ case AX25_P_ROSE:
#endif
-#ifdef CONFIG_ROSE_MODULE
- case AX25_P_ROSE:
- if (ax25_protocol_is_registered(AX25_P_ROSE))
- return -ESOCKTNOSUPPORT;
-#endif
- default:
- break;
- }
- break;
- case SOCK_RAW:
- break;
- default:
return -ESOCKTNOSUPPORT;
+
+ default:
+ if (ax25_protocol_is_registered(protocol))
+ return -ESOCKTNOSUPPORT;
+ }
+ break;
+ case SOCK_RAW:
+ break;
+ default:
+ return -ESOCKTNOSUPPORT;
}
if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL)
@@ -904,226 +645,162 @@ int ax25_create(struct socket *sock, int protocol)
return 0;
}
-struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev)
+static int ax25_release(struct socket *sock)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
ax25_cb *ax25;
- if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL)
- return NULL;
+ if (sk == NULL)
+ return 0;
- if ((ax25 = ax25_create_cb()) == NULL) {
- sk_free(sk);
- return NULL;
- }
+ ax25 = sk->protinfo.ax25;
- switch (osk->type) {
- case SOCK_DGRAM:
- break;
- case SOCK_SEQPACKET:
- break;
- default:
- sk_free(sk);
- ax25_free_cb(ax25);
- return NULL;
- }
+ sk->state = TCP_CLOSE;
+ sk->shutdown = SHUTDOWN_MASK;
+ /*
+ * don't wake me, I'm dying: this one has cost me nerves a bit.
+ * Seems that we should not attempt to wake an application
+ * that is currently exiting, which is exactly what state_change()
+ * does. It results in calling __wake_up() with invalid arguments
+ */
+ if (!(current->flags & PF_EXITING))
+ sk->state_change(sk);
+ sk->dead = 1;
- sock_init_data(NULL, sk);
+ if (sk->type == SOCK_STREAM || sk->type == SOCK_SEQPACKET) {
+ switch (ax25->state) {
+ default:
+ ax25_remove_cb(ax25);
+ break;
- sk->destruct = ax25_free_sock;
- sk->type = osk->type;
- sk->socket = osk->socket;
- sk->priority = osk->priority;
- sk->protocol = osk->protocol;
- sk->rcvbuf = osk->rcvbuf;
- sk->sndbuf = osk->sndbuf;
- sk->debug = osk->debug;
- sk->state = TCP_ESTABLISHED;
- sk->sleep = osk->sleep;
- sk->zapped = osk->zapped;
-
- ax25->modulus = osk->protinfo.ax25->modulus;
- ax25->backoff = osk->protinfo.ax25->backoff;
- ax25->pidincl = osk->protinfo.ax25->pidincl;
- ax25->iamdigi = osk->protinfo.ax25->iamdigi;
- ax25->rtt = osk->protinfo.ax25->rtt;
- ax25->t1 = osk->protinfo.ax25->t1;
- ax25->t2 = osk->protinfo.ax25->t2;
- ax25->t3 = osk->protinfo.ax25->t3;
- ax25->n2 = osk->protinfo.ax25->n2;
- ax25->idle = osk->protinfo.ax25->idle;
- ax25->paclen = osk->protinfo.ax25->paclen;
- ax25->window = osk->protinfo.ax25->window;
-
- ax25->ax25_dev = ax25_dev;
- ax25->source_addr = osk->protinfo.ax25->source_addr;
-
- if (osk->protinfo.ax25->digipeat != NULL) {
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- sk_free(sk);
- return NULL;
+ case AX25_STATE_3: /* connected */
+ case AX25_STATE_4: /* timer recovery */
+ ax25_set_cond(ax25, AX25_COND_RELEASE);
+ break;
}
-
- memcpy(ax25->digipeat, osk->protinfo.ax25->digipeat, sizeof(ax25_digi));
}
- sk->protinfo.ax25 = ax25;
- ax25->sk = sk;
-
- return sk;
-}
-
-static int ax25_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
-
- if (sk == NULL) return 0;
-
- if (sk->type == SOCK_SEQPACKET) {
- switch (sk->protinfo.ax25->state) {
- case AX25_STATE_0:
- ax25_disconnect(sk->protinfo.ax25, 0);
- ax25_destroy_socket(sk->protinfo.ax25);
- break;
-
- case AX25_STATE_1:
- case AX25_STATE_2:
- ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(sk->protinfo.ax25, 0);
- ax25_destroy_socket(sk->protinfo.ax25);
- break;
-
- case AX25_STATE_3:
- case AX25_STATE_4:
- ax25_clear_queues(sk->protinfo.ax25);
- sk->protinfo.ax25->n2count = 0;
- switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_stop_t2timer(sk->protinfo.ax25);
- ax25_stop_t3timer(sk->protinfo.ax25);
- ax25_stop_idletimer(sk->protinfo.ax25);
- break;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- ax25_stop_t3timer(sk->protinfo.ax25);
- ax25_stop_idletimer(sk->protinfo.ax25);
- break;
-#endif
- }
- ax25_calculate_t1(sk->protinfo.ax25);
- ax25_start_t1timer(sk->protinfo.ax25);
- sk->protinfo.ax25->state = AX25_STATE_2;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- sk->destroy = 1;
- break;
-
- default:
- break;
- }
+ sk->protinfo.ax25 = NULL;
+ if (ax25->inserted && ax25->device != NULL) {
+ ax25->killtimer = 0;
+ ax25->sk = NULL;
} else {
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- ax25_destroy_socket(sk->protinfo.ax25);
+ ax25_destroy_cb(ax25);
}
-
- sock->sk = NULL;
- sk->socket = NULL; /* Not used, but we should do this */
-
+ ax25_destroy_socket(sk);
return 0;
}
+/* ---------------------------------------------------------------------*/
/*
- * We support a funny extension here so you can (as root) give any callsign
- * digipeated via a local address as source. This hack is obsolete now
- * that we've implemented support for SO_BINDTODEVICE. It is however small
- * and trivially backward compatible.
+ * Former semantics:
+ *
+ * - struct sockaddr_ax25 contains the interface callsign, outgoing
+ * user connects either get the interface callsign or the one
+ * provided by the uid/callsign translation table for the source
+ * address
+ *
+ * - struct full_sockaddr_ax25 provides the interface callsign as
+ * the first digipeater, fsa.fsa_ax25call provides the source
+ * address for the connection.
+ *
+ * New semantics:
+ *
+ * We now have SO_BINDTODEVICE, ax25_bind (should) only set the
+ * source address. Thus we'll allow the "bind to device callsign
+ * provided with the digipeater field" hack only for backward
+ * compatibility.
+ *
+ * NB: I don't follow Matthias' and Jens' patch here as I do
+ * plan to allow multiple callsigns for one uid (including
+ * multiple SSIDs) and assigning user callsigns per interface.
*/
+
static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
- ax25_address *call;
- ax25_dev *ax25_dev = NULL;
+ struct net_device *dev;
+ ax25_address *call = NULL;
+ /* already bound */
if (sk->zapped == 0)
return -EINVAL;
- if (addr_len != sizeof(struct sockaddr_ax25) &&
- addr_len != sizeof(struct full_sockaddr_ax25)) {
- /* support for old structure may go away some time */
- if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
- (addr_len > sizeof(struct full_sockaddr_ax25)))
- return -EINVAL;
-
- printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n",
- current->comm);
- }
-
- if (addr->fsa_ax25.sax25_family != AF_AX25)
+ if (addr_len != sizeof(struct sockaddr_ax25) &&
+ addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
- call = ax25_findbyuid(current->euid);
- if (call == NULL && ax25_uid_policy && !capable(CAP_NET_ADMIN))
- return -EACCES;
-
- if (call == NULL)
- sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call;
- else
- sk->protinfo.ax25->source_addr = *call;
+ /* wrong family */
+ if (addr->fsax25_family != AF_AX25)
+ return -EINVAL;
/*
- * User already set interface with SO_BINDTODEVICE
+ * User did not set interface with SO_BINDTODEVICE
+ * thus we'll use the compatibility code
*/
- if (sk->protinfo.ax25->ax25_dev != NULL)
- goto done;
+ dev = sk->protinfo.ax25->device;
+ if (dev == NULL) {
+ /* Try to find the interface... */
+ if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) {
+ /* device callsign provided... */
+ if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
+ (dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL)
+ return -EADDRNOTAVAIL;
+ } else {
+ /* addr->fsax25_call is device callsign */
+ if ((dev = ax25rtr_get_dev(&addr->fsax25_call)) == NULL)
+ return -EADDRNOTAVAIL;
+ }
+
+ ax25_fillin_cb(sk->protinfo.ax25, dev);
+ }
- if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
- if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
- (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL)
- return -EADDRNOTAVAIL;
- } else {
- if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL)
- return -EADDRNOTAVAIL;
+ /* root can do whatever (s)he likes, but anyone else... */
+ if (!capable(CAP_NET_BIND_SERVICE))
+ {
+ call = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name);
+ if (call == NULL && ax25_uid_policy)
+ return -EACCES;
}
- if (ax25_dev != NULL)
- ax25_fillin_cb(sk->protinfo.ax25, ax25_dev);
+ if (call == NULL) call = &addr->fsax25_call;
-done:
- ax25_insert_socket(sk->protinfo.ax25);
+ sk->protinfo.ax25->addr.src = *call;
+ sk->protinfo.ax25->addr.dcount = 0;
+ sk->protinfo.ax25->addr.lastrepeat = -1;
+
+// ax25_insert_socket(sk->protinfo.ax25); /* FIXME: gone with Matthias' patch, intentionally? */
sk->zapped = 0;
return 0;
}
-/*
- * FIXME: nonblock behaviour looks like it may have a bug.
- */
-static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
{
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
- ax25_digi *digi = NULL;
+ ax25_addr_t *addr;
+ ax25_address *dest;
int ct = 0, err;
/* deal with restarts */
if (sock->state == SS_CONNECTING) {
switch (sk->state) {
case TCP_SYN_SENT: /* still trying */
- return -EINPROGRESS;
+ if (!(flags & O_NONBLOCK))
+ goto wait_for_con;
+ return -EALREADY;
case TCP_ESTABLISHED: /* connection established */
sock->state = SS_CONNECTED;
return 0;
case TCP_CLOSE: /* connection refused */
+ case TCP_CLOSE_WAIT:
sock->state = SS_UNCONNECTED;
return -ECONNREFUSED;
}
@@ -1139,118 +816,77 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
* some sanity checks. code further down depends on this
*/
- if (addr_len == sizeof(struct sockaddr_ax25)) {
- /* support for this will go away in early 2.5.x */
+ if (addr_len == sizeof(struct sockaddr_ax25))
printk(KERN_WARNING "ax25_connect(): %s uses obsolete socket structure\n",
current->comm);
- }
- else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
- /* support for old structure may go away some time */
- if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
- (addr_len > sizeof(struct full_sockaddr_ax25)))
- return -EINVAL;
-
- printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n",
- current->comm);
- }
-
- if (fsa->fsa_ax25.sax25_family != AF_AX25)
+ else if (addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
- if (sk->protinfo.ax25->digipeat != NULL) {
- kfree(sk->protinfo.ax25->digipeat);
- sk->protinfo.ax25->digipeat = NULL;
- }
-
/*
* Handle digi-peaters to be used.
*/
- if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) {
+ SOCK_DEBUG(sk, "ax25_connect: ndigi=%d\n", fsa->fsax25_ndigis);
+
+ addr = &sk->protinfo.ax25->addr;
+ addr->dcount = 0;
+ addr->lastrepeat = -1;
+ if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsax25_ndigis != 0) {
/* Valid number of digipeaters ? */
- if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS)
+ if (fsa->fsax25_ndigis < 0 || fsa->fsax25_ndigis > AX25_MAX_DIGIS)
return -EINVAL;
- if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
- return -ENOBUFS;
+ addr->dcount = fsa->fsax25_ndigis;
- digi->ndigi = fsa->fsa_ax25.sax25_ndigis;
- digi->lastrepeat = -1;
-
- while (ct < fsa->fsa_ax25.sax25_ndigis) {
- if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) {
- digi->repeated[ct] = 1;
- digi->lastrepeat = ct;
- } else {
- digi->repeated[ct] = 0;
- }
- digi->calls[ct] = fsa->fsa_digipeater[ct];
+ while (ct < fsa->fsax25_ndigis) {
+ if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi)
+ addr->lastrepeat = ct;
+ addr->digipeater[ct] = fsa->fsa_digipeater[ct];
ct++;
}
+
+ /* where to go next? either to next digipeater in path ...*/
+ dest = &addr->digipeater[addr->lastrepeat+1];
+ } else {
+ /* ... or directly to the destination */
+ dest = &fsa->fsax25_call;
}
- /*
- * Must bind first - autobinding in this may or may not work. If
- * the socket is already bound, check to see if the device has
- * been filled in, error if it hasn't.
- */
- if (sk->zapped) {
- /* check if we can remove this feature. It is broken. */
- printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n",
- current->comm);
- if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0)
+ if (sk->protinfo.ax25->device == NULL) {
+ if ((err = ax25_rt_fillin_dev(sk->protinfo.ax25, dest)) < 0)
return err;
- ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
- ax25_insert_socket(sk->protinfo.ax25);
- } else {
- if (sk->protinfo.ax25->ax25_dev == NULL)
- return -EHOSTUNREACH;
+ SOCK_DEBUG(sk, "ax25_connect: device filled in\n");
}
+ addr->dest = fsa->fsax25_call;
- if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) {
- if (digi != NULL) kfree(digi);
- return -EADDRINUSE; /* Already such a connection */
+ if (sk->type == SOCK_SEQPACKET) {
+ ax25_cb* ax25 = ax25_find_cb(addr, sk->protinfo.ax25->device);
+ if (ax25) {
+ if (ax25->state != AX25_STATE_0)
+ return -EADDRINUSE; /* Already such a connection */
+ ax25_destroy_cb(ax25);
+ }
}
- sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call;
- sk->protinfo.ax25->digipeat = digi;
+ ax25_insert_cb(sk->protinfo.ax25);
/* First the easy one */
- if (sk->type != SOCK_SEQPACKET) {
+ if (sk->type != SOCK_SEQPACKET && sk->type != SOCK_STREAM) {
sock->state = SS_CONNECTED;
sk->state = TCP_ESTABLISHED;
return 0;
}
/* Move to connecting socket, ax.25 lapb WAIT_UA.. */
- sock->state = SS_CONNECTING;
- sk->state = TCP_SYN_SENT;
-
- switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_establish_data_link(sk->protinfo.ax25);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- sk->protinfo.ax25->modulus = AX25_MODULUS;
- sk->protinfo.ax25->window = sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- if (sk->protinfo.ax25->ax25_dev->dama.slave)
- ax25_ds_establish_data_link(sk->protinfo.ax25);
- else
- ax25_std_establish_data_link(sk->protinfo.ax25);
- break;
-#endif
- }
-
- sk->protinfo.ax25->state = AX25_STATE_1;
-
- ax25_start_heartbeat(sk->protinfo.ax25);
+ sock->state = SS_CONNECTING;
+ sk->state = TCP_SYN_SENT;
+ /* Start going SABM SABM until a UA or a give up and DM */
+ ax25_establish_data_link(sk->protinfo.ax25);
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
return -EINPROGRESS;
+ wait_for_con:
cli(); /* To avoid races on the sleep */
/* A DM or timeout will go to closed, a UA will go to ABM */
@@ -1270,12 +906,11 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
}
sock->state = SS_CONNECTED;
-
sti();
-
return 0;
}
+/* ---------------------------------------------------------------------*/
static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
{
@@ -1310,12 +945,14 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
}
} while (skb == NULL);
- newsk = skb->sk;
- newsk->pair = NULL;
+ newsk = skb->sk;
+ newsk->pair = NULL;
newsk->socket = newsock;
newsk->sleep = &newsock->wait;
/* Now attach up the new socket */
+ skb->sk = NULL;
+ skb->destructor = NULL;
kfree_skb(skb);
sk->ack_backlog--;
newsock->sk = newsk;
@@ -1324,132 +961,108 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
{
+ struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
struct sock *sk = sock->sk;
- struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
- unsigned char ndigi, i;
+ unsigned char dcount;
- if (peer != 0) {
+ if (peer) {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
- fsa->fsa_ax25.sax25_family = AF_AX25;
- fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->dest_addr;
- fsa->fsa_ax25.sax25_ndigis = 0;
-
- if (sk->protinfo.ax25->digipeat != NULL) {
- ndigi = sk->protinfo.ax25->digipeat->ndigi;
- fsa->fsa_ax25.sax25_ndigis = ndigi;
- for (i = 0; i < ndigi; i++)
- fsa->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i];
- }
+ sax->fsax25_family = AF_AX25;
+ sax->fsax25_call = sk->protinfo.ax25->addr.dest;
+ dcount = sax->fsax25_ndigis = sk->protinfo.ax25->addr.dcount;
+ memcpy(sax->fsa_digipeater, sk->protinfo.ax25->addr.digipeater, dcount * AX25_ADDR_LEN);
} else {
- fsa->fsa_ax25.sax25_family = AF_AX25;
- fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->source_addr;
- fsa->fsa_ax25.sax25_ndigis = 1;
- if (sk->protinfo.ax25->ax25_dev != NULL) {
- memcpy(&fsa->fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN);
+ sax->fsax25_family = AF_AX25;
+ sax->fsax25_call = sk->protinfo.ax25->addr.src;
+
+ if (sk->protinfo.ax25->device != NULL) {
+ sax->fsax25_ndigis = 1;
+ memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN);
} else {
- fsa->fsa_digipeater[0] = null_ax25_address;
+ sax->fsax25_ndigis = 0;
}
}
*uaddr_len = sizeof (struct full_sockaddr_ax25);
return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
int err;
- struct sockaddr_ax25 sax;
struct sk_buff *skb;
unsigned char *asmptr;
int size;
- ax25_digi *dp;
- ax25_digi dtmp;
int lv;
int addr_len = msg->msg_namelen;
+ ax25_addr_t addr;
if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR))
return -EINVAL;
+ /* socket must be bound to a name */
if (sk->zapped)
return -EADDRNOTAVAIL;
+ /* socket ist shut down */
if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 0);
return -EPIPE;
}
- if (sk->protinfo.ax25->ax25_dev == NULL)
+ if (sk->protinfo.ax25->device == NULL)
return -ENETUNREACH;
- if (usax != NULL) {
+ if (addr_len != 0) {
if (usax->sax25_family != AF_AX25)
return -EINVAL;
+ if (sk->type == SOCK_SEQPACKET)
+ return -EISCONN;
- if (addr_len == sizeof(struct sockaddr_ax25)) {
+ if (addr_len != sizeof(struct sockaddr_ax25)) {
printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n",
current->comm);
- }
- else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
- /* support for old structure may go away some time */
- if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
- (addr_len > sizeof(struct full_sockaddr_ax25)))
- return -EINVAL;
-
- printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n",
- current->comm);
- }
+ } else if (addr_len != sizeof(struct full_sockaddr_ax25))
+ return -EINVAL;
if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) {
- int ct = 0;
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax;
+ int ct;
/* Valid number of digipeaters ? */
- if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS)
+ if (usax->sax25_ndigis < 0 || usax->sax25_ndigis > AX25_MAX_DIGIS)
return -EINVAL;
- dtmp.ndigi = usax->sax25_ndigis;
-
- while (ct < usax->sax25_ndigis) {
- dtmp.repeated[ct] = 0;
- dtmp.calls[ct] = fsa->fsa_digipeater[ct];
- ct++;
- }
-
- dtmp.lastrepeat = 0;
+ for (ct = 0; ct < usax->sax25_ndigis; ct++)
+ addr.digipeater[ct] = fsa->fsa_digipeater[ct];
+ addr.lastrepeat = -1;
}
-
- sax = *usax;
- if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0)
- return -EISCONN;
- if (usax->sax25_ndigis == 0)
- dp = NULL;
- else
- dp = &dtmp;
+ addr.dcount = usax->sax25_ndigis;
+ addr.dest = usax->sax25_call;
} else {
- /*
- * FIXME: 1003.1g - if the socket is like this because
- * it has become closed (not started closed) and is VC
- * we ought to SIGPIPE, EPIPE
- */
- if (sk->state != TCP_ESTABLISHED)
+ if (sk->state != TCP_ESTABLISHED) {
+ if (sk->dead) {
+ send_sig(SIGPIPE, current, 0);
+ return -EPIPE;
+ }
return -ENOTCONN;
- sax.sax25_family = AF_AX25;
- sax.sax25_call = sk->protinfo.ax25->dest_addr;
- dp = sk->protinfo.ax25->digipeat;
+ }
+ addr = sk->protinfo.ax25->addr;
}
- SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n");
-
- /* Build a packet */
- SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n");
+ SOCK_DEBUG(sk, "AX.25: sendto: Addresses built, building packet.\n");
/* Assume the worst case */
- size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN;
+ size = len + 3 + ax25_sizeof_addr(&addr) + AX25_BPQ_HEADER_LEN;
if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
return err;
@@ -1463,10 +1076,8 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
skb->nh.raw = skb->data;
/* Add the PID if one is not supplied by the user in the skb */
- if (!sk->protinfo.ax25->pidincl) {
- asmptr = skb_push(skb, 1);
- *asmptr = sk->protocol;
- }
+ if (!sk->protinfo.ax25->pidincl)
+ *skb_push(skb, 1) = sk->protocol;
SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n");
@@ -1476,44 +1087,38 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
kfree_skb(skb);
return -ENOTCONN;
}
-
ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */
-
return len;
- } else {
- asmptr = skb_push(skb, 1 + ax25_addr_size(dp));
-
- SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp);
-
- if (dp != NULL)
- SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi);
-
- /* Build an AX.25 header */
- asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS));
+ }
- SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
+ addr.src = sk->protinfo.ax25->addr.src;
+ asmptr = skb_push(skb, ax25_sizeof_addr(&addr)+1);
- skb->h.raw = asmptr;
+ SOCK_DEBUG(sk, "Num digipeaters=%d\n", addr.dcount);
- SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr);
+ /* Build an AX.25 header */
+ lv = ax25_build_addr(asmptr, &addr, AX25_COMMAND, AX25_SEQMASK);
+ asmptr += lv;
- *asmptr = AX25_UI;
+ SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
+ SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr);
- /* Datagram frames go straight out of the door as UI */
- skb->dev = sk->protinfo.ax25->ax25_dev->dev;
+ *asmptr = AX25_UI;
- ax25_queue_xmit(skb);
+ /* Datagram frames go straight out of the door as UI */
+ ax25_send_unproto(skb, sk->protinfo.ax25->device);
- return len;
- }
+ return len;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
int copied;
struct sk_buff *skb;
- int er;
+ int err;
/*
* This works for seqpacket too. The receiver has ordered the
@@ -1523,8 +1128,8 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f
return -ENOTCONN;
/* Now we can treat all alike */
- if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
- return er;
+ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL)
+ return err;
if (!sk->protinfo.ax25->pidincl)
skb_pull(skb, 1); /* Remove PID */
@@ -1540,25 +1145,19 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (msg->msg_namelen != 0) {
- struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
- ax25_digi digi;
- ax25_address dest;
+ struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)msg->msg_name;
+ ax25_pktinfo pkt;
- ax25_addr_parse(skb->mac.raw+1, skb->data-skb->mac.raw-1, NULL, &dest, &digi, NULL, NULL);
+ ax25_parse_addr(skb->mac.raw, skb->data-skb->mac.raw, &pkt);
- sax->sax25_family = AF_AX25;
- /* We set this correctly, even though we may not let the
- application know the digi calls further down (because it
- did NOT ask to know them). This could get political... **/
- sax->sax25_ndigis = digi.ndigi;
- sax->sax25_call = dest;
+ sax->fsax25_family = AF_AX25;
+ sax->fsax25_ndigis = pkt.addr.dcount;
+ sax->fsax25_call = pkt.addr.dest;
- if (sax->sax25_ndigis != 0) {
+ if (sax->fsax25_ndigis != 0) {
int ct;
- struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax;
-
- for (ct = 0; ct < digi.ndigi; ct++)
- fsa->fsa_digipeater[ct] = digi.calls[ct];
+ for (ct = 0; ct < pkt.addr.dcount; ct++)
+ sax->fsa_digipeater[ct] = pkt.addr.digipeater[ct];
}
msg->msg_namelen = sizeof(struct full_sockaddr_ax25);
}
@@ -1568,12 +1167,31 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f
return copied;
}
-static int ax25_shutdown(struct socket *sk, int how)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_shutdown(struct socket *sock, int how)
{
- /* FIXME - generate DM and RNR states */
- return -EOPNOTSUPP;
+ switch (how) {
+ case 0:
+ sock->sk->shutdown = RCV_SHUTDOWN;
+ break;
+
+ case 1:
+ sock->sk->shutdown = SEND_SHUTDOWN;
+ break;
+
+ case 2:
+ sock->sk->shutdown = SHUTDOWN_MASK;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
@@ -1584,7 +1202,9 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
- return put_user(amount, (int *)arg);
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
+ return 0;
}
case TIOCINQ: {
@@ -1593,15 +1213,19 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->receive_queue)) != NULL)
amount = skb->len;
- return put_user(amount, (int *)arg);
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
+ return 0;
}
case SIOCGSTAMP:
if (sk != NULL) {
if (sk->stamp.tv_sec == 0)
return -ENOENT;
- return copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0;
- }
+ if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+ return -EFAULT;
+ return 0;
+ }
return -EINVAL;
case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */
@@ -1637,29 +1261,31 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return -EPERM;
return ax25_ctl_ioctl(cmd, (void *)arg);
- case SIOCAX25GETINFO:
+ case SIOCAX25GETINFO:
case SIOCAX25GETINFOOLD: {
struct ax25_info_struct ax25_info;
- ax25_info.t1 = sk->protinfo.ax25->t1 / HZ;
- ax25_info.t2 = sk->protinfo.ax25->t2 / HZ;
- ax25_info.t3 = sk->protinfo.ax25->t3 / HZ;
- ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ);
+ read_lock(&sk->protinfo.ax25->timer_lock);
+ ax25_info.t1 = sk->protinfo.ax25->t1;
+ ax25_info.t2 = sk->protinfo.ax25->t2;
+ ax25_info.t3 = sk->protinfo.ax25->t3 / AX25_SLOWHZ;
+ ax25_info.idle = sk->protinfo.ax25->idle / AX25_SLOWHZ;
ax25_info.n2 = sk->protinfo.ax25->n2;
- ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ;
- ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ;
- ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ;
- ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ);
+ ax25_info.t1timer = sk->protinfo.ax25->wrt_timer;
+ ax25_info.t3timer = sk->protinfo.ax25->wrt_timer / AX25_SLOWHZ;
+ ax25_info.t2timer = sk->protinfo.ax25->ack_timer;
+ ax25_info.idletimer = sk->protinfo.ax25->idletimer / AX25_SLOWHZ;
ax25_info.n2count = sk->protinfo.ax25->n2count;
ax25_info.state = sk->protinfo.ax25->state;
ax25_info.rcv_q = atomic_read(&sk->rmem_alloc);
ax25_info.snd_q = atomic_read(&sk->wmem_alloc);
ax25_info.vs = sk->protinfo.ax25->vs;
- ax25_info.vr = sk->protinfo.ax25->vr;
- ax25_info.va = sk->protinfo.ax25->va;
- ax25_info.vs_max = sk->protinfo.ax25->vs; /* reserved */
+ ax25_info.vr = sk->protinfo.ax25->vr;
+ ax25_info.va = sk->protinfo.ax25->va;
+ ax25_info.vs_max = sk->protinfo.ax25->vs_max;
ax25_info.paclen = sk->protinfo.ax25->paclen;
ax25_info.window = sk->protinfo.ax25->window;
+ read_unlock(&sk->protinfo.ax25->timer_lock);
/* old structure? */
if (cmd == SIOCAX25GETINFOOLD) {
@@ -1670,12 +1296,14 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
warned=1;
}
- if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated)))
+ if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated)))
return -EFAULT;
} else {
- if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct)))
+ if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct)))
return -EINVAL;
- }
+ }
+
+
return 0;
}
@@ -1709,48 +1337,47 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return 0;
}
-static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
-{
- ax25_cb *ax25;
- int k;
- int len = 0;
- off_t pos = 0;
- off_t begin = 0;
+/* ---------------------------------------------------------------------*/
- cli();
+static int ax25_print_list(char *buffer, off_t *begin, off_t offset, int length, off_t *pos, ax25_cb *ax25, char *devname) {
+ int len = 0;
/*
* New format:
* magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode
*/
-
- for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
+
+ for ( ; ax25 != NULL; ax25 = ax25->next) {
+ int k;
+
len += sprintf(buffer+len, "%8.8lx %s %s%s ",
- (long) ax25,
- ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name,
- ax2asc(&ax25->source_addr),
- ax25->iamdigi? "*":"");
-
- len += sprintf(buffer+len, "%s", ax2asc(&ax25->dest_addr));
-
- for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) {
+ (long) ax25,
+ devname,
+ ax2asc(&ax25->addr.src),
+ ax25->iamdigi? "*":"");
+
+ len += sprintf(buffer+len, "%s", ax2asc(&ax25->addr.dest));
+
+ for (k=0; k < ax25->addr.dcount; k++) {
len += sprintf(buffer+len, ",%s%s",
- ax2asc(&ax25->digipeat->calls[k]),
- ax25->digipeat->repeated[k]? "*":"");
+ ax2asc(&ax25->addr.digipeater[k]),
+ ax25->addr.lastrepeat == k ? "*":"");
}
-
- len += sprintf(buffer+len, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d",
+
+ read_lock(&ax25->timer_lock);
+ len += sprintf(buffer+len, " %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
ax25->state,
ax25->vs, ax25->vr, ax25->va,
- ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ,
- ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ,
- ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ,
- ax25_display_timer(&ax25->idletimer) / (60 * HZ),
- ax25->idle / (60 * HZ),
+ ax25->wrt_timer , ax25->t1,
+ ax25->ack_timer , ax25->t2,
+ ax25->t3 , ax25->t3,
+ ax25->idletimer / AX25_SLOWHZ,
+ ax25->idle / AX25_SLOWHZ,
ax25->n2count, ax25->n2,
- ax25->rtt / HZ,
+ ax25->rtt / AX25_TICS,
ax25->window,
ax25->paclen);
+ read_unlock(&ax25->timer_lock);
if (ax25->sk != NULL) {
len += sprintf(buffer + len, " %d %d %ld\n",
@@ -1761,121 +1388,70 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
len += sprintf(buffer + len, " * * *\n");
}
- pos = begin + len;
+ *pos = *begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
+ if (*pos < offset) {
+ len = 0;
+ *begin = *pos;
}
- if (pos > offset + length)
+ if (*pos > offset + length)
break;
}
- sti();
-
- *start = buffer + (offset - begin);
- len -= (offset - begin);
-
- if (len > length) len = length;
-
- return(len);
+ return len;
}
-static struct net_proto_family ax25_family_ops = {
- family: PF_AX25,
- create: ax25_create,
-};
-static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = {
- family: PF_AX25,
+/* ---------------------------------------------------------------------*/
- release: ax25_release,
- bind: ax25_bind,
- connect: ax25_connect,
- socketpair: sock_no_socketpair,
- accept: ax25_accept,
- getname: ax25_getname,
- poll: datagram_poll,
- ioctl: ax25_ioctl,
- listen: ax25_listen,
- shutdown: ax25_shutdown,
- setsockopt: ax25_setsockopt,
- getsockopt: ax25_getsockopt,
- sendmsg: ax25_sendmsg,
- recvmsg: ax25_recvmsg,
- mmap: sock_no_mmap,
-};
+static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ struct net_device *dev;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = begin;
+ int i;
-#include <linux/smp_lock.h>
-SOCKOPS_WRAP(ax25_proto, PF_AX25);
+ read_lock(&ax25_dev_lock);
-/*
- * Called by socket.c on kernel start up
- */
-static struct packet_type ax25_packet_type = {
- type: __constant_htons(ETH_P_AX25),
- func: ax25_kiss_rcv,
-};
+ len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_list, "*");
-static struct notifier_block ax25_dev_notifier = {
- notifier_call: ax25_device_event,
-};
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ dev = ax25_devices[i];
+ if (!dev || !(dev->flags & IFF_UP))
+ continue;
-EXPORT_SYMBOL(ax25_encapsulate);
-EXPORT_SYMBOL(ax25_rebuild_header);
-EXPORT_SYMBOL(ax25_findbyuid);
-EXPORT_SYMBOL(ax25_find_cb);
-EXPORT_SYMBOL(ax25_linkfail_register);
-EXPORT_SYMBOL(ax25_linkfail_release);
-EXPORT_SYMBOL(ax25_listen_register);
-EXPORT_SYMBOL(ax25_listen_release);
-EXPORT_SYMBOL(ax25_protocol_register);
-EXPORT_SYMBOL(ax25_protocol_release);
-EXPORT_SYMBOL(ax25_send_frame);
-EXPORT_SYMBOL(ax25_uid_policy);
-EXPORT_SYMBOL(ax25cmp);
-EXPORT_SYMBOL(ax2asc);
-EXPORT_SYMBOL(asc2ax);
-EXPORT_SYMBOL(null_ax25_address);
-EXPORT_SYMBOL(ax25_display_timer);
+ len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_dev_list(dev), dev->name);
+ }
-static const char banner[] __initdata = KERN_INFO "NET4: G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET4.0\n";
+ read_unlock(&ax25_dev_lock);
-static int __init ax25_init(void)
-{
- sock_register(&ax25_family_ops);
- dev_add_pack(&ax25_packet_type);
- register_netdevice_notifier(&ax25_dev_notifier);
- ax25_register_sysctl();
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
- proc_net_create("ax25_route", 0, ax25_rt_get_info);
- proc_net_create("ax25", 0, ax25_get_info);
- proc_net_create("ax25_calls", 0, ax25_uid_get_info);
+ if (len > length) len = length;
- printk(banner);
- return 0;
+ return len;
}
-module_init(ax25_init);
-
-MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
-MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol");
+/* ---------------------------------------------------------------------*/
-static void __exit ax25_exit(void)
+static int ax25_gifconf(struct net_device *dev, char *buf, int len)
{
- proc_net_remove("ax25_route");
- proc_net_remove("ax25");
- proc_net_remove("ax25_calls");
- ax25_rt_free();
- ax25_uid_free();
- ax25_dev_free();
+ struct ifreq ifr;
+ int done=0;
- ax25_unregister_sysctl();
- unregister_netdevice_notifier(&ax25_dev_notifier);
-
- dev_remove_pack(&ax25_packet_type);
-
- sock_unregister(PF_AX25);
+ if (!buf) {
+ done += sizeof(ifr);
+ return done;
+ }
+ if (len < (int) sizeof(ifr)) return done;
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strcpy(ifr.ifr_name, dev->name);
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_AX25;
+ if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) return -EFAULT;
+ done += sizeof(struct ifreq);
+ return done;
}
-module_exit(ax25_exit);
+
diff --git a/net/ax25/af_ax25.h b/net/ax25/af_ax25.h
new file mode 100644
index 000000000..9980f54f6
--- /dev/null
+++ b/net/ax25/af_ax25.h
@@ -0,0 +1,21 @@
+/*
+ * Interface declaration for AF_AX25 base routines layer.
+ *
+ * Matthias Welwarsky (DG2FEF) 05/28/98
+ *
+ */
+
+
+#ifndef _AF_AX25_H
+#define _AF_AX25_H
+
+#include <linux/skbuff.h>
+#include <net/ax25.h>
+
+extern struct net_device* ax25rtr_get_dev(ax25_address *);
+extern struct sock* ax25_make_new(struct sock*, struct net_device*);
+extern ax25_cb * ax25_list;
+extern void ax25_kill_by_device(struct net_device*);
+extern rwlock_t ax25_dev_lock;
+
+#endif
diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c
deleted file mode 100644
index f96c65586..000000000
--- a/net/ax25/ax25_addr.c
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_subr.c.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * The null address is defined as a callsign of all spaces with an
- * SSID of zero.
- */
-ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
-
-/*
- * ax25 -> ascii conversion
- */
-char *ax2asc(ax25_address *a)
-{
- static char buf[11];
- char c, *s;
- int n;
-
- for (n = 0, s = buf; n < 6; n++) {
- c = (a->ax25_call[n] >> 1) & 0x7F;
-
- if (c != ' ') *s++ = c;
- }
-
- *s++ = '-';
-
- if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
- *s++ = '1';
- n -= 10;
- }
-
- *s++ = n + '0';
- *s++ = '\0';
-
- if (*buf == '\0' || *buf == '-')
- return "*";
-
- return buf;
-
-}
-
-/*
- * ascii -> ax25 conversion
- */
-ax25_address *asc2ax(char *callsign)
-{
- static ax25_address addr;
- char *s;
- int n;
-
- for (s = callsign, n = 0; n < 6; n++) {
- if (*s != '\0' && *s != '-')
- addr.ax25_call[n] = *s++;
- else
- addr.ax25_call[n] = ' ';
- addr.ax25_call[n] <<= 1;
- addr.ax25_call[n] &= 0xFE;
- }
-
- if (*s++ == '\0') {
- addr.ax25_call[6] = 0x00;
- return &addr;
- }
-
- addr.ax25_call[6] = *s++ - '0';
-
- if (*s != '\0') {
- addr.ax25_call[6] *= 10;
- addr.ax25_call[6] += *s++ - '0';
- }
-
- addr.ax25_call[6] <<= 1;
- addr.ax25_call[6] &= 0x1E;
-
- return &addr;
-}
-
-/*
- * Compare two ax.25 addresses
- */
-int ax25cmp(ax25_address *a, ax25_address *b)
-{
- int ct = 0;
-
- while (ct < 6) {
- if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */
- return 1;
- ct++;
- }
-
- if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */
- return 0;
-
- return 2; /* Partial match */
-}
-
-/*
- * Compare two AX.25 digipeater paths.
- */
-int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2)
-{
- int i;
-
- if (digi1->ndigi != digi2->ndigi)
- return 1;
-
- if (digi1->lastrepeat != digi2->lastrepeat)
- return 1;
-
- for (i = 0; i < digi1->ndigi; i++)
- if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0)
- return 1;
-
- return 0;
-}
-
-/*
- * Given an AX.25 address pull of to, from, digi list, command/response and the start of data
- *
- */
-unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
-{
- int d = 0;
-
- if (len < 14) return NULL;
-
- if (flags != NULL) {
- *flags = 0;
-
- if (buf[6] & AX25_CBIT)
- *flags = AX25_COMMAND;
- if (buf[13] & AX25_CBIT)
- *flags = AX25_RESPONSE;
- }
-
- if (dama != NULL)
- *dama = ~buf[13] & AX25_DAMA_FLAG;
-
- /* 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] & AX25_EBIT)) {
- if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */
- if (len < 7) return NULL; /* Short packet */
-
- memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
- digi->ndigi = d + 1;
-
- if (buf[6] & AX25_HBIT) {
- digi->repeated[d] = 1;
- digi->lastrepeat = d;
- } else {
- digi->repeated[d] = 0;
- }
-
- buf += AX25_ADDR_LEN;
- len -= AX25_ADDR_LEN;
- d++;
- }
-
- return buf;
-}
-
-/*
- * Assemble an AX.25 header from the bits
- */
-int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus)
-{
- int len = 0;
- int ct = 0;
-
- memcpy(buf, dest, AX25_ADDR_LEN);
- buf[6] &= ~(AX25_EBIT | AX25_CBIT);
- buf[6] |= AX25_SSSID_SPARE;
-
- if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
-
- memcpy(buf, src, AX25_ADDR_LEN);
- buf[6] &= ~(AX25_EBIT | AX25_CBIT);
- buf[6] &= ~AX25_SSSID_SPARE;
-
- if (modulus == AX25_MODULUS)
- buf[6] |= AX25_SSSID_SPARE;
- else
- buf[6] |= AX25_ESSID_SPARE;
-
- if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT;
-
- /*
- * Fast path the normal digiless path
- */
- if (d == NULL || d->ndigi == 0) {
- buf[6] |= AX25_EBIT;
- return 2 * AX25_ADDR_LEN;
- }
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
-
- while (ct < d->ndigi) {
- memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
-
- if (d->repeated[ct])
- buf[6] |= AX25_HBIT;
- else
- buf[6] &= ~AX25_HBIT;
-
- buf[6] &= ~AX25_EBIT;
- buf[6] |= AX25_SSSID_SPARE;
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
- ct++;
- }
-
- buf[-1] |= AX25_EBIT;
-
- return len;
-}
-
-int ax25_addr_size(ax25_digi *dp)
-{
- if (dp == NULL)
- return 2 * AX25_ADDR_LEN;
-
- return AX25_ADDR_LEN * (2 + dp->ndigi);
-}
-
-/*
- * Reverse Digipeat List. May not pass both parameters as same struct
- */
-void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
-{
- int ct;
-
- out->ndigi = in->ndigi;
- out->lastrepeat = in->ndigi - in->lastrepeat - 2;
-
- /* Invert the digipeaters */
- for (ct = 0; ct < in->ndigi; ct++) {
- out->calls[ct] = in->calls[in->ndigi - ct - 1];
-
- if (ct <= out->lastrepeat) {
- out->calls[ct].ax25_call[6] |= AX25_HBIT;
- out->repeated[ct] = 1;
- } else {
- out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
- out->repeated[ct] = 0;
- }
- }
-}
-
diff --git a/net/ax25/ax25_core.c b/net/ax25/ax25_core.c
new file mode 100644
index 000000000..37d171d83
--- /dev/null
+++ b/net/ax25/ax25_core.c
@@ -0,0 +1,513 @@
+/*
+ * ax25_core.c: AX.25 core and support functions for NEW-AX.25
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ * Jonathan (G4KLX), Alan Cox (GW4PTS)
+ *
+ * Comment:
+ *
+ * Changelog:
+ *
+ * License: This module 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.
+ */
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/ax25.h>
+#include <linux/spinlock.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include <net/sock.h>
+
+#include "af_ax25.h"
+#include "ax25_vj.h"
+#include "ax25_ddi.h"
+#include "ax25_core.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_ddi.h"
+
+
+rwlock_t ax25_list_lock = RW_LOCK_UNLOCKED;
+
+/* ---------------------------------------------------------------------*/
+/*
+ * The null address is defined as a callsign of all spaces with an
+ * SSID of zero.
+ */
+ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
+
+/* --------------------------------------------------------------------- */
+/*
+ * ax25 -> ascii conversion
+ */
+char *ax2asc(ax25_address *a)
+{
+ static char buf[11];
+ char c, *s;
+ int n;
+
+ for (n = 0, s = buf; n < 6; n++) {
+ c = (a->ax25_call[n] >> 1) & 0x7F;
+
+ if (c != ' ') *s++ = c;
+ }
+
+ *s++ = '-';
+
+ if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
+ *s++ = '1';
+ n -= 10;
+ }
+
+ *s++ = n + '0';
+ *s++ = '\0';
+
+ if (*buf == '\0' || *buf == '-')
+ return "*";
+
+ return buf;
+
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * ascii -> ax25 conversion
+ */
+ax25_address *asc2ax(char *callsign)
+{
+ static ax25_address addr;
+ char *s;
+ int n;
+
+ for (s = callsign, n = 0; n < 6; n++) {
+ if (*s != '\0' && *s != '-')
+ addr.ax25_call[n] = *s++;
+ else
+ addr.ax25_call[n] = ' ';
+ addr.ax25_call[n] <<= 1;
+ addr.ax25_call[n] &= 0xFE;
+ }
+
+ if (*s++ == '\0') {
+ addr.ax25_call[6] = 0x00;
+ return &addr;
+ }
+
+ addr.ax25_call[6] = *s++ - '0';
+
+ if (*s != '\0') {
+ addr.ax25_call[6] *= 10;
+ addr.ax25_call[6] += *s++ - '0';
+ }
+
+ addr.ax25_call[6] <<= 1;
+ addr.ax25_call[6] &= 0x1E;
+
+ return &addr;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Compare two ax.25 addresses
+ */
+int ax25cmp(ax25_address *a, ax25_address *b)
+{
+ int ct = 0;
+
+ while (ct < 6) {
+ if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */
+ return 1;
+ ct++;
+ }
+
+ if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */
+ return 0;
+
+ return 2; /* Partial match */
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Add a socket to the bound sockets list.
+ */
+void ax25_insert_cb(ax25_cb *ax25)
+{
+ if (ax25->device != NULL) {
+ ax25_dev_insert_cb(ax25);
+ return;
+ }
+
+ ax25->prev = NULL;
+ ax25->next = ax25_list;
+
+ write_lock(&ax25_list_lock);
+
+ if (ax25_list != NULL)
+ ax25_list->prev = ax25;
+ ax25_list = ax25;
+
+ write_unlock(&ax25_list_lock);
+
+ ax25->inserted = 1;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Socket removal is now protected agains bottom half ints
+ * with a start/end_bh_atomic bracket. There should be no
+ * need to mask interrupts on hardware level.
+ */
+void ax25_remove_cb(ax25_cb *ax25)
+{
+ /*
+ * unbound sockets are not in any list
+ */
+
+ if (!ax25->inserted)
+ return;
+
+ if (ax25->device != NULL) {
+ ax25_dev_remove_cb(ax25);
+ return;
+ }
+
+
+ if (ax25_list != NULL) {
+ write_lock(&ax25_list_lock);
+ if (ax25->prev == NULL)
+ ax25_list = ax25->next;
+ else
+ ax25->prev->next = ax25->next;
+
+ if (ax25->next != NULL)
+ ax25->next->prev = ax25->prev;
+ write_unlock(&ax25_list_lock);
+ }
+
+ ax25->inserted = 0;
+
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Find an AX.25 control block given both ends.
+ */
+ax25_cb *ax25_find_cb(ax25_addr_t *addr, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ read_lock(&ax25_list_lock);
+ for (s = ax25_dev_list(dev) ; s != NULL; s = s->next) {
+
+ if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
+ continue;
+
+ if (s->addr.dcount == addr->dcount && !ax25cmp(&s->addr.src, &addr->src)
+ && !ax25cmp(&s->addr.dest, &addr->dest))
+ {
+ int i;
+
+ if (addr->dcount == 0)
+ break;
+ if (addr->lastrepeat != s->addr.lastrepeat)
+ continue;
+ i = addr->dcount;
+ while (i--) {
+ if (ax25cmp(&s->addr.digipeater[i], &addr->digipeater[i]))
+ break;
+ }
+ if (i < 0)
+ break;
+ }
+ }
+ read_unlock(&ax25_list_lock);
+ return s;
+}
+
+/* ---------------------------------------------------------------------*/
+
+void ax25_destroy_cb(ax25_cb *ax25)
+{
+ ax25_remove_cb(ax25);
+ ax25_clear_queues(ax25);
+ ax25_free_cb(ax25);
+}
+
+/* ---------------------------------------------------------------------*/
+
+void ax25_destroy_socket(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ /*
+ * this may be a pending SABM, waiting in the receive queue
+ * to become accepted. But the listener just died :-)
+ */
+ if (skb->sk != sk) {
+ /*
+ * signal the peer that we have closed the socket.
+ * if he has already disconnected himself, just mark
+ * the socket dead and move to TCP_CLOSE. This is only
+ * for security, as ax25_disconnect should have already
+ * done this.
+ */
+ if (skb->sk->state == TCP_ESTABLISHED)
+ skb->sk->protinfo.ax25->condition |= AX25_COND_RELEASE;
+ else {
+ printk(KERN_DEBUG "ax25_destroy_socket: TCP_CLOSE\n");
+ skb->sk->state = TCP_CLOSE;
+ }
+ skb->sk->dead = 1;
+ }
+ kfree_skb(skb);
+ }
+ sk_free(sk);
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Fill in a created AX.25 control block with the default
+ * values for a particular device.
+ */
+void ax25_fillin_cb(ax25_cb *ax25, struct net_device *dev)
+{
+ ax25->device = dev;
+ ax25->vs_rtt = -1;
+
+ if (dev != NULL) {
+ ax25->rtt = ax25_dev_get_value(dev, AX25_VALUES_T1) / 4;
+ ax25->t1 = ax25_dev_get_value(dev, AX25_VALUES_T1);
+ ax25->t2 = ax25_dev_get_value(dev, AX25_VALUES_T2);
+ ax25->t3 = ax25_dev_get_value(dev, AX25_VALUES_T3);
+ ax25->n2 = ax25_dev_get_value(dev, AX25_VALUES_N2);
+ ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
+ ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
+ ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF);
+
+ if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
+ } else {
+ ax25->rtt = AX25_DEF_T1 / 4;
+ ax25->t1 = AX25_DEF_T1;
+ ax25->t2 = AX25_DEF_T2;
+ ax25->t3 = AX25_DEF_T3;
+ ax25->n2 = AX25_DEF_N2;
+ ax25->paclen = AX25_DEF_PACLEN;
+ ax25->idle = AX25_DEF_IDLE;
+ ax25->backoff = AX25_DEF_BACKOFF;
+
+ if (AX25_DEF_AXDEFMODE) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = AX25_DEF_EWINDOW;
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = AX25_DEF_WINDOW;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Create an empty AX.25 control block.
+ */
+ax25_cb *ax25_create_cb(void)
+{
+ ax25_cb *ax25;
+
+ if ((ax25 = (ax25_cb *)kmalloc(sizeof(ax25_cb), GFP_ATOMIC)) == NULL)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+
+ memset(ax25, 0x00, sizeof(ax25_cb));
+
+ skb_queue_head_init(&ax25->write_queue);
+ skb_queue_head_init(&ax25->frag_queue);
+ skb_queue_head_init(&ax25->ack_queue);
+ skb_queue_head_init(&ax25->rcv_queue);
+
+ ax25->state = AX25_LISTEN;
+ ax25->condition = AX25_COND_SETUP;
+
+ return ax25;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Free an allocated ax25 control block. This is done to centralise
+ * the MOD count code.
+ */
+void ax25_free_cb(ax25_cb *ax25)
+{
+ if (ax25->slcomp != NULL) {
+ axhc_free(ax25->slcomp);
+ }
+
+ if (ax25->peer != NULL) {
+ ax25->peer->peer = NULL;
+ }
+
+ kfree(ax25);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/* ---------------------------------------------------------------------*/
+
+void ax25_free_sock(struct sock *sk)
+{
+ if (sk->protinfo.ax25 != NULL)
+ ax25_free_cb(sk->protinfo.ax25);
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Given an AX.25 address pull of to, from, digi list, command/response and the start of data
+ *
+ */
+unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_pktinfo *pkt_info)
+{
+ int d = 0;
+
+ if (len < 15)
+ return NULL;
+
+ pkt_info->cmdrsp = 0;
+
+ memcpy(&pkt_info->addr.dest, buf, AX25_ADDR_LEN);
+
+ if (buf[6] & AX25_CBIT)
+ pkt_info->cmdrsp = AX25_COMMAND;
+ buf += AX25_ADDR_LEN;
+ len -= AX25_ADDR_LEN;
+
+ memcpy(&pkt_info->addr.src, buf, AX25_ADDR_LEN);
+
+ if (buf[6] & AX25_CBIT)
+ pkt_info->cmdrsp = AX25_RESPONSE;
+
+ pkt_info->dama = !(buf[6] & AX25_DAMA_FLAG);
+
+ pkt_info->addr.lastrepeat = -1;
+ pkt_info->addr.dcount = 0;
+
+ while (!(buf[6] & AX25_EBIT)) {
+ buf += AX25_ADDR_LEN;
+ len -= AX25_ADDR_LEN;
+
+ if (d < AX25_MAX_DIGIS && len >= 7) {
+ memcpy(&pkt_info->addr.digipeater[d], buf, AX25_ADDR_LEN);
+ if (buf[6] & AX25_HBIT)
+ pkt_info->addr.lastrepeat = d;
+ ++d;
+ pkt_info->addr.dcount = d;
+ } else
+ return NULL;
+ }
+
+ return buf + AX25_ADDR_LEN;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Assemble an AX.25 header from the bits
+ */
+int ax25_build_addr(unsigned char *buf, ax25_addr_t *addr, int flag, int seqmask)
+{
+ int len = 0;
+ int ct = 0;
+
+ memcpy(buf, &addr->dest, AX25_ADDR_LEN);
+ buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+ buf[6] |= AX25_SSSID_SPARE;
+
+ if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+
+ memcpy(buf, &addr->src, AX25_ADDR_LEN);
+ buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+ buf[6] &= ~AX25_SSSID_SPARE;
+
+ if (seqmask == AX25_SEQMASK)
+ buf[6] |= AX25_SSSID_SPARE;
+ else
+ buf[6] |= AX25_ESSID_SPARE;
+
+ if (flag == AX25_RESPONSE)
+ buf[6] |= AX25_CBIT;
+
+ /*
+ * Fast path the normal digiless path
+ */
+ if (addr->dcount == 0) {
+ buf[6] |= AX25_EBIT;
+ return 2 * AX25_ADDR_LEN;
+ }
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+
+ while (ct < addr->dcount) {
+ memcpy(buf, &addr->digipeater[ct], AX25_ADDR_LEN);
+
+ if (ct <= addr->lastrepeat)
+ buf[6] |= AX25_HBIT;
+ else
+ buf[6] &= ~AX25_HBIT;
+
+ buf[6] &= ~AX25_EBIT;
+ buf[6] |= AX25_SSSID_SPARE;
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+ ct++;
+ }
+
+ buf[-1] |= AX25_EBIT;
+
+ return len;
+}
+
+/* ---------------------------------------------------------------------*/
+
+int ax25_sizeof_addr(ax25_addr_t *addr)
+{
+ return AX25_ADDR_LEN * (addr->dcount+2);
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Invert AX.25 address. May not pass both parameters as same struct
+ */
+void ax25_invert_addr(ax25_addr_t *in, ax25_addr_t *out)
+{
+ ax25_address *ip, *op;
+ int dcount;
+
+ dcount = out->dcount = in->dcount;
+ out->lastrepeat = dcount - in->lastrepeat - 2;
+
+ /* source/destination */
+ out->dest = in->src;
+ out->src = in->dest;
+
+ /* Invert the digipeaters */
+ if (dcount) {
+ ip = in->digipeater;
+ op = out->digipeater + (dcount-1); /* pointer scaled! */
+ while (dcount--)
+ *op-- = *ip++;
+ }
+}
diff --git a/net/ax25/ax25_core.h b/net/ax25/ax25_core.h
new file mode 100644
index 000000000..bbb31183f
--- /dev/null
+++ b/net/ax25/ax25_core.h
@@ -0,0 +1,21 @@
+#ifndef _AX25_CORE_H
+#define _AX25_CORE_H
+
+extern char* ax2asc(ax25_address*);
+extern int ax25cmp(ax25_address*, ax25_address*);
+extern ax25_address* asc2ax(char*);
+extern void ax25_insert_cb(ax25_cb*);
+extern void ax25_remove_cb(ax25_cb*);
+extern ax25_cb* ax25_find_cb(ax25_addr_t*, struct net_device*);
+extern void ax25_destroy_cb(ax25_cb*);
+extern void ax25_destroy_socket(struct sock*);
+extern void ax25_fillin_cb(ax25_cb*, struct net_device*);
+extern ax25_cb* ax25_create_cb(void);
+extern void ax25_free_cb(ax25_cb*);
+extern void ax25_free_sock(struct sock*);
+extern int ax25_build_addr(unsigned char*, ax25_addr_t*, int, int);
+extern int ax25_sizeof_addr(ax25_addr_t*);
+extern void ax25_invert_addr(ax25_addr_t*, ax25_addr_t*);
+
+extern ax25_address null_ax25_address;
+#endif
diff --git a/net/ax25/ax25_ctl.c b/net/ax25/ax25_ctl.c
new file mode 100644
index 000000000..da4cd49c8
--- /dev/null
+++ b/net/ax25/ax25_ctl.c
@@ -0,0 +1,133 @@
+/*
+ * ax25_ctl.c: Implements ioctl()s on persisting VC sockets (NEW-AX.25)
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Joerg (DL1BKE)
+ *
+ * Comment:
+ *
+ * Changelog:
+ *
+ * License: This module 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.
+ */
+
+#include <asm/uaccess.h>
+#include <linux/ax25.h>
+#include <net/ax25.h>
+
+#include "af_ax25.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
+
+/*
+ * dl1bke 960311: set parameters for existing AX.25 connections,
+ * includes a KILL command to abort any connection.
+ * VERY useful for debugging ;-)
+ */
+int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
+{
+ struct ax25_ctl_struct ax25_ctl;
+ struct net_device *dev;
+ ax25_cb *ax25;
+ ax25_addr_t addr;
+ int err;
+
+ if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0)
+ return err;
+
+ if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))
+ return -EFAULT;;
+
+ if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL)
+ return -ENODEV;
+
+ addr.src = ax25_ctl.source_addr;
+ addr.dest = ax25_ctl.dest_addr;
+ addr.dcount = 0;
+
+ if ((ax25 = ax25_find_cb(&addr, dev)) == NULL)
+ return -ENOTCONN;
+
+ switch (ax25_ctl.cmd) {
+ case AX25_KILL:
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, ENETRESET);
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, ENETRESET);
+ break;
+
+ case AX25_WINDOW:
+ if (ax25->seqmask == AX25_SEQMASK) {
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
+ return -EINVAL;
+ } else {
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
+ return -EINVAL;
+ }
+ ax25->window = ax25_ctl.arg;
+ break;
+
+ case AX25_T1:
+ if (ax25_ctl.arg < 1)
+ return -EINVAL;
+ ax25->rtt = (ax25_ctl.arg * AX25_TICS) / 4;
+ ax25->t1 = ax25_ctl.arg;
+ write_lock(&ax25->timer_lock);
+ if (ax25->wrt_timer > ax25->t1)
+ ax25->wrt_timer = ax25->t1;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_T2:
+ if (ax25_ctl.arg < 0)
+ return -EINVAL;
+ ax25->t2 = ax25_ctl.arg;
+ write_lock(&ax25->timer_lock);
+ if (ax25->ack_timer > ax25->t2)
+ ax25->ack_timer = ax25->t2;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_N2:
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
+ return -EINVAL;
+ ax25->n2count = 0;
+ ax25->n2 = ax25_ctl.arg;
+ break;
+
+ case AX25_T3:
+ if (ax25_ctl.arg < 0)
+ return -EINVAL;
+ ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ;
+ write_lock(&ax25->timer_lock);
+ if (ax25->wrt_timer != 0)
+ ax25->wrt_timer = ax25->t3;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_IDLE:
+ if (ax25_ctl.arg < 0)
+ return -EINVAL;
+ ax25->idle = ax25_ctl.arg * AX25_SLOWHZ;
+ write_lock(&ax25->timer_lock);
+ if (ax25->idletimer != 0)
+ ax25->idletimer = ax25->idle;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_PACLEN:
+ if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
+ return -EINVAL;
+ ax25->paclen = ax25_ctl.arg;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/net/ax25/ax25_ctl.h b/net/ax25/ax25_ctl.h
new file mode 100644
index 000000000..10a41d991
--- /dev/null
+++ b/net/ax25/ax25_ctl.h
@@ -0,0 +1,6 @@
+#ifndef _AX25_CTL_H
+#define _AX25_CTL_H
+
+extern int ax25_ctl_ioctl(const unsigned int, void*);
+
+#endif
diff --git a/net/ax25/ax25_ddi.c b/net/ax25/ax25_ddi.c
new file mode 100644
index 000000000..594484c77
--- /dev/null
+++ b/net/ax25/ax25_ddi.c
@@ -0,0 +1,1186 @@
+/*
+ * ax25_ddi.c: Device Driver Independent Module for NEW-AX.25
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ *
+ * Comment: Contains device driver interface, scheduler, channel arbitration
+ * LAPB state machine is synchronized here to avoid race conditions
+ * Written from scratch by Matthias Welwarsky in 1998.
+ *
+ * Changelog:
+ *
+ * License: This module 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.
+ */
+
+#include <linux/config.h>
+#include <linux/netdevice.h>
+#include <linux/tqueue.h>
+#include <linux/time.h>
+#include <linux/spinlock.h>
+
+#include <net/tcp.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "af_ax25.h"
+#include "ax25_ddi.h"
+#include "ax25_core.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_route.h"
+#include "ax25_timer.h"
+
+struct net_device *ax25_devices[AX25_MAX_DEVICES];
+rwlock_t ax25_dev_lock = RW_LOCK_UNLOCKED;
+
+
+
+/*
+ * ------------------------------------------------------------------------
+ * declaration of private functions
+ * ------------------------------------------------------------------------
+ */
+
+static void clear_ax25devices(void);
+static void ax25_dev_timer(unsigned long);
+static void ax25_dev_tic(unsigned long);
+static void ax25_transmit_buffer(ax25_cb*, struct sk_buff*, int);
+static void ax25_send_iframe(ax25_cb*, struct sk_buff*, int);
+static void ax25_send_control(ax25_cb*, int, int, int);
+static void ax25_kick_device(struct ax25_dev*);
+static __inline__ void ax25_dev_add_ready(struct ax25_dev *, ax25_cb *);
+static __inline__ void ax25_dev_remove_active(struct ax25_dev *);
+static __inline__ void ax25_dev_remove_ready(struct ax25_dev *, ax25_cb *);
+static void ax25_dev_set_tic(struct ax25_dev *);
+static void ax25_dev_set_timer(struct ax25_dev *, unsigned int);
+static void ax25_queue_xmit(struct sk_buff *);
+static struct ax25_dev *ax25_dev_get_dev(struct net_device *);
+
+/*
+ * ------------------------------------------------------------------------
+ * Interface implementation
+ * All public functions of this module are defined here
+ * ------------------------------------------------------------------------
+ */
+
+void ax25_ddi_init(void)
+{
+ clear_ax25devices();
+}
+
+/*
+ * queue a fully assembled frame in the unproto queue of the
+ * device and mark the channel ready for transmission
+ */
+void ax25_send_unproto(struct sk_buff* skb, struct net_device* dev)
+{
+ struct ax25_dev* ax25_device = AX25_PTR(dev);
+
+ skb->dev = dev;
+ skb_queue_tail(&ax25_device->unproto_queue, skb);
+ ax25_kick_device(ax25_device);
+}
+
+void ax25_send_broadcast(struct sk_buff *skb)
+{
+ int i;
+
+ read_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ struct net_device *dev = ax25_devices[i];
+
+ if (dev != NULL && (dev->flags & (IFF_UP|IFF_BROADCAST)) != 0) {
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+ if (newskb != NULL)
+ ax25_send_unproto(newskb, dev);
+ else
+ printk(KERN_ERR "ax25_send_broadcast: unable to clone packet.\n");
+ }
+ }
+ read_unlock(&ax25_dev_lock);
+
+ /* caller frees original packet */
+}
+
+/*
+ * put a connection on the ready list of it's device and mark the device
+ * ready for transmission.
+ */
+void ax25_kick(ax25_cb *ax25)
+{
+ if (ax25->device != NULL) {
+ struct ax25_dev *ax25_device = AX25_PTR(ax25->device);
+
+ /*
+ * put the connection on the readylist of this channel,
+ * if it's not already there.
+ */
+ ax25_dev_add_ready(ax25_device, ax25);
+
+ /*
+ * mark the channel ready
+ */
+ ax25_kick_device(ax25_device);
+ }
+}
+
+/*
+ * return the connection list to a given device
+ */
+ax25_cb *ax25_dev_list(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device;
+
+ if (dev == NULL)
+ return ax25_list;
+
+ if ((ax25_device = AX25_PTR(dev)) != NULL && ax25_device->magic == AX25_DEV_MAGIC)
+ return ax25_device->list.all;
+
+ return NULL;
+}
+
+/*
+ * insert a connection into a device queue
+ */
+void ax25_dev_insert_cb(ax25_cb *ax25)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(ax25->device);
+
+ if (ax25_device->magic != AX25_DEV_MAGIC) {
+ printk(KERN_ERR "ax25_dev_insert_cb: wrong magic number.\n");
+ return;
+ }
+
+ ax25->prev = NULL;
+ ax25->next = ax25_device->list.all;
+
+ write_lock(&ax25_dev_lock);
+ if (ax25_device->list.all != NULL)
+ ax25_device->list.all->prev = ax25;
+ ax25_device->list.all = ax25;
+ write_unlock(&ax25_dev_lock);
+
+ ax25->inserted = 1;
+}
+
+/*
+ * remove a connection from a device queue
+ */
+void ax25_dev_remove_cb(ax25_cb *ax25)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(ax25->device);
+ struct net_device *dev = ax25->device;
+ struct ax25_cb *axp;
+
+ if (ax25_device->magic != AX25_DEV_MAGIC) {
+ printk(KERN_ERR "ax25_dev_remove_cb: wrong magic number.\n");
+ return;
+ }
+
+ if (ax25_device->list.all == NULL) {
+ printk(KERN_ERR "ax25_dev_remove_cb: empty list.\n");
+ return;
+ }
+
+ write_lock(&ax25_dev_lock);
+ if (ax25->prev == NULL) {
+ ax25_device->list.all = ax25->next;
+ } else {
+ ax25->prev->next = ax25->next;
+ }
+
+ if (ax25->next != NULL)
+ ax25->next->prev = ax25->prev;
+
+ ax25->inserted = 0;
+
+ if (xchg(&ax25->ready.state, AX25_SCHED_IDLE) == AX25_SCHED_READY)
+ ax25_dev_remove_ready(ax25_device, ax25);
+
+ /* search for active circuits and set DAMA flag accordingly */
+ for (axp=ax25_device->list.all; axp!=NULL; axp=axp->next)
+ if ((axp->state == AX25_STATE_3) || (axp->state == AX25_STATE_4)) break;
+ if (axp == NULL) ax25_dev_set_dama(dev, 0);
+
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * Look for any matching address.
+ */
+int ax25_dev_match_addr(ax25_address *addr, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ for (s = ax25_dev_list(dev); s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Find a control block that wants to accept the SABM we have just
+ * received.
+ */
+ax25_cb *ax25_dev_find_listener(ax25_address *addr, int digi, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ read_lock(&ax25_dev_lock);
+ for (s = ax25_dev_list(dev); s != NULL; s = s->next) {
+ if (s->state != AX25_LISTEN)
+ continue;
+ if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
+ continue;
+ if (ax25cmp(&s->addr.src, addr) == 0)
+ break;
+ }
+ read_unlock(&ax25_dev_lock);
+ return s;
+}
+
+/*
+ * Find an AX.25 socket given both ends.
+ */
+struct sock *ax25_dev_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type)
+{
+ ax25_cb *s;
+
+ read_lock(&ax25_dev_lock);
+ for (s = ax25_dev_list(dev); s != NULL; s = s->next) {
+ if (s->sk != NULL && ax25cmp(&s->addr.src, my_addr) == 0
+ && ax25cmp(&s->addr.dest, dest_addr) == 0 && s->sk->type == type) {
+ read_unlock(&ax25_dev_lock);
+ return s->sk;
+ }
+ }
+ read_unlock(&ax25_dev_lock);
+ return NULL;
+}
+
+/*
+ * This function is called whenever a parameter is modified using
+ * ax25_dev_set_value_notify or via the proc/sysctl interface. It
+ * decides whether to notify the device driver of the event. If the
+ * decision is positive, it uses the parameter_change downcall.
+ * The driver can then react and re-set the value or pick the
+ * closest value the hardware allows (e.g. by baud rate divider etc.).
+ * The most important values for the device driver are duplex, txdelay,
+ * txtail, {tx,rx}bitrate. Slottime and p-persistence are currently
+ * only "for info" since channel arbitration is done by DDI layer now.
+ */
+void ax25_notify_dispatcher(struct net_device *dev, int id, int oldval, int newval)
+{
+ struct ax25_dev *ax_dev;
+
+ if (!dev) return; /* paranoia */
+ ax_dev = AX25_PTR(dev);
+ if (!ax_dev) return; /* paranoia */
+
+ switch (id) {
+ case AX25_VALUES_MEDIA_DUPLEX:
+ case AX25_VALUES_MEDIA_TXDELAY:
+ case AX25_VALUES_MEDIA_TXTAIL:
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ case AX25_VALUES_MEDIA_SLOTTIME:
+ case AX25_VALUES_MEDIA_PPERSISTENCE:
+ case AX25_VALUES_MEDIA_AUTO_ADJUST:
+ if (ax_dev->hw.parameter_change_notify) {
+ (ax_dev->hw.parameter_change_notify)(dev, id, oldval, newval);
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/*
+ * Call this function from AX.25 driver to check if driver has
+ * to be notified of the event.
+ */
+void ax25_dev_set_value_notify(struct net_device *dev, int valueno, int newvalue)
+{
+ int oldvalue;
+
+ oldvalue = ax25_dev_get_value(dev, valueno);
+ ax25_dev_set_value(dev, valueno, newvalue);
+ if (oldvalue != newvalue)
+ ax25_notify_dispatcher(dev, valueno, oldvalue, newvalue);
+}
+
+/*
+ * This is called when an interface is brought up. These are
+ * reasonable defaults. We try not to mess with the media parameters
+ * if they appear as having been set already.
+ */
+void ax25_dev_device_up(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+ int txbitrate;
+
+ if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC)
+ return;
+
+ ax25_device->ready_lock = RW_LOCK_UNLOCKED;
+ ax25_device->forward = NULL;
+ ax25_device->list.all = NULL;
+ ax25_device->list.ready = NULL;
+ skb_queue_head_init(&ax25_device->unproto_queue);
+ ax25_device->bytes_sent = 0;
+ ax25_device->dama_mode = 0;
+
+ ax25_dev_set_value_notify(dev, AX25_VALUES_IPDEFMODE, AX25_DEF_IPDEFMODE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_AXDEFMODE, AX25_DEF_AXDEFMODE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_BACKOFF, AX25_DEF_BACKOFF);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_CONMODE, AX25_DEF_CONMODE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_WINDOW, AX25_DEF_WINDOW);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_EWINDOW, AX25_DEF_EWINDOW);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_T1, AX25_DEF_T1);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_T3, AX25_DEF_T3);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_IDLE, AX25_DEF_IDLE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_N2, AX25_DEF_N2);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_PACLEN, AX25_DEF_PACLEN);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_PROTOCOL, AX25_DEF_PROTOCOL);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_DAMA_SLAVE_TIMEOUT, AX25_DEF_DAMA_SLAVE_TIMEOUT);
+
+ txbitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_T2,
+ txbitrate > 0 ? (3600 / AX25_TICS) * HZ / txbitrate : 0);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, AX25_DEF_MEDIA_PPERSISTENCE);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_SLOTTIME, AX25_DEF_MEDIA_SLOTTIME);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, AX25_DEF_MEDIA_AUTO_ADJUST);
+
+ init_timer(&ax25_device->timer);
+ ax25_dev_set_timer(ax25_device, AX25_TICS);
+ init_timer(&ax25_device->tics);
+ ax25_dev_set_tic(ax25_device);
+}
+
+/*
+ * this is called when a device is brought down. Delete the device
+ * timers and update the sysctl interface.
+ */
+void ax25_dev_device_down(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+
+ ax25_kill_by_device(dev);
+ ax25_rt_device_down(dev);
+
+ if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) {
+ printk(KERN_ERR "ax25_dev_device_down: not an AX.25 device.\n");
+ return;
+ }
+
+ del_timer(&ax25_device->timer);
+ del_timer(&ax25_device->tics);
+
+ /* FIXME: do I have to lock this or not? */
+ /* start_bh_atomic(); */
+ skb_queue_purge(&ax25_device->unproto_queue);
+ /* end_bh_atomic(); */
+}
+
+/*
+ * Packet forwarding control IOCTL
+ * FIXME: does anybody really need this feature?
+ */
+int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
+{
+ struct net_device *dev;
+ struct ax25_dev *ax25_dev;
+
+ if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL)
+ return -EINVAL;
+
+ if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCAX25ADDFWD:
+ if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL)
+ return -EINVAL;
+ if (ax25_dev->forward != NULL)
+ return -EINVAL;
+ ax25_dev->forward = dev;
+ break;
+
+ case SIOCAX25DELFWD:
+ if (ax25_dev->forward == NULL)
+ return -EINVAL;
+ ax25_dev->forward = NULL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct net_device *ax25_fwd_dev(struct net_device *dev)
+{
+ struct ax25_dev *ax25_dev;
+
+ if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
+ return dev;
+
+ if (ax25_dev->forward == NULL)
+ return dev;
+
+ return ax25_dev->forward;
+}
+
+int ax25_dev_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ int i;
+ struct net_device *dev;
+ char devname[7];
+
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+
+ len += sprintf(buffer, "device hwaddr rifr tifr rrej rkby tkby duplex tx-bps rx-bps ppers slot auto txd txt \n");
+
+ read_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if ((dev = ax25_devices[i]) != NULL) {
+ strncpy(devname, dev->name, 6);
+ devname[6] = 0;
+ len += sprintf(buffer+len, "%-6s %-9s %-6ld %-6ld %-6ld %-9ld %-9ld %-6s %-8d %-8d %-5d %-4d %-4s %-4d %-4d\n",
+ devname, ax2asc((ax25_address *)dev->dev_addr),
+ AX25_PTR(dev)->rx_iframes, AX25_PTR(dev)->tx_iframes,
+ AX25_PTR(dev)->rx_rejects,
+ AX25_PTR(dev)->rx_bytes/1024,
+ AX25_PTR(dev)->tx_bytes/1024,
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX) ? "full" : "half",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST) ? "on" : "off",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL));
+
+ pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ }
+ }
+ read_unlock(&ax25_dev_lock);
+
+ *start = buffer + (offset - begin);
+ len -= offset - begin;
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+/*
+ * This function is called by core/dev.c whenever a new netdevice is
+ * being registerd. We initialize its ax25_dev structure and include
+ * it in our list. We also register the sysctl tree for it and initialize
+ * its parameters.
+ */
+void register_ax25device(struct net_device *dev)
+{
+ int i;
+ struct ax25_dev *axdev = AX25_PTR(dev);
+
+ axdev->magic = AX25_DEV_MAGIC;
+ axdev->netdev = dev;
+
+ memcpy((char *) dev->broadcast, (char *) asc2ax("QST-0"), AX25_ADDR_LEN);
+
+ ax25_unregister_sysctl();
+ write_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if (ax25_devices[i] == NULL) {
+ ax25_devices[i] = dev;
+ break;
+ }
+ }
+
+ ax25_register_sysctl();
+ if (i == AX25_MAX_DEVICES) {
+ printk(KERN_ERR "AX.25: Too many devices, could not register.\n");
+ goto done;
+ }
+
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXDELAY, AX25_DEF_MEDIA_TXDELAY);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXTAIL, AX25_DEF_MEDIA_TXTAIL);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXBITRATE, AX25_DEF_MEDIA_TXBITRATE);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_RXBITRATE, AX25_DEF_MEDIA_RXBITRATE);
+ /*
+ * slottime, p-persistence and auto-adjust defaults are
+ * loaded upon interface start
+ */
+
+done:
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * This function is executed when an interface is about to be removed.
+ * It must already have been downed before. We remove it from our
+ * list and remove sysctl directory entry.
+ */
+void unregister_ax25device(struct net_device *dev)
+{
+ int i;
+
+ ax25_unregister_sysctl();
+ write_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if (ax25_devices[i] == dev) {
+ ax25_devices[i] = NULL;
+ break;
+ }
+ }
+ write_unlock(&ax25_dev_lock);
+ ax25_register_sysctl();
+}
+
+/*
+ * Activate/Deactivate DAMA on a given interface.
+ * We automagically configure the media for full duplex if neccessary.
+ */
+void ax25_dev_set_dama(struct net_device *dev, int dama)
+{
+ if (dama && (ax25_dev_get_value(dev, AX25_VALUES_PROTOCOL) == 1)) {
+ if (!(AX25_PTR(dev)->dama_mode & DAMA_SLAVE)) {
+ AX25_PTR(dev)->dama_mode |= DAMA_SLAVE;
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 1);
+ }
+ } else {
+ if (AX25_PTR(dev)->dama_mode & DAMA_SLAVE) {
+ AX25_PTR(dev)->dama_mode &= ~DAMA_SLAVE;
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+ }
+ }
+ return;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ * End of public area, all private functions of this module are defined
+ * here.
+ * ------------------------------------------------------------------------
+ */
+
+static void clear_ax25devices(void)
+{
+ int i;
+
+ write_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++)
+ ax25_devices[i] = NULL;
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * simple pseudo-random number generator, stolen from hdlcdrv.c :)
+ */
+static inline unsigned short random_num(void)
+{
+ static unsigned short random_seed;
+
+ random_seed = 28629 * random_seed + 157;
+ return random_seed & 0xFF;
+}
+
+/*
+ * add a connection to the channels readylist
+ */
+static inline void ax25_dev_add_ready(struct ax25_dev *ax25_device, ax25_cb *ax25)
+{
+ write_lock(&ax25_device->ready_lock);
+ if (ax25->ready.state != AX25_SCHED_READY) {
+ ax25->ready.state = AX25_SCHED_READY;
+ if (ax25_device->list.ready == NULL) {
+ ax25->ready.prev = ax25;
+ ax25->ready.next = ax25;
+ ax25_device->list.ready = ax25;
+ } else {
+ ax25->ready.next = ax25_device->list.ready;
+ ax25->ready.prev = ax25_device->list.ready->ready.prev;
+ ax25_device->list.ready->ready.prev->ready.next = ax25;
+ ax25_device->list.ready->ready.prev = ax25;
+ }
+ }
+ write_unlock(&ax25_device->ready_lock);
+}
+
+/*
+ * remove the active connection from the channels readylist
+ * NB: caller must do write_lock() on ax25_device->ready_lock!
+ */
+static inline void ax25_dev_remove_active(struct ax25_dev *ax25_device)
+{
+ ax25_cb *active = ax25_device->list.ready;
+ if (active->ready.next == active) {
+ ax25_device->list.ready = NULL;
+ } else {
+ ax25_device->list.ready = active->ready.next;
+ active->ready.next->ready.prev = active->ready.prev;
+ active->ready.prev->ready.next = active->ready.next;
+ }
+ active->ready.state = AX25_SCHED_IDLE;
+}
+
+/*
+ * remove a connection from the channels readylist
+ */
+static inline void ax25_dev_remove_ready(struct ax25_dev *ax25_device, ax25_cb *ax25)
+{
+ write_lock(&ax25_device->ready_lock);
+
+ if (ax25 == ax25_device->list.ready) {
+ ax25_dev_remove_active(ax25_device);
+ } else {
+ ax25->ready.next->ready.prev = ax25->ready.prev;
+ ax25->ready.prev->ready.next = ax25->ready.next;
+ ax25->ready.state = AX25_SCHED_IDLE;
+ }
+
+ write_unlock(&ax25_device->ready_lock);
+}
+
+/*
+ * Timer for a per device 100ms timing tic. AX.25 Timers of all
+ * connections on this device are driven by this timer.
+ */
+static void ax25_dev_set_tic(struct ax25_dev *this)
+{
+ this->tics.data = (unsigned long)this;
+ this->tics.function = &ax25_dev_tic;
+ this->tics.expires = jiffies + AX25_TICS;
+
+ add_timer(&this->tics);
+}
+
+static void ax25_dev_tic(unsigned long param)
+{
+ ax25_cb *active;
+ struct ax25_dev *this = (struct ax25_dev *) param;
+
+ if (!this->needs_transmit && ((!this->hw.ptt) || (!this->hw.ptt(this->netdev)))) {
+ for (active = this->list.all; active; active = active->next) {
+ /*
+ * only run the timer on idle connections.
+ */
+ if (!active->ready.state)
+ ax25_timer(active);
+ }
+ }
+ ax25_dev_set_tic(this);
+}
+
+/*
+ * Timer for channel access arbitration. Fires every 100ms if the channel
+ * is idle (i.e. no connections need to transmit), and in intervals of
+ * half of a frame length if trying to transmit
+ */
+static void ax25_dev_set_timer(struct ax25_dev *this, unsigned int tics)
+{
+ this->timer.data = (unsigned long)this;
+ this->timer.function = &ax25_dev_timer;
+ this->timer.expires = jiffies + tics;
+
+ add_timer(&this->timer);
+}
+
+static void ax25_dev_timer(unsigned long param)
+{
+ struct ax25_dev *this = (struct ax25_dev *) param;
+ struct net_device *dev = this->netdev;
+ ax25_cb *active;
+ struct sk_buff *skb;
+ unsigned int bytes_sent = 0;
+ unsigned int max_bytes;
+ int ppers = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE);
+ int br = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ int duplex = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX);
+ int bit_per_jiffie;
+ int jiffies_per_slot;
+
+ if (br == 0) {
+ printk(KERN_ERR "ax25_dev_timer(%s): TX-Bitrate unset!!!\n", dev->name);
+ }
+ bit_per_jiffie = br / HZ;
+ jiffies_per_slot = 1200 * HZ / br + 1;
+
+ if (this->dama_mode & DAMA_SLAVE) {
+ /* >>>>> DAMA slave <<<<<
+ *
+ * we only transmit when we are asked to do so or when
+ * T3 ran out, which should only occur if the master forgot
+ * our circuits (i.e. had a reset or is broken otherwise).
+ */
+ if (this->dama_polled) {
+ /* we have been polled, it's ok to transmit */
+ this->dama_polled = 0;
+ goto arbitration_ok;
+ } else {
+ /*
+ * we are not allowed to transmit. Maybe next time.
+ */
+ ax25_dev_set_timer(this, jiffies_per_slot);
+ return;
+ }
+ } else if (this->dama_mode & DAMA_MASTER) {
+ /* >>>>> DAMA master <<<<<
+ *
+ * insert code here
+ * this could have been your ad! :-)
+ */
+ } else {
+ /* >>>>> CSMA <<<<<
+ *
+ * this implements a rather innovative channel access method.
+ * the basic idea is to run the usual slottime/persistence
+ * scheme, but with two significant changes:
+ * 1. slottime is derived from the bitrate of the channel
+ * 2. persistence is variable, depending on the dcd pattern
+ * of the channel.
+ *
+ * "Sample the dcd in intervals of half of a frames length and
+ * - increment persistence value if dcd is inactive,
+ * - decrement persistence value if dcd is active."
+ *
+ * simulations show that this scheme gives good collision
+ * avoidance and throughput without knowledge about the
+ * dcd propagation delay and station count. It will probably
+ * perform *much* too aggressive in a hidden station environment.
+ *
+ * Note: The check for hw.fast skips the channel arbitration
+ * stuff. Set this for KISS and ethernet devices.
+ */
+ if (!this->hw.fast && !duplex && !this->hw.ptt(this->netdev)) {
+ /* decide whether this is a "good" slot or not */
+ if (random_num() < ppers) {
+ /* ok, a good one, check the dcd now */
+ if (this->hw.dcd(this->netdev)) {
+ this->dcd_memory = 1;
+ /*
+ * too bad, dcd is up. we're too aggressive,
+ * but we must wait for a falling edge of the dcd
+ * before we can decrement persistence
+ */
+ if (this->dcd_dropped && ppers > 1)
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST))
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers);
+ if (this->needs_transmit)
+ ax25_dev_set_timer(this, jiffies_per_slot);
+ return;
+ }
+ /* update dcd memory */
+ this->dcd_memory = 0;
+ this->dcd_dropped = 0;
+ goto arbitration_ok;
+ } else {
+ /* a bad slot, check the dcd */
+ if (!this->hw.dcd(this->netdev)) {
+ /* um. dcd is down, we should have tx'd here. */
+ if (ppers < 128)
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST))
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers+1);
+ /* was it up the slot before? */
+ if (this->dcd_memory) {
+ this->dcd_dropped = 1;
+ }
+ this->dcd_memory = 0;
+ } else {
+ this->dcd_memory = 1;
+ }
+ if (this->needs_transmit)
+ ax25_dev_set_timer(this, jiffies_per_slot);
+ return;
+ }
+ }
+ }
+
+arbitration_ok:
+ /*
+ * OK, we may transmit, arbitration successful.
+ */
+ if (this->hw.rts) this->hw.rts(this->netdev);
+
+ /*
+ * compute the amount of bytes to send during 100ms (AX25_TICS)
+ */
+ max_bytes = (bit_per_jiffie * AX25_TICS);
+
+ /*
+ * UI Frames
+ */
+ while ((bytes_sent < max_bytes || this->hw.fast)
+ && ((skb = skb_dequeue(&this->unproto_queue)) != NULL)) {
+ ax25_queue_xmit(skb);
+ bytes_sent += skb->len;
+ }
+
+ /*
+ * traverse our list of connections. we're messing with a
+ * private list here and we will not sleep and schedule, so no
+ * further protection should be necessary.
+ *
+ * we implement a simple round robin style packet scheduler here.
+ * each device has a list of cnnections ready to transmit packets,
+ * and we loop through the connections until
+ * a. the list becomes empty
+ * b. the transmit time limit is reached.
+ * if a connection has no more packets left or exceeds its window
+ * of outbound packets, it is removed from the list.
+ */
+ while ((active = this->list.ready) != NULL && ((bytes_sent < max_bytes) || this->hw.fast)) {
+ unsigned short start;
+ unsigned short end;
+ struct sk_buff *skbn;
+ ax25_cb *peer;
+ int in_retransmit = 0;
+
+ skbn = skb_peek(&active->ack_queue);
+
+ /* transmit supervisory stuff first */
+ if (active->tx_rsp) {
+ int poll_bit = active->tx_rsp & 0x100;
+ int frametype = active->tx_rsp & 0x0ff;
+ active->tx_rsp = 0;
+ ax25_send_control(active, frametype, poll_bit, AX25_RESPONSE);
+
+
+ /*
+ * supervisory stuff is all done, clear state-change flag
+ */
+ ax25_clr_cond(active, AX25_COND_STATE_CHANGE);
+
+ if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */
+ active->ack_timer = 0;
+ ax25_clr_cond(active, AX25_COND_ACK_PENDING);
+ }
+ }
+
+ if (active->tx_cmd) {
+ int poll_bit = active->tx_cmd & 0x100;
+ int frametype = active->tx_cmd & 0x0ff;
+
+ active->tx_cmd = 0;
+
+ /*
+ * a problem exists due to a race condition between linux'
+ * packet-scheduler and the timer routine: a write timeout might
+ * happen before the packet actually reaches the device and is copied
+ * for transmission. our transmit routine will then grab the first
+ * packet off the ack queue, put a header in front of the data and
+ * queue it for transmission. now we have the obscure situation that
+ * we have two packets in our transmit queue that share a single data
+ * segment. this isn't bad by itself, but since the first
+ * retransmitted frame will have the poll bit set and eventually will
+ * carry an updated N(r), we modify the header of a yet unsent packet,
+ * resulting in a protocol violation.
+ *
+ * we do the obvious thing to prevent this here: if the packet we
+ * got from the ack queue is cloned, we make a private copy of the
+ * data.
+ */
+ if (poll_bit && skbn
+ && frametype == AX25_RR
+ && !(active->condition & (AX25_COND_PEER_RX_BUSY|AX25_COND_STATE_CHANGE))
+ && active->n2count < 4)
+ {
+ if (skb_cloned(skbn)) {
+ skb = skb_copy(skbn, GFP_ATOMIC);
+ } else
+ skb = skb_clone(skbn, GFP_ATOMIC);
+ if (skb) {
+ active->vs = active->va;
+ ax25_send_iframe(active, skb, AX25_POLLON);
+ active->vs = active->vs_max;
+ }
+ } else {
+ ax25_send_control(active, frametype, poll_bit, AX25_COMMAND);
+ }
+ /*
+ * supervisory stuff is all done, clear state-change flag
+ */
+ ax25_clr_cond(active, AX25_COND_STATE_CHANGE);
+ if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */
+ active->ack_timer = 0;
+ ax25_clr_cond(active, AX25_COND_ACK_PENDING);
+ }
+ }
+
+ /*
+ * if the write queue and ack queue are both empty,
+ * or connection is not in info transfer state
+ * or the peer station is busy
+ * or the window is closed
+ * or the write queue is empty and we may not retransmit yet
+ * then remove connection from the devices' readylist;
+ *
+ * NOTE: ax25_dev_remove_active implicitly advances the
+ * round robin pointer to schedule the next connection
+ * on the readylist.
+ */
+ skb = skb_peek(&active->write_queue);
+ if ((skb == NULL && skbn == NULL)
+ || active->state != AX25_STATE_3
+ || (active->condition & AX25_COND_PEER_RX_BUSY) != 0
+ || (start = active->vs) == (end = (active->va + active->window) & active->seqmask)
+ || (skb == NULL && start != active->va))
+ {
+ if (active->condition & AX25_COND_START_T1) {
+ ax25_clr_cond(active, AX25_COND_START_T1);
+ write_lock(&active->timer_lock);
+ active->wrt_timer = active->t1 = ax25_calculate_t1(active);
+ write_unlock(&active->timer_lock);
+ }
+ write_lock(&this->ready_lock); /* paranoia */
+ ax25_dev_remove_active(this);
+ write_unlock(&this->ready_lock);
+ continue;
+ }
+
+ /*
+ * handle RTS/CTS handshaking. drivers can request TX-Delay
+ * by returning 0 in the cts method. Note, that the driver still
+ * has to handle handshaking itself, but it can prevent to be
+ * flooded with frames while it's not ready to send.
+ */
+ if (this->needs_transmit < AX25_TX_STATE_CTS) {
+ if (this->hw.cts == NULL || this->hw.cts(this->netdev))
+ this->needs_transmit = AX25_TX_STATE_CTS;
+ else if (this->needs_transmit == AX25_TX_STATE_RTS)
+ this->needs_transmit = AX25_TX_STATE_WAIT_CTS;
+ else
+ break;
+ }
+
+ if (skbn != NULL && start == active->va) {
+ skb = skbn;
+ in_retransmit = 1;
+ }
+
+ /*
+ * clone the buffer, put the original into the
+ * ack_queue and transmit the copy. That way the
+ * socket will be uncharged from the memory when
+ * the packet is acked, not when it's transmitted.
+ */
+ if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ break;
+
+ /* advance pointer to current connection */
+ this->list.ready = active->ready.next;
+
+ ax25_send_iframe(active, skbn, AX25_POLLOFF);
+ if (!(DAMA_STATE(active) & DAMA_SLAVE)) {
+ ax25_start_t1(active);
+ }
+
+ /* implicit ACK */
+ ax25_clr_cond(active, AX25_COND_ACK_PENDING);
+
+ if (!in_retransmit) {
+ active->vs_max = active->vs = (active->vs + 1) & active->seqmask;
+ skb_dequeue(&active->write_queue);
+ skb_queue_tail(&active->ack_queue, skb);
+
+ if (active->vs_rtt == -1) {
+ active->rtt_timestamp = jiffies;
+ active->vs_rtt = active->vs;
+ }
+
+ this->tx_iframes++;
+ this->tx_bytes += skbn->len;
+ } else {
+ active->vs = active->vs_max;
+ if (active->condition & AX25_COND_START_T1) {
+ ax25_clr_cond(active, AX25_COND_START_T1);
+ write_lock(&active->timer_lock);
+ active->wrt_timer = active->t1 = ax25_calculate_t1(active);
+ write_unlock(&active->timer_lock);
+ }
+ ax25_dev_remove_ready(this, active);
+ }
+
+ bytes_sent += skbn->len;
+
+ peer = active->peer;
+ if (peer && (peer->condition & AX25_COND_OWN_RX_BUSY)
+ && skb_queue_len(&active->write_queue) < 5)
+ {
+ ax25_clr_cond(peer, AX25_COND_OWN_RX_BUSY);
+ ax25_set_cond(peer, AX25_COND_STATE_CHANGE);
+ peer->state = AX25_STATE_4;
+ ax25_transmit_enquiry(peer);
+ }
+ }
+
+ this->bytes_sent += bytes_sent;
+
+ if (this->list.ready == NULL) {
+ this->bytes_sent = 0;
+ this->needs_transmit = AX25_TX_STATE_IDLE;
+ } else {
+ if (this->bytes_sent > this->max_bytes) {
+ this->bytes_sent = 0;
+ ax25_dev_set_timer(this, HZ/2);
+ } else
+ ax25_dev_set_timer(this, AX25_TICS);
+ }
+}
+
+/*
+ * send a control frame
+ */
+static void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+ struct net_device *dev;
+
+ if ((dev = ax25->device) == NULL)
+ return; /* Route died */
+
+ if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr) + 2, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr));
+
+ /* Assume a response - address structure for DTE */
+ if (ax25->seqmask == AX25_SEQMASK) {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= (poll_bit) ? AX25_PF : 0;
+ if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */
+ *dptr |= (ax25->vr << 5);
+ } else {
+ if ((frametype & AX25_U) == AX25_U) {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= (poll_bit) ? AX25_PF : 0;
+ } else {
+ dptr = skb_put(skb, 2);
+ dptr[0] = frametype;
+ dptr[1] = (ax25->vr << 1);
+ dptr[1] |= (poll_bit) ? AX25_EPF : 0;
+ }
+ }
+
+ skb->nh.raw = skb->data;
+ ax25_transmit_buffer(ax25, skb, type);
+ ax25->vl = ax25->vr; /* vl: last acked frame */
+}
+
+static void ax25_kick_device(struct ax25_dev* ax25_device)
+{
+ write_lock(&ax25_dev_lock);
+ if (!ax25_device->needs_transmit) {
+ ax25_device->needs_transmit = AX25_TX_STATE_RTS;
+ ax25_device->task_queue.routine = (void *) ax25_dev_timer;
+ ax25_device->task_queue.data = (void *)ax25_device;
+ ax25_device->task_queue.sync = 0;
+ queue_task(&ax25_device->task_queue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * This procedure is passed a buffer descriptor for an iframe. It builds
+ * the rest of the control part of the frame and then writes it out.
+ *
+ */
+static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
+{
+ unsigned char *frame;
+
+ skb->nh.raw = skb->data;
+
+ if (ax25->seqmask == AX25_SEQMASK) {
+ frame = skb_push(skb, 1);
+
+ *frame = AX25_I;
+ *frame |= (poll_bit) ? AX25_PF : 0;
+ *frame |= (ax25->vr << 5);
+ *frame |= (ax25->vs << 1);
+ } else {
+ frame = skb_push(skb, 2);
+
+ frame[0] = AX25_I;
+ frame[0] |= (ax25->vs << 1);
+ frame[1] = (poll_bit) ? AX25_EPF : 0;
+ frame[1] |= (ax25->vr << 1);
+ }
+
+ ax25->idletimer = ax25->idle;
+ ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
+ ax25->vl = ax25->vr; /* vl: last acked frame */
+}
+
+static void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
+{
+ unsigned char *ptr;
+
+ if (ax25->device == NULL)
+ return;
+
+ if (skb_headroom(skb) < ax25_sizeof_addr(&ax25->addr)) {
+ printk(KERN_WARNING "ax25_transmit_buffer: not enough room for digi-peaters\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ ptr = skb_push(skb, ax25_sizeof_addr(&ax25->addr));
+ ax25_build_addr(ptr, &ax25->addr, type, ax25->seqmask);
+ skb->dev = ax25->device;
+ ax25_queue_xmit(skb);
+}
+
+/* ---------------------------------------------------------------------*/
+
+/* A small shim to dev_queue_xmit to do any packet forwarding in operation. */
+static void ax25_queue_xmit(struct sk_buff *skb)
+{
+ skb->protocol = htons(ETH_P_AX25);
+ skb->dev = ax25_fwd_dev(skb->dev);
+
+ dev_queue_xmit(skb);
+}
+
+/* ---------------------------------------------------------------------*/
+
+static struct ax25_dev *ax25_dev_get_dev(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+
+ if (ax25_device == NULL)
+ return NULL;
+
+ if (ax25_device->magic == AX25_DEV_MAGIC)
+ return ax25_device;
+
+ return NULL;
+}
diff --git a/net/ax25/ax25_ddi.h b/net/ax25/ax25_ddi.h
new file mode 100644
index 000000000..acaf4aa37
--- /dev/null
+++ b/net/ax25/ax25_ddi.h
@@ -0,0 +1,39 @@
+/*
+ * Interface declaration for the DDI layer.
+ *
+ * Matthias Welwarsky (DG2FEF) 05/25/98
+ *
+ */
+
+
+#ifndef _AX25_DDI_H
+#define _AX25_DDI_H
+
+enum {
+ AX25_TX_STATE_IDLE = 0,
+ AX25_TX_STATE_RTS,
+ AX25_TX_STATE_WAIT_CTS,
+ AX25_TX_STATE_CTS
+};
+
+extern void ax25_ddi_init(void);
+extern ax25_cb* ax25_dev_list(struct net_device *);
+extern void ax25_dev_insert_cb(ax25_cb *);
+extern void ax25_dev_remove_cb(ax25_cb *);
+extern ax25_cb* ax25_dev_find_listener(ax25_address *, int, struct net_device *);
+extern struct sock* ax25_dev_find_socket(ax25_address *, ax25_address *, struct net_device *, int);
+extern int ax25_dev_match_addr(ax25_address *, struct net_device *);
+extern void ax25_kick(ax25_cb *);
+extern void ax25_send_unproto(struct sk_buff*, struct net_device*);
+extern void ax25_send_broadcast(struct sk_buff*);
+extern void ax25_dev_set_value_notify(struct net_device *dev, int, int);
+extern void ax25_dev_device_up(struct net_device *);
+extern void ax25_dev_device_down(struct net_device *);
+extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *);
+extern struct net_device* ax25_fwd_dev(struct net_device *);
+extern int ax25_dev_get_info(char*, char**, off_t, int);
+extern void ax25_notify_dispatcher(struct net_device *dev, int id, int oldval, int newval);
+extern void ax25_dev_set_dama(struct net_device *dev, int dama);
+
+extern struct net_device* ax25_devices[];
+#endif
diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c
deleted file mode 100644
index efeec64e0..000000000
--- a/net/ax25/ax25_dev.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * Other kernels modules in this kit are generally BSD derived. See the copyright headers.
- *
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_route.c.
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-
-ax25_dev *ax25_dev_list;
-
-ax25_dev *ax25_dev_ax25dev(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
-
- for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
- if (ax25_dev->dev == dev)
- return ax25_dev;
-
- return NULL;
-}
-
-ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
-{
- ax25_dev *ax25_dev;
-
- for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
- if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0)
- return ax25_dev;
-
- return NULL;
-}
-
-/*
- * This is called when an interface is brought up. These are
- * reasonable defaults.
- */
-void ax25_dev_device_up(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
- unsigned long flags;
-
- if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
- printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n");
- return;
- }
-
- ax25_unregister_sysctl();
-
- memset(ax25_dev, 0x00, sizeof(*ax25_dev));
-
- ax25_dev->dev = dev;
- ax25_dev->forward = NULL;
-
- ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
- ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
- ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF;
- ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE;
- ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW;
- ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW;
- ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1;
- ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2;
- ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3;
- ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE;
- ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2;
- ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN;
- ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL;
- ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT;
-
- save_flags(flags); cli();
- ax25_dev->next = ax25_dev_list;
- ax25_dev_list = ax25_dev;
- restore_flags(flags);
-
- ax25_register_sysctl();
-}
-
-void ax25_dev_device_down(struct net_device *dev)
-{
- ax25_dev *s, *ax25_dev;
- unsigned long flags;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return;
-
- ax25_unregister_sysctl();
-
- save_flags(flags); cli();
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- ax25_ds_del_timer(ax25_dev);
-#endif
-
- /*
- * Remove any packet forwarding that points to this device.
- */
- for (s = ax25_dev_list; s != NULL; s = s->next)
- if (s->forward == dev)
- s->forward = NULL;
-
- if ((s = ax25_dev_list) == ax25_dev) {
- ax25_dev_list = s->next;
- restore_flags(flags);
- kfree(ax25_dev);
- ax25_register_sysctl();
- return;
- }
-
- while (s != NULL && s->next != NULL) {
- if (s->next == ax25_dev) {
- s->next = ax25_dev->next;
- restore_flags(flags);
- kfree(ax25_dev);
- ax25_register_sysctl();
- return;
- }
-
- s = s->next;
- }
-
- restore_flags(flags);
- ax25_register_sysctl();
-}
-
-int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
-{
- ax25_dev *ax25_dev, *fwd_dev;
-
- if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL)
- return -EINVAL;
-
- switch (cmd) {
- case SIOCAX25ADDFWD:
- if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL)
- return -EINVAL;
- if (ax25_dev->forward != NULL)
- return -EINVAL;
- ax25_dev->forward = fwd_dev->dev;
- break;
-
- case SIOCAX25DELFWD:
- if (ax25_dev->forward == NULL)
- return -EINVAL;
- ax25_dev->forward = NULL;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-struct net_device *ax25_fwd_dev(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return dev;
-
- if (ax25_dev->forward == NULL)
- return dev;
-
- return ax25_dev->forward;
-}
-
-/*
- * Free all memory associated with device structures.
- */
-void __exit ax25_dev_free(void)
-{
- ax25_dev *s, *ax25_dev = ax25_dev_list;
-
- while (ax25_dev != NULL) {
- s = ax25_dev;
- ax25_dev = ax25_dev->next;
-
- kfree(s);
- }
-}
diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c
deleted file mode 100644
index 6c65baea5..000000000
--- a/net/ax25/ax25_ds_in.c
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c
- * Joerg(DL1BKE) Fixed it.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Joerg(DL1BKE) ax25->n2count never got reset
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/ip.h> /* For ip_rcv */
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * State machine for state 1, Awaiting Connection State.
- * The handling of the timer(s) is in file ax25_ds_timer.c.
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_SABME:
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
- break;
-
- case AX25_UA:
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_ESTABLISHED;
- /* For WAIT_SABM connections we will produce an accept ready socket here */
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- }
- ax25_dama_on(ax25);
-
- /* according to DK4EG´s spec we are required to
- * send a RR RESPONSE FINAL NR=0.
- */
-
- ax25_std_enquiry_response(ax25);
- break;
-
- case AX25_DM:
- if (pf) ax25_disconnect(ax25, ECONNREFUSED);
- break;
-
- default:
- if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 2, Awaiting Release State.
- * The handling of the timer(s) is in file ax25_ds_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_dama_off(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- case AX25_UA:
- if (pf) {
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, 0);
- }
- break;
-
- case AX25_I:
- case AX25_REJ:
- case AX25_RNR:
- case AX25_RR:
- if (pf) {
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_dama_off(ax25);
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 3, Connected State.
- * The handling of the timer(s) is in file ax25_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
-{
- int queued = 0;
-
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- if (frametype == AX25_SABM) {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- } else {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- }
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->condition = 0x00;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25_requeue_frames(ax25);
- ax25_dama_on(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, ECONNRESET);
- break;
-
- case AX25_RR:
- case AX25_RNR:
- if (frametype == AX25_RR)
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- else
- ax25->condition |= AX25_COND_PEER_RX_BUSY;
-
- if (ax25_validate_nr(ax25, nr)) {
- if (ax25_check_iframes_acked(ax25, nr))
- ax25->n2count=0;
- if (type == AX25_COMMAND && pf)
- ax25_ds_enquiry_response(ax25);
- } else {
- ax25_ds_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_REJ:
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-
- if (ax25_validate_nr(ax25, nr)) {
- if (ax25->va != nr)
- ax25->n2count=0;
-
- ax25_frames_acked(ax25, nr);
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_requeue_frames(ax25);
-
- if (type == AX25_COMMAND && pf)
- ax25_ds_enquiry_response(ax25);
- } else {
- ax25_ds_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_I:
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_ds_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- break;
- }
- if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
- ax25_frames_acked(ax25, nr);
- ax25->n2count = 0;
- } else {
- if (ax25_check_iframes_acked(ax25, nr))
- ax25->n2count = 0;
- }
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
- if (pf) ax25_ds_enquiry_response(ax25);
- break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25->vr = ns; /* ax25->vr - 1 */
- ax25->condition &= ~AX25_COND_REJECT;
- if (pf) {
- ax25_ds_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->condition |= AX25_COND_ACK_PENDING;
- ax25_start_t2timer(ax25);
- }
- }
- } else {
- if (ax25->condition & AX25_COND_REJECT) {
- if (pf) ax25_ds_enquiry_response(ax25);
- } else {
- ax25->condition |= AX25_COND_REJECT;
- ax25_ds_enquiry_response(ax25);
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- }
- }
- break;
-
- case AX25_FRMR:
- case AX25_ILLEGAL:
- ax25_ds_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
-
- default:
- break;
- }
-
- return queued;
-}
-
-/*
- * Higher level upcall for a LAPB frame
- */
-int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
-{
- int queued = 0, frametype, ns, nr, pf;
-
- frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
-
- switch (ax25->state) {
- case AX25_STATE_1:
- queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_2:
- queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_3:
- queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
- break;
- }
-
- return queued;
-}
-
diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c
deleted file mode 100644
index e3e88d771..000000000
--- a/net/ax25/ax25_ds_subr.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c.
- * Joerg(DL1BKE) Changed ax25_ds_enquiry_response(),
- * fixed ax25_dama_on() and ax25_dama_off().
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-void ax25_ds_nr_error_recovery(ax25_cb *ax25)
-{
- ax25_ds_establish_data_link(ax25);
-}
-
-/*
- * dl1bke 960114: transmit I frames on DAMA poll
- */
-void ax25_ds_enquiry_response(ax25_cb *ax25)
-{
- ax25_cb *ax25o;
-
- /* Please note that neither DK4EG´s nor DG2FEF´s
- * DAMA spec mention the following behaviour as seen
- * with TheFirmware:
- *
- * DB0ACH->DL1BKE <RR C P R0> [DAMA]
- * DL1BKE->DB0ACH <I NR=0 NS=0>
- * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
- * DL1BKE->DB0ACH <RR R F R0>
- *
- * The Flexnet DAMA Master implementation apparently
- * insists on the "proper" AX.25 behaviour:
- *
- * DB0ACH->DL1BKE <RR C P R0> [DAMA]
- * DL1BKE->DB0ACH <RR R F R0>
- * DL1BKE->DB0ACH <I NR=0 NS=0>
- * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
- *
- * Flexnet refuses to send us *any* I frame if we send
- * a REJ in case AX25_COND_REJECT is set. It is superfluous in
- * this mode anyway (a RR or RNR invokes the retransmission).
- * Is this a Flexnet bug?
- */
-
- ax25_std_enquiry_response(ax25);
-
- if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) {
- ax25_requeue_frames(ax25);
- ax25_kick(ax25);
- }
-
- if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL)
- ax25_ds_t1_timeout(ax25);
- else
- ax25->n2count = 0;
-
- ax25_start_t3timer(ax25);
- ax25_ds_set_timer(ax25->ax25_dev);
-
- for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
- if (ax25o == ax25)
- continue;
-
- if (ax25o->ax25_dev != ax25->ax25_dev)
- continue;
-
- if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) {
- ax25_ds_t1_timeout(ax25o);
- continue;
- }
-
- if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) {
- ax25_requeue_frames(ax25o);
- ax25_kick(ax25o);
- }
-
- if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
- ax25_ds_t1_timeout(ax25o);
-
- /* do not start T3 for listening sockets (tnx DD8NE) */
-
- if (ax25o->state != AX25_STATE_0)
- ax25_start_t3timer(ax25o);
- }
-}
-
-void ax25_ds_establish_data_link(ax25_cb *ax25)
-{
- ax25->condition &= AX25_COND_DAMA_MODE;
- ax25->n2count = 0;
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t3timer(ax25);
-}
-
-/*
- * :::FIXME:::
- * This is a kludge. Not all drivers recognize kiss commands.
- * We need a driver level request to switch duplex mode, that does
- * either SCC changing, PI config or KISS as required. Currently
- * this request isn't reliable.
- */
-static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param)
-{
- struct sk_buff *skb;
- unsigned char *p;
-
- if (ax25_dev->dev == NULL)
- return;
-
- if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
- return;
-
- skb->nh.raw = skb->data;
- p = skb_put(skb, 2);
-
- *p++ = cmd;
- *p++ = param;
-
- skb->dev = ax25_dev->dev;
- skb->protocol = htons(ETH_P_AX25);
-
- dev_queue_xmit(skb);
-}
-
-/*
- * A nasty problem arises if we count the number of DAMA connections
- * wrong, especially when connections on the device already existed
- * and our network node (or the sysop) decides to turn on DAMA Master
- * mode. We thus flag the 'real' slave connections with
- * ax25->dama_slave=1 and look on every disconnect if still slave
- * connections exist.
- */
-static int ax25_check_dama_slave(ax25_dev *ax25_dev)
-{
- ax25_cb *ax25;
-
- for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next)
- if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1)
- return 1;
-
- return 0;
-}
-
-void ax25_dev_dama_on(ax25_dev *ax25_dev)
-{
- if (ax25_dev == NULL)
- return;
-
- if (ax25_dev->dama.slave == 0)
- ax25_kiss_cmd(ax25_dev, 5, 1);
-
- ax25_dev->dama.slave = 1;
- ax25_ds_set_timer(ax25_dev);
-}
-
-void ax25_dev_dama_off(ax25_dev *ax25_dev)
-{
- if (ax25_dev == NULL)
- return;
-
- if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) {
- ax25_kiss_cmd(ax25_dev, 5, 0);
- ax25_dev->dama.slave = 0;
- ax25_ds_del_timer(ax25_dev);
- }
-}
-
-void ax25_dama_on(ax25_cb *ax25)
-{
- ax25_dev_dama_on(ax25->ax25_dev);
- ax25->condition |= AX25_COND_DAMA_MODE;
-}
-
-void ax25_dama_off(ax25_cb *ax25)
-{
- ax25->condition &= ~AX25_COND_DAMA_MODE;
- ax25_dev_dama_off(ax25->ax25_dev);
-}
-
diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c
deleted file mode 100644
index 3c5b2ea9c..000000000
--- a/net/ax25/ax25_ds_timer.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c.
- * Joerg(DL1BKE) Added DAMA Slave Timeout timer
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-static void ax25_ds_timeout(unsigned long);
-
-/*
- * Add DAMA slave timeout timer to timer list.
- * Unlike the connection based timers the timeout function gets
- * triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT
- * (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in
- * 1/10th of a second.
- */
-
-static void ax25_ds_add_timer(ax25_dev *ax25_dev)
-{
- struct timer_list *t = &ax25_dev->dama.slave_timer;
- t->data = (unsigned long) ax25_dev;
- t->function = &ax25_ds_timeout;
- t->expires = jiffies + HZ;
- add_timer(t);
-}
-
-void ax25_ds_del_timer(ax25_dev *ax25_dev)
-{
- if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer);
-}
-
-void ax25_ds_set_timer(ax25_dev *ax25_dev)
-{
- if (ax25_dev == NULL) /* paranoia */
- return;
-
- del_timer(&ax25_dev->dama.slave_timer);
- ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10;
- ax25_ds_add_timer(ax25_dev);
-}
-
-/*
- * DAMA Slave Timeout
- * Silently discard all (slave) connections in case our master forgot us...
- */
-
-static void ax25_ds_timeout(unsigned long arg)
-{
- ax25_dev *ax25_dev = (struct ax25_dev *) arg;
- ax25_cb *ax25;
-
- if (ax25_dev == NULL || !ax25_dev->dama.slave)
- return; /* Yikes! */
-
- if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) {
- ax25_ds_set_timer(ax25_dev);
- return;
- }
-
- for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) {
- if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE))
- continue;
-
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(ax25, ETIMEDOUT);
- }
-
- ax25_dev_dama_off(ax25_dev);
-}
-
-void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
-{
- switch (ax25->state) {
-
- case AX25_STATE_0:
- /* Magic here: If we listen() and a new link dies before it
- is accepted() it isn't 'dead' so doesn't get removed. */
- if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
- ax25_destroy_socket(ax25);
- return;
- }
- break;
-
- case AX25_STATE_3:
- /*
- * Check the state of the receive buffer.
- */
- if (ax25->sk != NULL) {
- if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) &&
- (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
- ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- break;
- }
- }
- break;
- }
-
- ax25_start_heartbeat(ax25);
-}
-
-/* dl1bke 960114: T3 works much like the IDLE timeout, but
- * gets reloaded with every frame for this
- * connection.
- */
-void ax25_ds_t3timer_expiry(ax25_cb *ax25)
-{
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, ETIMEDOUT);
-}
-
-/* dl1bke 960228: close the connection when IDLE expires.
- * unlike T3 this timer gets reloaded only on
- * I frames.
- */
-void ax25_ds_idletimer_expiry(ax25_cb *ax25)
-{
- ax25_clear_queues(ax25);
-
- ax25->n2count = 0;
- ax25->state = AX25_STATE_2;
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- ax25_stop_t3timer(ax25);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
-}
-
-/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
- * within the poll of any connected channel. Remember
- * that we are not allowed to send anything unless we
- * get polled by the Master.
- *
- * Thus we'll have to do parts of our T1 handling in
- * ax25_enquiry_response().
- */
-void ax25_ds_t1_timeout(ax25_cb *ax25)
-{
- switch (ax25->state) {
-
- case AX25_STATE_1:
- if (ax25->n2count == ax25->n2) {
- if (ax25->modulus == AX25_MODULUS) {
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25->n2count = 0;
- ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
- }
- } else {
- ax25->n2count++;
- if (ax25->modulus == AX25_MODULUS)
- ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND);
- }
- break;
-
- case AX25_STATE_2:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- }
- break;
-
- case AX25_STATE_3:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- }
- break;
- }
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
-}
diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c
deleted file mode 100644
index 15ca9c453..000000000
--- a/net/ax25/ax25_iface.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-static struct protocol_struct {
- struct protocol_struct *next;
- unsigned int pid;
- int (*func)(struct sk_buff *, ax25_cb *);
-} *protocol_list;
-
-static struct linkfail_struct {
- struct linkfail_struct *next;
- void (*func)(ax25_cb *, int);
-} *linkfail_list;
-
-static struct listen_struct {
- struct listen_struct *next;
- ax25_address callsign;
- struct net_device *dev;
-} *listen_list;
-
-int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
-{
- struct protocol_struct *protocol;
- unsigned long flags;
-
- if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
- return 0;
-#ifdef CONFIG_INET
- if (pid == AX25_P_IP || pid == AX25_P_ARP)
- return 0;
-#endif
- if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
- return 0;
-
- protocol->pid = pid;
- protocol->func = func;
-
- save_flags(flags);
- cli();
-
- protocol->next = protocol_list;
- protocol_list = protocol;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_protocol_release(unsigned int pid)
-{
- struct protocol_struct *s, *protocol = protocol_list;
- unsigned long flags;
-
- if (protocol == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (protocol->pid == pid) {
- protocol_list = protocol->next;
- restore_flags(flags);
- kfree(protocol);
- return;
- }
-
- while (protocol != NULL && protocol->next != NULL) {
- if (protocol->next->pid == pid) {
- s = protocol->next;
- protocol->next = protocol->next->next;
- restore_flags(flags);
- kfree(s);
- return;
- }
-
- protocol = protocol->next;
- }
-
- restore_flags(flags);
-}
-
-int ax25_linkfail_register(void (*func)(ax25_cb *, int))
-{
- struct linkfail_struct *linkfail;
- unsigned long flags;
-
- if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
- return 0;
-
- linkfail->func = func;
-
- save_flags(flags);
- cli();
-
- linkfail->next = linkfail_list;
- linkfail_list = linkfail;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_linkfail_release(void (*func)(ax25_cb *, int))
-{
- struct linkfail_struct *s, *linkfail = linkfail_list;
- unsigned long flags;
-
- if (linkfail == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (linkfail->func == func) {
- linkfail_list = linkfail->next;
- restore_flags(flags);
- kfree(linkfail);
- return;
- }
-
- while (linkfail != NULL && linkfail->next != NULL) {
- if (linkfail->next->func == func) {
- s = linkfail->next;
- linkfail->next = linkfail->next->next;
- restore_flags(flags);
- kfree(s);
- return;
- }
-
- linkfail = linkfail->next;
- }
-
- restore_flags(flags);
-}
-
-int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
-{
- struct listen_struct *listen;
- unsigned long flags;
-
- if (ax25_listen_mine(callsign, dev))
- return 0;
-
- if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
- return 0;
-
- listen->callsign = *callsign;
- listen->dev = dev;
-
- save_flags(flags);
- cli();
-
- listen->next = listen_list;
- listen_list = listen;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_listen_release(ax25_address *callsign, struct net_device *dev)
-{
- struct listen_struct *s, *listen = listen_list;
- unsigned long flags;
-
- if (listen == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
- listen_list = listen->next;
- restore_flags(flags);
- kfree(listen);
- return;
- }
-
- while (listen != NULL && listen->next != NULL) {
- if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
- s = listen->next;
- listen->next = listen->next->next;
- restore_flags(flags);
- kfree(s);
- return;
- }
-
- listen = listen->next;
- }
-
- restore_flags(flags);
-}
-
-int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
-{
- struct protocol_struct *protocol;
-
- for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
- if (protocol->pid == pid)
- return protocol->func;
-
- return NULL;
-}
-
-int ax25_listen_mine(ax25_address *callsign, struct net_device *dev)
-{
- struct listen_struct *listen;
-
- for (listen = listen_list; listen != NULL; listen = listen->next)
- if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
- return 1;
-
- return 0;
-}
-
-void ax25_link_failed(ax25_cb *ax25, int reason)
-{
- struct linkfail_struct *linkfail;
-
- for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
- (linkfail->func)(ax25, reason);
-}
-
-int ax25_protocol_is_registered(unsigned int pid)
-{
- struct protocol_struct *protocol;
-
- for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
- if (protocol->pid == pid)
- return 1;
-
- return 0;
-}
-
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 7c1484d6e..1e51005c4 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -1,68 +1,333 @@
/*
- * AX.25 release 037
+ * ax25_in.c: Routines for processing of incoming frames (NEW-AX.25)
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ * Jonathan (G4KLX), Alan Cox (GW4PTS), HaJo (DD8NE)
*
- * This module:
- * This module 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.
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
*
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
+ * Changelog:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from
- * the sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * Jonathan(G4KLX) Added IP mode registration.
- * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception.
- * Upgraded state machine for SABME.
- * Added arbitrary protocol id support.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * HaJo(DD8NE) Added Idle Disc Timer T5
- * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly
- * different behaviour. Fixed defrag
- * routine (I hope)
- * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed.
- * AX.25 033 Jonathan(G4KLX) Remove auto-router.
- * Modularisation changes.
- * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
- * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file.
- * Joerg(DL1BKE) Fixed DAMA Slave.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Thomas(DL9SAU) Fixed missing initialization of skb->protocol.
+ * License: This module 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.
*/
#include <linux/config.h>
-#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
-#include <linux/netfilter.h>
+#include <linux/netdevice.h>
#include <net/sock.h>
-#include <net/ip.h> /* For ip_rcv */
-#include <net/arp.h> /* For arp_rcv */
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
+#include <net/ip.h>
+#include <net/arp.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "af_ax25.h"
+#include "ax25_route.h"
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_lapb.h"
+#include "ax25_core.h"
+#include "ax25_subr.h"
+#include "ax25_netlink.h"
+
+static int ax25_rx_fragment(ax25_cb*, struct sk_buff*);
+static void ax25_decode(ax25_cb*, struct sk_buff*, ax25_pktinfo*);
+static struct sock* ax25_find_socket(ax25_address*, ax25_address*, struct net_device*, int);
+static int ax25_match_addr(ax25_address*, struct net_device*);
+/* static void ax25_send_to_raw(struct sock*, struct sk_buff*, int); */
+static int ax25_rcv(struct sk_buff*, struct net_device*, struct packet_type*);
+
+/*
+ * Packet type registration data
+ */
+struct packet_type ax25_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_AX25),*/
+ 0, /* copy */
+ ax25_rcv,
+ NULL,
+ NULL,
+};
+
+/*
+ * Higher level upcall for a LAPB frame
+ */
+int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo* pkt_info)
+{
+ struct net_device *dev = skb->dev;
+
+ ax25_decode(ax25, skb, pkt_info);
+ ax25_dev_set_dama(dev, pkt_info->dama);
+ if (pkt_info->pf && (pkt_info->cmdrsp == AX25_COMMAND))
+ AX25_PTR(dev)->dama_polled = 1;
+ return ax25_lapb_table[ax25->state](ax25, skb, pkt_info);
+}
+
+/*
+ * Resequencer
+ */
+static inline unsigned int ax25_csum_skb(struct sk_buff *skb)
+{
+ unsigned char *cp = skb->data;
+ unsigned int csum = 0;
+
+ while (cp < (skb->data + skb->len))
+ csum += *cp++;
+
+ return csum;
+}
+
+void ax25_reseq_update(ax25_cb* ax25, struct sk_buff *skb, int ns)
+{
+ if (ax25->reseq[ns].skb != NULL)
+ return;
+
+ ax25->reseq[ns].csum = ax25_csum_skb(skb);
+}
+
+int ax25_reseq_in(ax25_cb *ax25, struct sk_buff *skb, int ns, int pf)
+{
+ unsigned int csum;
+
+ /*
+ * if either
+ * - the poll flag is set
+ * - the slot is not empty
+ * - we've already seen this frame
+ * drop it
+ */
+
+ if (pf || ax25->reseq[ns].skb != NULL ||
+ ax25->reseq[ns].csum == (csum = ax25_csum_skb(skb)))
+ return 0;
+
+ /*
+ * otherwise queue it
+ */
+ ax25->reseq[ns].skb = skb;
+ ax25->reseq[ns].csum = csum;
+
+ return 1;
+}
+
+void ax25_reseq_out(ax25_cb *ax25)
+{
+ struct sk_buff *skb;
+
+ while ((skb = ax25->reseq[ax25->vr].skb) != NULL && ax25_rx_iframe(ax25, skb)) {
+ ax25->reseq[ax25->vr].skb = NULL;
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ }
+}
+
+/*
+ * This is where all valid I frames are sent to, to be dispatched to
+ * whichever protocol requires them.
+ */
+int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+{
+ ax25_cb *peer;
+ unsigned char pid = *skb->data;
+ struct sock *sk = NULL;
+ int queued = 0;
+
+ int (*func)(struct sk_buff *, ax25_cb *);
+
+ if (ax25->device) {
+ AX25_PTR(ax25->device)->rx_iframes++;
+ AX25_PTR(ax25->device)->rx_bytes += skb->len;
+ }
+
+ ax25_clr_cond(ax25, AX25_COND_SETUP);
+
+ /*
+ * don't take any more data if we're about to close
+ * the connection.
+ */
+ if (ax25->condition & AX25_COND_RELEASE) {
+ ax25_set_cond(ax25, AX25_COND_OWN_RX_BUSY);
+ kfree_skb(skb);
+ return 1;
+ }
+
+ /*
+ * we're only digipeating, don't care about pid
+ * and idle timeouts.
+ */
+ if ((peer = ax25->peer) != NULL) {
+ int max_qlen;
+
+ /*
+ * don't queue any more data if the peer is just about to
+ * be disconnected.
+ */
+ if (peer->state < AX25_STATE_3 || peer->condition & AX25_COND_RELEASE) {
+ kfree_skb(skb);
+ return 1;
+ }
+
+ skb_queue_tail(&peer->write_queue, skb);
+
+ /*
+ * check the length of write_queue, set OWN_RX_BUSY if
+ * enough queued.
+ */
+ max_qlen = peer->seqmask << 1;
+ if (max_qlen > 16)
+ max_qlen = 16;
+ if (skb_queue_len(&peer->write_queue) > max_qlen) {
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ }
+ ax25_kick(peer);
+
+ return 1;
+ }
+
+ ax25->idletimer = ax25->idle;
+
+ if (pid == AX25_P_SEGMENT) {
+ skb_pull(skb, 1);
+ queued = ax25_rx_fragment(ax25, skb);
+
+ } else if ((func = ax25_protocol_function(pid)) != NULL) {
+ skb_pull(skb, 1); /* Remove PID */
+ queued = func(skb, ax25);
+
+ } else if ((sk = ax25->sk) != NULL) {
+ struct sk_buff *oskb;
+ /*
+ * check if we have frames left in ax25->rcv_queue, these
+ * must be delivered first to maintain the sequence.
+ */
+ while ((oskb = skb_peek(&ax25->rcv_queue)) != NULL) {
+ if (sk->shutdown & RCV_SHUTDOWN)
+ break;
+ if (atomic_read(&sk->rmem_alloc) + oskb->truesize < sk->rcvbuf) {
+ skb_dequeue(&ax25->rcv_queue);
+ sock_queue_rcv_skb(sk, oskb);
+ } else
+ break;
+ }
+ if (oskb == NULL)
+ ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+
+ /*
+ * now handle the frame that has just come in.
+ */
+ if (ax25->pidincl || sk->protocol == pid) {
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ kfree_skb(skb);
+ } else if (sock_queue_rcv_skb(sk, skb) != 0) {
+ /*
+ * no space left on the socket, become busy
+ * but keep queueing up the data.
+ */
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+
+ /*
+ * don't queue an infinite amount of
+ * data, 10 frames should be enough
+ * and we'll send an RNR anyway. If
+ * the peer keeps bombing us with
+ * valid iframes, "return 0" forces
+ * the l2 to drop the frame without
+ * ACK'ng it. This will produce heavy
+ * load on the channel due to constantly
+ * retransmitted frames, but we cannot
+ * just drop the packets, the application
+ * cannot handle the data loss induced.
+ */
+ if (skb_queue_len(&ax25->rcv_queue) < ax25->seqmask)
+ skb_queue_tail(&ax25->rcv_queue, skb);
+ else
+ return 0;
+ }
+ queued = 1;
+ }
+ }
+
+ /*
+ * this is a kludge to satisfy the IMHO broken interface
+ * between the L2 and upper protocol handlers. There was no
+ * handler for the PID and the socket didn't accept the PID
+ * either, so we free the buffer and pretend to have queued
+ * it.
+ */
+ if (!queued)
+ kfree_skb(skb);
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+struct sock *ax25_make_new(struct sock *osk, struct net_device *dev)
+{
+ struct sock *sk;
+ ax25_cb *ax25;
+
+ if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL)
+ return NULL;
+
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ sk_free(sk);
+ return NULL;
+ }
+
+ switch (osk->type) {
+ case SOCK_DGRAM:
+ break;
+ case SOCK_SEQPACKET:
+ break;
+ default:
+ sk_free(sk);
+ ax25_free_cb(ax25);
+ return NULL;
+ }
+
+ sock_init_data(NULL, sk);
+
+ sk->destruct = ax25_free_sock;
+ sk->type = osk->type;
+ sk->socket = osk->socket;
+ sk->priority = osk->priority;
+ sk->protocol = osk->protocol;
+ sk->rcvbuf = osk->rcvbuf;
+ sk->sndbuf = osk->sndbuf;
+ sk->debug = osk->debug;
+ sk->state = TCP_ESTABLISHED;
+ sk->sleep = osk->sleep;
+ sk->zapped = osk->zapped;
+
+ ax25->seqmask = osk->protinfo.ax25->seqmask;
+ ax25->backoff = osk->protinfo.ax25->backoff;
+ ax25->pidincl = osk->protinfo.ax25->pidincl;
+ ax25->iamdigi = osk->protinfo.ax25->iamdigi;
+ ax25->rtt = osk->protinfo.ax25->rtt;
+ ax25->t1 = osk->protinfo.ax25->t1;
+ ax25->t2 = osk->protinfo.ax25->t2;
+ ax25->t3 = osk->protinfo.ax25->t3;
+ ax25->n2 = osk->protinfo.ax25->n2;
+ ax25->idle = osk->protinfo.ax25->idle;
+ ax25->paclen = osk->protinfo.ax25->paclen;
+ ax25->window = osk->protinfo.ax25->window;
+
+ ax25->device = dev;
+ ax25->addr = osk->protinfo.ax25->addr;
+
+ sk->protinfo.ax25 = ax25;
+ ax25->sk = sk;
+
+ return sk;
+}
/*
* Given a fragment, queue it on the fragment queue and if the fragment
@@ -83,16 +348,15 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
/* Last fragment received ? */
if (ax25->fragno == 0) {
- if ((skbn = alloc_skb(AX25_MAX_HEADER_LEN + ax25->fraglen, GFP_ATOMIC)) == NULL) {
+ if ((skbn = alloc_skb(MAX_HEADER + ax25->fraglen, GFP_ATOMIC)) == NULL) {
while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL)
kfree_skb(skbo);
return 1;
}
- skb_reserve(skbn, AX25_MAX_HEADER_LEN);
+ skb_reserve(skbn, MAX_HEADER);
- skbn->dev = ax25->ax25_dev->dev;
- skbn->h.raw = skbn->data;
+ skbn->dev = ax25->device;
skbn->nh.raw = skbn->data;
/* Copy data from the fragments */
@@ -126,360 +390,422 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
return 0;
}
+
/*
- * This is where all valid I frames are sent to, to be dispatched to
- * whichever protocol requires them.
+ * This routine is the centralised routine for parsing the control
+ * information for the different frame formats.
*/
-int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+static void ax25_decode(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt_info)
{
- int (*func)(struct sk_buff *, ax25_cb *);
- volatile int queued = 0;
- unsigned char pid;
-
- if (skb == NULL) return 0;
-
- ax25_start_idletimer(ax25);
+ unsigned char *frame;
+
+ frame = skb->data;
+ pkt_info->frametype = AX25_ILLEGAL;
+ pkt_info->ns = pkt_info->nr = pkt_info->pf = 0;
+
+ if (ax25->seqmask == AX25_SEQMASK) {
+ if ((frame[0] & AX25_S) == 0) {
+ pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */
+ pkt_info->ns = (frame[0] >> 1) & 0x07;
+ pkt_info->nr = (frame[0] >> 5) & 0x07;
+ pkt_info->pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
+ pkt_info->frametype = frame[0] & 0x0F;
+ pkt_info->nr = (frame[0] >> 5) & 0x07;
+ pkt_info->pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ pkt_info->frametype = frame[0] & ~AX25_PF;
+ pkt_info->pf = frame[0] & AX25_PF;
+ }
+ skb_pull(skb, 1);
+ } else {
+ if ((frame[0] & AX25_S) == 0) {
+ pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */
+ pkt_info->ns = (frame[0] >> 1) & 0x7F;
+ pkt_info->nr = (frame[1] >> 1) & 0x7F;
+ pkt_info->pf = frame[1] & AX25_EPF;
+ skb_pull(skb, 2);
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
+ pkt_info->frametype = frame[0] & 0x0F;
+ pkt_info->nr = (frame[1] >> 1) & 0x7F;
+ pkt_info->pf = frame[1] & AX25_EPF;
+ skb_pull(skb, 2);
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ pkt_info->frametype = frame[0] & ~AX25_PF;
+ pkt_info->pf = frame[0] & AX25_PF;
+ skb_pull(skb, 1);
+ }
+ }
+}
- pid = *skb->data;
+/* ---------------------------------------------------------------------*/
+/*
+ * Find a control block that wants to accept the SABM we have just
+ * received.
+ */
+ax25_cb *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev)
+{
+ ax25_cb *s;
-#ifdef CONFIG_INET
- if (pid == AX25_P_IP) {
- /* working around a TCP bug to keep additional listeners
- * happy. TCP re-uses the buffer and destroys the original
- * content.
- */
- struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC);
- if (skbn != NULL) {
- kfree_skb(skb);
- skb = skbn;
- }
+ if (dev != NULL)
+ if ((s = ax25_dev_find_listener(addr, digi, dev)) != NULL)
+ return s;
- skb_pull(skb, 1); /* Remove PID */
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = ax25->ax25_dev->dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_IP);
- ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */
- return 1;
- }
-#endif
- if (pid == AX25_P_SEGMENT) {
- skb_pull(skb, 1); /* Remove PID */
- return ax25_rx_fragment(ax25, skb);
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN
+ && s->iamdigi == digi
+ && !ax25cmp(&s->addr.src, addr))
+ return s;
}
+ return NULL;
+}
- if ((func = ax25_protocol_function(pid)) != NULL) {
- skb_pull(skb, 1); /* Remove PID */
- return (*func)(skb, ax25);
- }
+/* ---------------------------------------------------------------------*/
+/*
+ * Find an AX.25 socket given both ends.
+ */
+static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type)
+{
+ ax25_cb *s;
+ struct sock *sk;
- if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) {
- if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) {
- if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
- queued = 1;
- else
- ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ if (dev != NULL)
+ if ((sk = ax25_dev_find_socket(my_addr, dest_addr, dev, type)) != NULL)
+ return sk;
+
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->sk != NULL && s->sk->type == type
+ && !ax25cmp(&s->addr.src, my_addr)
+ && !ax25cmp(&s->addr.dest, dest_addr))
+ {
+ return s->sk;
}
}
+ return NULL;
+}
+
+/* ---------------------------------------------------------------------*/
- return queued;
+static int ax25_match_addr(ax25_address *addr, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ if (dev != NULL && ax25_dev_match_addr(addr, dev))
+ return 1;
+
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0)
+ return 1;
+ }
+ return 0;
}
+/* ---------------------------------------------------------------------*/
+
+#ifdef notdef
/*
- * Higher level upcall for a LAPB frame
+ * this is completely broken.
+ *
+ * it depends on that sk is the first of a linked list of sockets
+ * and traverses the list to find them all, cloning the packet if the
+ * protocol and socket type both match.
*/
-static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
+static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
{
- int queued = 0;
+ struct sk_buff *copy;
- if (ax25->state == AX25_STATE_0)
- return 0;
+ while (sk != NULL) {
+ if (sk->type == SOCK_RAW &&
+ sk->protocol == proto &&
+ atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) {
+ if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ return;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- queued = ax25_std_frame_in(ax25, skb, type);
- break;
+ if (sock_queue_rcv_skb(sk, copy) != 0)
+ kfree_skb(copy);
+ }
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (dama || ax25->ax25_dev->dama.slave)
- queued = ax25_ds_frame_in(ax25, skb, type);
- else
- queued = ax25_std_frame_in(ax25, skb, type);
- break;
-#endif
+ sk = sk->next;
}
-
- return queued;
}
+#endif
-static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *dev_addr, struct packet_type *ptype)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
{
struct sock *make;
struct sock *sk;
- int type = 0;
- ax25_digi dp, reverse_dp;
- ax25_cb *ax25;
- ax25_address src, dest;
- ax25_address *next_digi = NULL;
- ax25_dev *ax25_dev;
- struct sock *raw;
- int mine = 0;
- int dama;
+ ax25_cb *ax25, *peer = NULL;
+ ax25_address *next_digi;
+ ax25_pktinfo pinf;
+ ax25_addr_t reverse_addr;
+ int queued;
/*
* Process the AX.25/LAPB frame.
*/
- skb->h.raw = skb->data;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+ skb->sk = NULL;
+ queued = 0;
+ AX25_PTR(dev)->dama_polled = 0;
/*
- * Parse the address header.
+ * TODO: packet filter
*/
-
- if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+#ifdef undef
+ if (call_in_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
+ goto out_normal;
+#endif
/*
- * Ours perhaps ?
+ * Parse the frame and Pull of the AX.25 headers
+ * leaving the CTRL/PID byte
*/
- if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */
- next_digi = &dp.calls[dp.lastrepeat + 1];
+ if (ax25_parse_addr(skb->data, skb->len, &pinf) == NULL)
+ goto out_normal;
+ skb_pull(skb, ax25_sizeof_addr(&pinf.addr));
+
/*
- * Pull of the AX.25 headers leaving the CTRL/PID bytes
+ * Steps to perform while processing the frame, fast path'd for
+ * digipeating.
*/
- skb_pull(skb, ax25_addr_size(&dp));
-
- /* For our port addresses ? */
- if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
- mine = 1;
-
- /* Also match on any registered callsign from L3/4 */
- if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
- mine = 1;
-
- /* UI frame - bypass LAPB processing */
- if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
- skb->h.raw = skb->data + 2; /* skip control and pid */
-
- if ((raw = ax25_addr_match(&dest)) != NULL)
- ax25_send_to_raw(raw, skb, skb->data[1]);
-
- if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
- kfree_skb(skb);
+ next_digi = NULL;
+ if (pinf.addr.lastrepeat + 1 < pinf.addr.dcount) {
+ /* Not yet digipeated completely */
+ struct net_device *out_dev;
+ next_digi = &pinf.addr.digipeater[pinf.addr.lastrepeat + 1];
+
+ /* check the frame type. do the easy thing first */
+ if ((*skb->data & ~AX25_PF) == AX25_UI) {
+ /* digipeat UI frame */
+
+ /* check if next_digi matches one of our interfaces */
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+
+ /* rebuild the header and transmit the frame */
+ out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev);
+ skb->nh.raw = skb->data;
+ skb_push(skb, ax25_sizeof_addr(&reverse_addr));
+ ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK);
+ ax25_send_unproto(skb, out_dev);
return 0;
}
- /* Now we are pointing at the pid byte */
- switch (skb->data[1]) {
-#ifdef CONFIG_INET
- case AX25_P_IP:
- skb_pull(skb,2); /* drop PID/CTRL */
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_IP);
- ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */
- break;
+ ax25_invert_addr(&pinf.addr, &reverse_addr);
- case AX25_P_ARP:
- skb_pull(skb,2);
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_ARP);
- arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */
- break;
-#endif
- case AX25_P_TEXT:
- /* Now find a suitable dgram socket */
- if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
- if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
- kfree_skb(skb);
- } else {
- /*
- * Remove the control and PID.
- */
- skb_pull(skb, 2);
- if (sock_queue_rcv_skb(sk, skb) != 0)
- kfree_skb(skb);
- }
- } else {
- kfree_skb(skb);
- }
- break;
-
- default:
- kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */
- break;
+ if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) {
+ /* no UI, but matches a given context */
+ queued = ax25_process_rx_frame(ax25, skb, &pinf);
+ goto out_queued;
}
- return 0;
- }
+ if ((*skb->data & ~AX25_PF) == AX25_SABM || (*skb->data & ~AX25_PF) == AX25_SABME) {
+ if ((ax25 = ax25_find_listener(next_digi, 1, dev)) != NULL)
+ goto listener_found;
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+ if ((peer = ax25_create_cb()) == NULL)
+ goto out_normal;
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ ax25_free_cb(peer);
+ goto out_normal;
+ }
- /*
- * Is connected mode supported on this device ?
- * If not, should we DM the incoming frame (except DMs) or
- * silently ignore them. For now we stay quiet.
- */
- if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) {
- kfree_skb(skb);
- return 0;
- }
+ out_dev = ax25_rt_set_addr(&peer->addr, &pinf.addr, dev, out_dev);
+ ax25_fillin_cb(peer, out_dev);
+ ax25_nlpost_route(&pinf, dev);
- /* LAPB */
+ /* set up the new control block */
+ ax25_fillin_cb(ax25, dev);
+ ax25->addr = reverse_addr;
- /* AX.25 state 1-4 */
+ if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
- ax25_digi_invert(&dp, &reverse_dp);
+ /* crosslink the peers */
+ ax25->peer = peer;
+ peer->peer = ax25;
- if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
- /*
- * Process the frame. If it is queued up internally it returns one otherwise we
- * free it immediately. This routine itself wakes the user context layers so we
- * do no further work
- */
- if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
- kfree_skb(skb);
+ /* set window and modulus */
+ peer->window = ax25->window;
+ peer->seqmask = ax25->seqmask;
- return 0;
- }
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ ax25->state = AX25_STATE_0;
+ ax25_insert_cb(ax25);
- /* AX.25 state 0 (disconnected) */
+ peer->state = AX25_STATE_1;
+ ax25_insert_cb(peer);
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
- /* a) received not a SABM(E) */
+ goto out_normal;
+ }
- if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
/*
- * Never reply to a DM. Also ignore any connects for
- * addresses that are not our interfaces and not a socket.
+ * the packet was no UI or SABM(E) and didn't match an existing
+ * context. If it's via us, digipeat blindly.
*/
- if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
- ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
+ /* check if next_digi matches one of our interfaces */
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+
+ /* rebuild the header and transmit the frame */
+ out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev);
+ skb->nh.raw = skb->data;
+ skb_push(skb, ax25_sizeof_addr(&reverse_addr));
+ ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK);
+ ax25_send_unproto(skb, out_dev);
return 0;
}
- /* b) received SABM(E) */
-
- if (dp.lastrepeat + 1 == dp.ndigi)
- sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
- else
- sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
+ /*
+ * UI frame - bypass LAPB processing
+ */
+ if ((*skb->data & ~0x10) == AX25_UI) {
+ int queued = 0;
+ int pid;
+ /* skip control and pid */
+ skb->nh.raw = skb->data + 2;
- if (sk != NULL) {
- if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, ax25_dev)) == NULL) {
- if (mine) ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
- }
+ /* copy the datagram to all raw sockets */
+/* ax25_send_to_raw(&dest, skb, dev); */
- ax25 = make->protinfo.ax25;
- skb_set_owner_r(skb, make);
- skb_queue_head(&sk->receive_queue, skb);
+ /* if it's not a broadcast or one of our floating listeners, drop it */
+ if (!ax25_match_addr(&pinf.addr.dest, dev)
+ && ax25cmp(&pinf.addr.dest, (ax25_address *)dev->broadcast))
+ goto out_normal;
- make->state = TCP_ESTABLISHED;
- make->pair = sk;
+ /* Now we are pointing at the pid byte */
+ switch (pid = skb->data[1]) {
+ int (*func)(struct sk_buff *, ax25_cb *);
- sk->ack_backlog++;
- } else {
- if (!mine) {
- kfree_skb(skb);
- return 0;
- }
+ case AX25_P_TEXT:
+ /* Now find a suitable dgram socket */
+ if ((sk = ax25_find_socket(&pinf.addr.dest, &pinf.addr.src, dev, SOCK_DGRAM)) != NULL) {
+ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
+ kfree_skb(skb);
+ } else {
+ /*
+ * Remove the control and PID.
+ */
+ skb_pull(skb, 2);
+ if (sock_queue_rcv_skb(sk, skb) != 0)
+ kfree_skb(skb);
+ }
+ } else {
+ kfree_skb(skb);
+ }
+ break;
- if ((ax25 = ax25_create_cb()) == NULL) {
- ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
+ default:
+ if ((func = ax25_protocol_function(pid)) != NULL) {
+ skb_pull(skb, 2); /* Remove CTL/PID */
+ queued = func(skb, NULL);
+ }
+ if (!queued)
+ kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */
+ break;
}
- ax25_fillin_cb(ax25, ax25_dev);
+ return 0;
}
- ax25->source_addr = dest;
- ax25->dest_addr = src;
-
+ /* LAPB */
/*
- * Sort out any digipeated paths.
+ * invert the digipeater path and try to find a context for the
+ * received packet.
*/
- if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- kfree_skb(skb);
- ax25_destroy_socket(ax25);
- return 0;
+ ax25_invert_addr(&pinf.addr, &reverse_addr);
+ if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) {
+ /*
+ * Process the frame. If it is queued up internally it returns one otherwise we
+ * free it immediately. This routine itself wakes the user context layers so we
+ * do no further work
+ */
+ queued = ax25_process_rx_frame(ax25, skb, &pinf);
+ goto out_queued;
}
- if (dp.ndigi == 0) {
- if (ax25->digipeat != NULL) {
- kfree(ax25->digipeat);
- ax25->digipeat = NULL;
+ if ((ax25 = ax25_find_listener(&pinf.addr.dest, 0, dev)) != NULL) {
+ listener_found:
+ /*
+ * if this frame is not a SABM(E) return DM to the sender.
+ * it belongs to a stale connection. maybe we have rebootet or the
+ * peer didn't see our UA/DM
+ */
+ if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
}
- } else {
- /* Reverse the source SABM's path */
- memcpy(&ax25->digipeat, &reverse_dp, sizeof(ax25_digi));
- }
- if ((*skb->data & ~AX25_PF) == AX25_SABME) {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
- }
-
- ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
+ /* ok, we have a listener */
+ ax25_nlpost_route(&pinf, dev);
+ if ((sk = ax25->sk) != NULL) {
+ /* the listener is a socket */
+ if (sk->type != SOCK_SEQPACKET || sk->state != TCP_LISTEN)
+ goto out_normal;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
- ax25_dama_on(ax25);
-#endif
+ if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
+ }
+ skb->sk = make;
+ make->state = TCP_ESTABLISHED;
+ make->pair = sk;
+ ax25 = make->protinfo.ax25;
+ skb_queue_head(&sk->receive_queue, skb);
+ sk->ack_backlog++;
+ if (!sk->dead)
+ sk->data_ready(sk, skb->len);
+ queued = 1;
+ } else {
+ /*
+ * the listener is no socket, must be a callsign registered
+ * by a higher protocol, we just take the connection
+ */
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
+ }
+ ax25->slcomp_enable = ax25_rt_mode_get(&pinf.addr.src) == 'C';
+ }
- ax25->state = AX25_STATE_3;
+ /* set up the new control block */
+ ax25_fillin_cb(ax25, dev);
+ ax25->addr = reverse_addr;
- ax25_insert_socket(ax25);
+ if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
- ax25_start_heartbeat(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
+ ax25->device = dev;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->state = AX25_STATE_3;
+ ax25_insert_cb(ax25);
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ }
- if (sk != NULL) {
- if (!sk->dead)
- sk->data_ready(sk, skb->len);
- } else {
+ out_queued:
+ if (!queued) {
+ out_normal:
kfree_skb(skb);
}
-
return 0;
}
-
-/*
- * Receive an AX.25 frame via a SLIP interface.
- */
-int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
-{
- skb->sk = NULL; /* Initially we don't know who it's for */
- skb->destructor = NULL; /* Who initializes this, dammit?! */
-
- if ((*skb->data & 0x0F) != 0) {
- kfree_skb(skb); /* Not a KISS data frame */
- return 0;
- }
-
- skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */
-
- return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
-}
-
diff --git a/net/ax25/ax25_in.h b/net/ax25/ax25_in.h
new file mode 100644
index 000000000..90aa12b1e
--- /dev/null
+++ b/net/ax25/ax25_in.h
@@ -0,0 +1,20 @@
+#ifndef _AX25_IN_H
+#define _AX25_IN_H
+
+/*
+ * prototype for state machine functions.
+ */
+
+typedef int (*ax25_statefunc_t)(ax25_cb*, struct sk_buff*, ax25_pktinfo*);
+
+extern int ax25_process_rx_frame(ax25_cb*, struct sk_buff*, ax25_pktinfo*);
+extern int ax25_rx_iframe(ax25_cb*, struct sk_buff*);
+extern void ax25_reseq_update(ax25_cb*, struct sk_buff*, int);
+extern int ax25_reseq_in(ax25_cb*, struct sk_buff*, int, int);
+extern void ax25_reseq_out(ax25_cb*);
+extern ax25_cb* ax25_find_listener(ax25_address*, int, struct net_device*);
+extern struct sock* ax25_make_new(struct sock*, struct net_device*);
+
+extern struct packet_type ax25_packet_type;
+
+#endif
diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c
deleted file mode 100644
index e6dcda83d..000000000
--- a/net/ax25/ax25_ip.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/termios.h> /* For TIOCINQ/OUTQ */
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/netfilter.h>
-#include <linux/sysctl.h>
-#include <net/ip.h>
-#include <net/arp.h>
-
-/*
- * IP over AX.25 encapsulation.
- */
-
-/*
- * Shove an AX.25 UI header on an IP packet and handle ARP
- */
-
-#ifdef CONFIG_INET
-
-int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
-{
- /* header is an AX.25 UI frame from us to them */
- unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
-
- *buff++ = 0x00; /* KISS DATA */
-
- if (daddr != NULL)
- memcpy(buff, daddr, dev->addr_len); /* Address specified */
-
- buff[6] &= ~AX25_CBIT;
- buff[6] &= ~AX25_EBIT;
- buff[6] |= AX25_SSSID_SPARE;
- buff += AX25_ADDR_LEN;
-
- if (saddr != NULL)
- memcpy(buff, saddr, dev->addr_len);
- else
- memcpy(buff, dev->dev_addr, dev->addr_len);
-
- buff[6] &= ~AX25_CBIT;
- buff[6] |= AX25_EBIT;
- buff[6] |= AX25_SSSID_SPARE;
- buff += AX25_ADDR_LEN;
-
- *buff++ = AX25_UI; /* UI */
-
- /* Append a suitable AX.25 PID */
- switch (type) {
- case ETH_P_IP:
- *buff++ = AX25_P_IP;
- break;
- case ETH_P_ARP:
- *buff++ = AX25_P_ARP;
- break;
- default:
- printk(KERN_ERR "AX.25: ax25_encapsulate - wrong protocol type 0x%x2.2\n", type);
- *buff++ = 0;
- break;
- }
-
- if (daddr != NULL)
- return AX25_HEADER_LEN;
-
- return -AX25_HEADER_LEN; /* Unfinished header */
-}
-
-int ax25_rebuild_header(struct sk_buff *skb)
-{
- struct sk_buff *ourskb;
- unsigned char *bp = skb->data;
- struct net_device *dev;
- ax25_address *src, *dst;
- ax25_route *route;
- ax25_dev *ax25_dev;
-
- dst = (ax25_address *)(bp + 1);
- src = (ax25_address *)(bp + 8);
-
- if (arp_find(bp + 1, skb))
- return 1;
-
- route = ax25_rt_find_route(dst, NULL);
- dev = route->dev;
-
- if (dev == NULL)
- dev = skb->dev;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return 1;
-
- if (bp[16] == AX25_P_IP) {
- if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
- /*
- * We copy the buffer and release the original thereby
- * keeping it straight
- *
- * Note: we report 1 back so the caller will
- * not feed the frame direct to the physical device
- * We don't want that to happen. (It won't be upset
- * as we have pulled the frame from the queue by
- * freeing it).
- *
- * NB: TCP modifies buffers that are still
- * on a device queue, thus we use skb_copy()
- * instead of using skb_clone() unless this
- * gets fixed.
- */
-
- ax25_address src_c;
- ax25_address dst_c;
-
- if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
- kfree_skb(skb);
- return 1;
- }
-
- if (skb->sk != NULL)
- skb_set_owner_w(ourskb, skb->sk);
-
- kfree_skb(skb);
-
- src_c = *src;
- dst_c = *dst;
-
- skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
- skb->nh.raw = skb->data;
-
- ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c,
-&dst_c, route->digipeat, dev);
-
- return 1;
- }
- }
-
- bp[7] &= ~AX25_CBIT;
- bp[7] &= ~AX25_EBIT;
- bp[7] |= AX25_SSSID_SPARE;
-
- bp[14] &= ~AX25_CBIT;
- bp[14] |= AX25_EBIT;
- bp[14] |= AX25_SSSID_SPARE;
-
- skb_pull(skb, AX25_KISS_HEADER_LEN);
-
- if (route->digipeat != NULL) {
- if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
- kfree_skb(skb);
- return 1;
- }
-
- skb = ourskb;
- }
-
- skb->dev = dev;
-
- ax25_queue_xmit(skb);
-
- return 1;
-}
-
-#else /* INET */
-
-int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
-{
- return -AX25_HEADER_LEN;
-}
-
-int ax25_rebuild_header(struct sk_buff *skb)
-{
- return 1;
-}
-
-#endif
-
diff --git a/net/ax25/ax25_ipax.c b/net/ax25/ax25_ipax.c
new file mode 100644
index 000000000..65e8090a3
--- /dev/null
+++ b/net/ax25/ax25_ipax.c
@@ -0,0 +1,708 @@
+/*
+ * ax25_ipax.c: implements "virtual" interface ipax0 and en/decapsulation of IP
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Includes "Protocol Booster" and VJ compression switch.
+ * Written from scratch by Matthias Welwarsky in 1998.
+ *
+ * Changelog:
+ *
+ * License: This module 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.
+ */
+
+#define AX25_ENCAP_MODE_IGNORE_PROTOCOL
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "ax25_route.h"
+#include "ax25_core.h"
+#include "ax25_ddi.h"
+#include "ax25_vj.h"
+#include "ax25_netlink.h"
+#include "ax25_subr.h"
+
+
+
+static int ipax_hard_header(struct sk_buff*, struct net_device*, unsigned short, void*, void*, unsigned);
+static int ipax_set_mac_address(struct net_device*, void*);
+static int ipax_probe(struct net_device*);
+static int ipax_device_event(struct notifier_block*, unsigned long, void*);
+static int ipax_open(struct net_device*);
+static int ipax_close(struct net_device*);
+static int ipax_rcv(struct sk_buff*, ax25_cb*);
+static int ipax_arp_rcv(struct sk_buff*, ax25_cb*);
+static int ipax_vjc_rcv(struct sk_buff*, struct ax25_cb*);
+static int ipax_vjunc_rcv(struct sk_buff*, struct ax25_cb*);
+static int ipax_send_packet(struct sk_buff*, struct net_device*);
+static struct net_device_stats *ipax_get_stats(struct net_device*);
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device ipax_device;
+
+static struct ipax_local_t {
+ struct net_device_stats stats;
+} ipax_local;
+
+/*
+ * Device up/down notifier block
+ */
+static struct notifier_block ipax_dev_notifier = {
+ ipax_device_event,
+ 0
+};
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* ---------------------------------------------------------------------*/
+
+int ipax_init(void)
+{
+ struct net_device *dev = &ipax_device;
+
+ memset(dev, 0, sizeof (struct net_device));
+ strcpy(dev->name, "ipax0");
+ dev->if_port = 0;
+ dev->init = ipax_probe;
+ dev->base_addr = 0;
+ dev->irq = 0;
+ dev->dma = 0;
+
+ register_netdevice_notifier(&ipax_dev_notifier);
+
+ if (register_netdev(dev)) {
+ printk(KERN_WARNING "ipax: cannot register net device\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+int ipax_cleanup(void)
+{
+ unregister_netdevice_notifier(&ipax_dev_notifier);
+ unregister_netdev(&ipax_device);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ return NOTIFY_DONE;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * set the MAC layer address of the interface. (i.e. the callsign)
+ *
+ */
+static int ipax_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+
+ if (netif_running(dev))
+ ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ if (netif_running(dev))
+ ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Interface startup.
+ *
+ */
+static int ipax_probe(struct net_device *dev)
+{
+ if (dev == NULL)
+ return -ENXIO;
+
+ dev->base_addr = 0;
+ dev->irq = 0;
+ dev->dma = 0;
+
+ /* Initialize the device structure. */
+ dev->priv = &ipax_local;
+
+ memset(dev->priv, 0, sizeof(struct ipax_local_t));
+
+ dev->open = ipax_open;
+ dev->stop = ipax_close;
+ dev->hard_start_xmit = ipax_send_packet;
+ dev->get_stats = ipax_get_stats;
+
+ dev->hard_header = ipax_hard_header;
+ dev->set_mac_address = ipax_set_mac_address;
+
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN;
+ dev->mtu = 1500;
+ dev->addr_len = AX25_ADDR_LEN;
+ dev->tx_queue_len = 8;
+
+ memcpy(dev->broadcast, ax25_bcast, dev->addr_len);
+ memcpy(dev->dev_addr, ax25_test, dev->addr_len);
+
+ dev->flags = IFF_BROADCAST;
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ */
+static int ipax_open(struct net_device *dev)
+{
+ if (netif_running(dev))
+ return 0;
+
+ if (!ax25_listen_register((ax25_address *)dev->dev_addr, NULL))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_IP, ipax_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_ARP, ipax_arp_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_VJCOMP, ipax_vjc_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_VJUNCOMP, ipax_vjunc_rcv))
+ return 0;
+
+ /* dev_restart(dev); */ /* FIXME: anything to do here at all? */
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * the inverse routine to ipax_open. close the interface end
+ * decrement the module use count.
+ */
+static int ipax_close(struct net_device *dev)
+{
+ if (!netif_running(dev))
+ return 0;
+
+ netif_stop_queue(dev);
+
+ ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+ ax25_protocol_release(AX25_P_IP);
+ ax25_protocol_release(AX25_P_ARP);
+ ax25_protocol_release(AX25_P_VJCOMP);
+ ax25_protocol_release(AX25_P_VJUNCOMP);
+
+ return 0;
+
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_rcv(struct sk_buff *skb, ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ if (ax25) /* FIXME: works only for VC */
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL); /* Wrong ptype */
+ lp->stats.rx_packets++;
+
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_arp_rcv(struct sk_buff *skb, ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_ARP);
+ arp_rcv(skb, dev, NULL); /* Wrong ptype */
+
+ lp->stats.rx_packets++;
+
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_vjc_rcv(struct sk_buff *skb, struct ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ if (!ax25)
+ return 0;
+
+ ax25->slcomp_enable = 1;
+
+ /*
+ * check if we already have initialized slots,
+ * if not, it's too late. flush the frame.
+ */
+ if (ax25->slcomp == NULL) {
+ ax25->slcomp = axhc_init(32, 32);
+ printk(KERN_DEBUG "ax25_vjc_recv: no vjc-slots allocated, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+
+ /*
+ * the data will grow upwards while decompressing,
+ * crippling the ax.25 header. copy the packet only if
+ * it's smaller than 256 bytes, else the defragmenter has
+ * already copied it, we don't do that twice.
+ */
+ if (skb->len <= 256) {
+ struct sk_buff *skbn;
+ if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+ kfree_skb(skb);
+ skb = skbn;
+ }
+ }
+
+ /* set device the packet came in */
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+
+ /*
+ * make headroom for MAX_HEADER bytes of TCP/IP header
+ */
+ if (skb_headroom(skb) < MAX_HEADER) {
+ struct sk_buff *skbn;
+ if ((skbn = skb_realloc_headroom(skb, MAX_HEADER)) == NULL) {
+ printk(KERN_DEBUG "ax25_vjc_recv: cannot reallocate headroom, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ kfree_skb(skb);
+ skb = skbn;
+ }
+
+ if (axhc_uncompress(ax25->slcomp, skb) <= 0) {
+ printk(KERN_DEBUG "ipax_vjc_rcv: error decompressing packet, dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ /* adjust pointer to network header */
+ skb->nh.raw = skb->data;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL);
+ lp->stats.rx_packets++;
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_vjunc_rcv(struct sk_buff *skb, struct ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ if (!ax25)
+ return 0;
+
+ ax25->slcomp_enable = 1;
+
+ /*
+ * MW:
+ * check if we already have initialized slots,
+ * do so if not.
+ */
+ if (ax25->slcomp == NULL)
+ ax25->slcomp = axhc_init(32, 32);
+
+ if (axhc_remember(ax25->slcomp, skb) <= 0) {
+ printk(KERN_DEBUG "ipax_vjunc_rcv: unable to remember slot, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL);
+ lp->stats.rx_packets++;
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static void ipax_fragment(struct sk_buff *skb, struct net_device *dev, ax25_addr_t *addr)
+{
+ struct iphdr *iph;
+ unsigned char *raw;
+ unsigned char *ptr;
+ struct sk_buff *skb2;
+ unsigned int mtu, hlen, left, len;
+ int offset;
+ int not_last_frag;
+
+ /*
+ * Point into the IP datagram header.
+ */
+
+ raw = skb->nh.raw;
+ iph = (struct iphdr*)raw;
+
+ /*
+ * Setup starting values.
+ */
+
+ hlen = iph->ihl * 4;
+ left = ntohs(iph->tot_len) - hlen; /* Space per frame */
+ mtu = dev->mtu; /* Size of data space */
+ ptr = raw + hlen; /* Where to start from */
+
+ /*
+ * The protocol doesn't seem to say what to do in the case that the
+ * frame + options doesn't fit the mtu. As it used to fall down dead
+ * in this case we were fortunate it didn't happen
+ *
+ * It is impossible, because mtu>=68. --ANK (980801)
+ */
+
+#ifdef CONFIG_NET_PARANOIA
+ if (mtu<8)
+ goto fail;
+#endif
+
+ /*
+ * Fragment the datagram.
+ */
+
+ offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+ not_last_frag = iph->frag_off & htons(IP_MF);
+
+ /*
+ * Keep copying data until we run out.
+ */
+
+ while(left > 0) {
+ len = left;
+ /* IF: it doesn't fit, use 'mtu' - the data space left */
+ if (len > mtu)
+ len = mtu;
+ /* IF: we are not sending upto and including the packet end
+ then align the next start on an eight byte boundary */
+ if (len < left) {
+ len &= ~7;
+ }
+ /*
+ * Allocate buffer.
+ */
+
+ if ((skb2 = alloc_skb(len+hlen+AX25_MAX_HEADER_LEN+15,GFP_ATOMIC)) == NULL) {
+ NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
+ goto fail;
+ }
+
+ /*
+ * Set up data on packet
+ */
+
+ skb2->pkt_type = skb->pkt_type;
+ skb2->priority = skb->priority;
+ skb_reserve(skb2, (AX25_MAX_HEADER_LEN+15)&~15);
+ skb_put(skb2, len + hlen);
+ skb2->nh.raw = skb2->data;
+ skb2->h.raw = skb2->data + hlen;
+
+ /*
+ * Charge the memory for the fragment to any owner
+ * it might possess
+ */
+
+ if (skb->sk)
+ skb_set_owner_w(skb2, skb->sk);
+ skb2->dst = dst_clone(skb->dst);
+
+ /*
+ * Copy the packet header into the new buffer.
+ */
+
+ memcpy(skb2->nh.raw, raw, hlen);
+
+ /*
+ * Copy a block of the IP datagram.
+ */
+ memcpy(skb2->h.raw, ptr, len);
+ left -= len;
+
+ /*
+ * Fill in the new header fields.
+ */
+ iph = skb2->nh.iph;
+ iph->frag_off = htons((offset >> 3));
+
+ /* ANK: dirty, but effective trick. Upgrade options only if
+ * the segment to be fragmented was THE FIRST (otherwise,
+ * options are already fixed) and make it ONCE
+ * on the initial skb, so that all the following fragments
+ * will inherit fixed options.
+ */
+ if (offset == 0)
+ ip_options_fragment(skb);
+
+ /*
+ * Added AC : If we are fragmenting a fragment that's not the
+ * last fragment then keep MF on each bit
+ */
+ if (left > 0 || not_last_frag)
+ iph->frag_off |= htons(IP_MF);
+ ptr += len;
+ offset += len;
+
+ /*
+ * Put this fragment into the sending queue.
+ */
+
+ /* FIXME: where did this go?
+ ip_statistics.IpFragCreates++;
+ */
+
+ iph->tot_len = htons(len + hlen);
+
+ ip_send_check(iph);
+
+ /* build UI packet header */
+ *skb_push(skb2, 1) = AX25_P_IP;
+ *skb_push(skb2, 1) = AX25_UI;
+ skb_push(skb2, ax25_sizeof_addr(addr));
+ ax25_build_addr(skb2->data, addr, AX25_COMMAND, AX25_SEQMASK);
+ ax25_send_unproto(skb2, dev);
+ }
+ kfree_skb(skb);
+
+ /* FIXME: where did this go?
+ ip_statistics.IpFragOKs++;
+ */
+ return;
+
+fail:
+ kfree_skb(skb);
+ /* FIXME: where did this go?
+ ip_statistics.IpFragFails++;
+ */
+
+}
+
+/* ---------------------------------------------------------------------*/
+
+static unsigned char ipax_try_compress(ax25_cb *ax25, struct sk_buff *skb)
+{
+ unsigned char pid;
+
+ if (ax25->slcomp == NULL)
+ ax25->slcomp = axhc_init(32, 32);
+ /* attempt to compress the frame */
+ pid = axhc_compress(ax25->slcomp, skb, ax25->slcomp_enable);
+ if (pid)
+ *skb_push(skb, 1) = pid;
+ return pid;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+ ax25_address *dest, *source;
+ struct ax25_route *axrt;
+ struct net_device *axdev;
+ ax25_addr_t addr;
+
+ /*
+ * send the packet down to the tunneling protocol
+ */
+ dest = (ax25_address *)skb->data;
+ source = (ax25_address *)(skb->data+AX25_ADDR_LEN);
+
+ if (!ax25cmp(dest, (ax25_address *)ax25_bcast)) {
+ ax25_send_broadcast(skb);
+ kfree_skb(skb);
+ lp->stats.tx_packets++;
+ return 0;
+ }
+
+ axrt = ax25_find_route(dest);
+ if (axrt == NULL) {
+ printk(KERN_DEBUG "ax25: ipax_send_packet(): no route found, discarding packet.\n");
+ kfree_skb(skb);
+ lp->stats.tx_dropped++;
+ return 0;
+ }
+
+ axdev = axrt->dev;
+
+ addr.dest = *dest;
+ addr.src = *source;
+ addr.dcount = axrt->path.dcount;
+ addr.lastrepeat = -1;
+ memcpy(addr.digipeater, axrt->path.digipeater, sizeof(ax25_address)*addr.dcount);
+
+ if (skb->data[15] == AX25_P_IP) {
+#ifndef AX25_ENCAP_MODE_IGNORE_PROTOCOL
+ struct iphdr *iph = (struct iphdr *)(skb->data+16);
+#endif
+ int mode = axrt->ip_mode;
+
+#ifdef AX25_ENCAP_MODE_IGNORE_PROTOCOL
+ if (1) {
+#else
+ if (iph->protocol != IPPROTO_UDP) {
+#endif
+ if (mode == 'V' || mode == 'C'
+ || (mode == ' ' && ax25_dev_get_value(axdev, AX25_VALUES_IPDEFMODE)))
+ {
+ int paclen = ax25_dev_get_value(axdev, AX25_VALUES_PACLEN);
+ ax25_cb* ax25 = ax25_find_cb(&addr, axdev);
+
+ if (ax25 != NULL) {
+ /* reuse a just disconnected control block */
+ if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) {
+ if (ax25->slcomp) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ goto out_reused;
+ }
+ goto out_ok;
+ }
+ if ((ax25 = ax25_create_cb()) == NULL)
+ goto out_dropped;
+ ax25_fillin_cb(ax25, axdev);
+ ax25->addr = addr;
+ ax25_insert_cb(ax25);
+ out_reused:
+ ax25->slcomp_enable = (mode == 'C');
+ ax25_establish_data_link(ax25);
+ out_ok:
+ /* completely strip the header */
+ skb_pull(skb, AX25_MIN_HEADER_LEN+1);
+ if (!ipax_try_compress(ax25, skb))
+ goto out_dropped;
+ ax25_output(ax25, paclen, skb);
+ dev->trans_start = jiffies;
+ /* netif_stop_queue(dev) */
+ lp->stats.tx_packets++;
+ return 0;
+ out_dropped:
+ lp->stats.tx_dropped++;
+ kfree_skb(skb);
+ /* netif_stop_queue(dev) */
+ return 0;
+ }
+ } else if (skb->len > axdev->mtu) {
+ /* completely strip the header */
+ skb_pull(skb, AX25_MIN_HEADER_LEN+1);
+ ipax_fragment(skb, axdev, &addr);
+ return 0;
+ }
+ }
+
+ /* rebuild the header if digipeaters are to be used */
+ if (addr.dcount != 0) {
+ /* strip address field, keep the control byte */
+ skb_pull(skb, AX25_MIN_HEADER_LEN-1);
+ skb_push(skb, ax25_sizeof_addr(&addr));
+ ax25_build_addr(skb->data, &addr, AX25_COMMAND, AX25_SEQMASK);
+ }
+
+ /*
+ * queue a completely assembled frame to the unproto
+ * queue of an interface
+ */
+ ax25_send_unproto(skb, axdev);
+ dev->trans_start = jiffies;
+ lp->stats.tx_packets++;
+ /* netif_stop_queue(dev) */
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *ipax_get_stats(struct net_device *dev)
+{
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+ return &lp->stats;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_hard_header(struct sk_buff* skb, struct net_device* dev, unsigned short type, void* daddr, void* saddr, unsigned len)
+{
+ unsigned char pid;
+ unsigned char *buf;
+ int hlen;
+
+ if (type == ETH_P_IP)
+ pid = AX25_P_IP;
+ else if (type == ETH_P_ARP)
+ pid = AX25_P_ARP;
+ else {
+ printk(KERN_ERR "ax25: ipax_hard_header: unknown packet type %d.\n", type);
+ pid = 0;
+ }
+ *skb_push(skb, 1) = pid;
+ *skb_push(skb, 1) = AX25_UI;
+
+ if (saddr != NULL)
+ memcpy(skb_push(skb, AX25_ADDR_LEN), saddr, AX25_ADDR_LEN);
+ else
+ memcpy(skb_push(skb, dev->addr_len), dev->dev_addr, dev->addr_len);
+
+ if (daddr != NULL) {
+ memcpy(skb_push(skb, AX25_ADDR_LEN), daddr, AX25_ADDR_LEN);
+ hlen = (AX25_MIN_HEADER_LEN+1);
+ } else {
+ memcpy(skb_push(skb, AX25_ADDR_LEN), &null_ax25_address, AX25_ADDR_LEN);
+ hlen = -(AX25_MIN_HEADER_LEN+1);
+ }
+
+ buf = skb->data;
+ buf[6] &= ~(AX25_CBIT|AX25_EBIT);
+ buf[6] |= AX25_SSSID_SPARE;
+ buf[13] &= ~AX25_CBIT;
+ buf[13] |= (AX25_EBIT|AX25_SSSID_SPARE);
+
+ return hlen;
+}
diff --git a/net/ax25/ax25_lapb.c b/net/ax25/ax25_lapb.c
new file mode 100644
index 000000000..849eeb44d
--- /dev/null
+++ b/net/ax25/ax25_lapb.c
@@ -0,0 +1,789 @@
+/*
+ * ax25_lapb.c: NEW-AX.25 state machine
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
+ * Rewritten from scratch by Matthias in 1998.
+ *
+ * Changelog:
+ *
+ * License: This module 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.
+ */
+
+#include <linux/socket.h>
+#include <linux/time.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include <net/tcp.h>
+
+#include "ax25_ddi.h"
+#include "ax25_vj.h"
+#include "ax25_route.h"
+#include "ax25_in.h"
+#include "ax25_lapb.h"
+#include "ax25_core.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
+
+/*
+ * Calculate the Round Trip Time
+ */
+static void ax25_calculate_rtt(ax25_cb *ax25, unsigned long rtt_raw)
+{
+ switch (ax25->backoff) {
+ case 0:
+ ax25->rtt = rtt_raw;
+ break;
+
+ case 1:
+ case 2:
+ ax25->rtt = (9 * ax25->rtt + rtt_raw) / 10;
+ break;
+ }
+}
+
+/*
+ * This routine purges the input queue of those frames that have been
+ * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
+ * SDL diagram.
+ */
+static void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
+{
+ struct sk_buff *skb;
+ /*
+ * Remove all the ack-ed frames from ack_queue.
+ */
+ while (ax25->va != nr && (skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
+ ax25->va = (ax25->va + 1) & ax25->seqmask;
+ kfree_skb(skb);
+
+ if (ax25->vs_rtt == ax25->va) {
+ ax25_calculate_rtt(ax25, jiffies - ax25->rtt_timestamp + 1);
+ ax25->vs_rtt = -1;
+ }
+ }
+ if (ax25->condition & AX25_COND_SETUP)
+ ax25_clr_cond(ax25, AX25_COND_SETUP);
+}
+
+/*
+ * This routine decides whether to restart T1 or not on an incoming
+ * frame. If the frame acks all outstanding frames, T1 is
+ * stopped, T3 is started. Else if it acks at least one
+ * outstanding frame T1 is restarted.
+ */
+static void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
+{
+ write_lock(&ax25->timer_lock);
+ if (ax25->va != nr) {
+ /* at least one frame acked */
+ ax25_frames_acked(ax25, nr);
+ if ((ax25->vs != nr) && !(DAMA_STATE(ax25) & DAMA_SLAVE))
+ ax25->wrt_timer = ax25->t1;
+ }
+ if (ax25->vs == ax25->va) ax25->wrt_timer = ax25->t3;
+ write_unlock(&ax25->timer_lock);
+}
+
+/*
+ * Due to the changed retransmission handling frames are not
+ * requeued from the ack_queue into the write queue. All
+ * retransmission stuff is done in ax25_dev_timer() now. This
+ * function is for clarity only, it will either be removed
+ * completely or turn into an inline function.
+ */
+static void ax25_requeue_frames(ax25_cb *ax25)
+{
+ /*
+ * reset the send sequence counter.
+ */
+ ax25->vs = ax25->va;
+}
+
+/*
+ * Validate that the value of nr is between va and vs. Return true or
+ * false for testing.
+ */
+static int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
+{
+ unsigned short ds;
+ unsigned short dr;
+
+ ds = ax25->vs_max - ax25->va;
+ ds &= ax25->seqmask;
+
+ dr = nr - ax25->va;
+ dr &= ax25->seqmask;
+
+ return (dr <= ds);
+}
+
+/*
+ * link reset handling. This is a rather complex routine, as it
+ * has to cope with a tricky situation in the protocol flow.
+ */
+static int ax25_reset_link(ax25_cb* ax25, struct sk_buff* skb)
+{
+ ax25_cb* peer = ax25->peer;
+
+ write_lock(&ax25->timer_lock);
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->idletimer = ax25->idle;
+ ax25->n2count = 0;
+ write_unlock(&ax25->timer_lock);
+
+ /*
+ * peer just didn't get our UA, handle gracefully.
+ */
+ if (ax25->condition & AX25_COND_SETUP) {
+ ax25_requeue_frames(ax25);
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ return 0;
+ }
+
+ /*
+ * Ok, this is a *real* reset. If we have an attached socket,
+ * disconnect it and produce a new, accept-ready one.
+ */
+ if (ax25->sk != NULL) {
+ struct sock* make;
+ struct sock* sk;
+ ax25_cb* tmp = ax25_find_listener(&ax25->addr.src, 0, ax25->device);
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting socket\n");
+
+ if (!tmp || !(sk = tmp->sk)
+ || sk->ack_backlog == sk->max_ack_backlog
+ || (make = ax25_make_new(sk, ax25->device)) == NULL)
+ {
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25->state = AX25_STATE_0;
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ return 0;
+ }
+
+ /* set the destination address */
+ make->protinfo.ax25->addr = ax25->addr;
+
+ /*
+ * ax25_make_new() has produced a new ax25_cb, attached to
+ * a struct sock, we just throw away the old one the regular
+ * way and insert the new one into the appropriate device queue
+ *
+ * It should be enough to disconnect() the old control block,
+ * as this will wake up the application which then in turn
+ * does a close() on the respective socket descriptor.
+ */
+ ax25_remove_cb(ax25);
+ ax25_disconnect(ax25, ECONNRESET);
+ ax25_close_socket(ax25->sk, ECONNRESET);
+
+ /*
+ * done with the old one, now for the new one
+ */
+ write_lock(&ax25->timer_lock);
+ ax25 = make->protinfo.ax25;
+ ax25->state = AX25_STATE_3;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs_rtt = -1;
+ write_unlock(&ax25->timer_lock);
+
+ make->pair = sk;
+ skb->sk = make;
+ skb_queue_head(&sk->receive_queue, skb);
+ sk->ack_backlog++;
+ ax25_dev_insert_cb(ax25);
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ sk->data_ready(sk, skb->len);
+ return 1;
+ }
+
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+
+ /*
+ * check if we have a peer connection to notify
+ */
+ if (peer) {
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting digipeated connection, state:%d peer state:%d\n",
+ ax25->state, peer->state);
+
+ ax25_clear_queues(ax25);
+
+ write_lock(&ax25->timer_lock);
+
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->state = AX25_STATE_0;
+ ax25->condition = AX25_COND_SETUP;
+
+ write_unlock(&ax25->timer_lock);
+
+ ax25_clear_queues(peer);
+
+ write_lock(&peer->timer_lock);
+
+ peer->wrt_timer = 0;
+ peer->ack_timer = 0;
+ peer->vs = 0;
+ peer->va = 0;
+ peer->vr = 0;
+ peer->vl = 0;
+ peer->condition = AX25_COND_SETUP;
+ peer->state = AX25_STATE_1;
+
+ write_unlock(&peer->timer_lock);
+
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
+ return 0;
+ }
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting protocol uplink\n");
+
+ if (ax25->slcomp != NULL) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25->wrt_timer = ax25->t3;
+ ax25->condition = AX25_COND_SETUP;
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+
+ return 0;
+}
+
+/*
+ * State machine for state 0, Disconnected State.
+ * we need this now for the hop-2-hop acknowledgement
+ */
+static int ax25_state0_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (peer) {
+ if (peer->condition & AX25_COND_SETUP) {
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ } else {
+ struct net_device *dev;
+ if ((dev = ax25_rt_set_addr(&peer->addr, &pkt->addr, ax25->device, peer->device)) == NULL)
+ ax25_tx_command(ax25, AX25_DM, AX25_POLLON);
+ else {
+ ax25_fillin_cb(peer, dev);
+ ax25_reset_link(ax25, skb);
+ }
+ }
+ } else if (!ax25->sk)
+ ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ default:
+ if (pkt->pf && pkt->cmdrsp == AX25_COMMAND)
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ break;
+ }
+ return 0; /* we never queue */
+}
+
+
+/*
+ * State machine for state 1, Awaiting Connection State.
+ * The handling of the timer(s) is in file ax25_timer.c.
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ if (peer || ax25->sk == NULL) {
+ if (ax25->seqmask != AX25_SEQMASK) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ }
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ if (peer) {
+ peer->state = AX25_STATE_3;
+ peer->vs_rtt = -1;
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ }
+ }
+ break;
+
+ case AX25_SABME:
+ if (ax25->seqmask == AX25_ESEQMASK && (peer || ax25->sk == NULL)) {
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ if (peer) {
+ peer->state = AX25_STATE_3;
+ peer->vs_rtt = -1;
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ }
+ }
+ break;
+
+ case AX25_DISC:
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ case AX25_UA:
+ if (pkt->pf) {
+ write_lock(&ax25->timer_lock);
+ ax25->state = AX25_STATE_3;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->killtimer = 0;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+ ax25->vs_rtt = -1;
+ ax25->n2count = 0;
+ ax25->tx_cmd = 0;
+ ax25->tx_rsp = 0;
+ write_unlock(&ax25->timer_lock);
+
+ if (peer != NULL) {
+ write_lock(&peer->timer_lock);
+ peer->wrt_timer = peer->t3;
+ peer->idletimer = 0;
+ peer->killtimer = 0;
+ peer->vs = 0;
+ peer->va = 0;
+ peer->vr = 0;
+ peer->vl = 0;
+ peer->vs_rtt = -1;
+ peer->n2count = 0;
+ peer->state = AX25_STATE_3;
+ ax25_clr_cond(peer, AX25_COND_STATE_CHANGE);
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ write_unlock(&peer->timer_lock);
+ } else if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_ESTABLISHED;
+ /* For WAIT_SABM connections we will produce an accept ready socket here */
+ ax25->sk->state_change(ax25->sk);
+ }
+
+ ax25_kick(ax25);
+ }
+ break;
+
+ case AX25_DM:
+ if (peer != NULL) {
+ ax25_set_cond(peer, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(peer, 0);
+ ax25_tx_response(peer, AX25_DM, pkt->pf);
+ } else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNREFUSED);
+ ax25_disconnect(ax25, ECONNREFUSED);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Release State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ case AX25_DM:
+ case AX25_UA:
+ if (pkt->pf) {
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_disconnect(ax25, 0);
+ ax25->killtimer=0;
+ ax25->tx_cmd=0;
+ }
+ break;
+
+ case AX25_I:
+ case AX25_REJ:
+ case AX25_RNR:
+ case AX25_RR:
+ if (pkt->pf && pkt->cmdrsp == AX25_COMMAND)
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ break;
+
+ default:
+ break;
+ }
+ return queued;
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ break;
+
+ case AX25_DM:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNRESET);
+ ax25_disconnect(ax25, ECONNRESET);
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_check_iframes_acked(ax25, pkt->nr);
+
+ if (pkt->frametype == AX25_RR)
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ else
+ ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY);
+
+ if (pkt->pf) {
+ if (pkt->cmdrsp == AX25_COMMAND) {
+ ax25_enquiry_response(ax25);
+ }
+ if (ax25->vs == ax25->va) {
+ ax25->n2count=0;
+ } else {
+ ax25_requeue_frames(ax25);
+ if (DAMA_STATE(ax25) & DAMA_SLAVE) {
+ /*
+ * in DAMA mode we use n2count in state 3
+ * to track the number of DAMA polls
+ * ack'ing only part of our I frames. This
+ * is neccessary to prevent hangs with buggy
+ * masters.
+ */
+ if (++ax25->n2count == ax25->n2) {
+ ax25->vs_rtt = -1;
+ ax25->n2count = 1;
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_4;
+ ax25_transmit_enquiry(ax25);
+ }
+ }
+ }
+ }
+
+ ax25_kick(ax25);
+ break;
+
+ case AX25_REJ:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+ ax25_check_iframes_acked(ax25, pkt->nr);
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ ax25_requeue_frames(ax25);
+ ax25_kick(ax25);
+ AX25_PTR(ax25->device)->rx_rejects++;
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_check_iframes_acked(ax25, pkt->nr);
+
+ if (pkt->ns == ax25->vr) {
+ if ((queued = ax25_rx_iframe(ax25, skb)) != 0) {
+ ax25_reseq_update(ax25, skb, pkt->ns);
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ ax25_reseq_out(ax25);
+ ax25_clr_cond(ax25, AX25_COND_REJECT);
+ }
+
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+#ifdef notdef
+ } else if (((pkt->ns+2) & ax25->seqmask) == ax25->vl) {
+ /*
+ * optimization (fast ack):
+ * If our peer's TX window is sent out
+ * completely we ack immediately without
+ * waiting for T2.
+ */
+ ax25_timeout_response(ax25);
+ } else {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+#else
+ } else ax25_timeout_response(ax25);
+#endif
+ } else {
+ /* frame is not in sequence */
+ queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf);
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+ } else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ ax25->ack_timer = 0;
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ ax25_set_cond(ax25, AX25_COND_REJECT);
+ ax25_tx_response(ax25, AX25_REJ, pkt->pf);
+ }
+ }
+ ax25_kick(ax25);
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_nr_error_recovery(ax25);
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * State machine for state 4, Timer Recovery State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ break;
+
+ case AX25_DM:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNRESET);
+ ax25_disconnect(ax25, ECONNRESET);
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ case AX25_REJ:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_frames_acked(ax25, pkt->nr);
+
+ if (pkt->frametype == AX25_RR)
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ else if (pkt->frametype == AX25_REJ) {
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ AX25_PTR(ax25->device)->rx_rejects++;
+ } else
+ ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY);
+
+ if (pkt->pf) {
+ if (pkt->cmdrsp == AX25_RESPONSE) {
+ write_lock(&ax25->timer_lock);
+ if (ax25->vs == ax25->va) {
+ ax25->n2count = 0;
+ ax25->wrt_timer = 0;
+ ax25->state = AX25_STATE_3;
+ ax25->tx_cmd = 0;
+ ax25->wrt_timer = ax25->t3;
+ }
+ write_unlock(&ax25->timer_lock);
+ ax25_requeue_frames(ax25);
+ } else {
+ if (DAMA_STATE(ax25) & DAMA_SLAVE)
+ ax25_requeue_frames(ax25);
+ ax25_enquiry_response(ax25);
+ }
+ }
+ ax25_kick(ax25);
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+ ax25_frames_acked(ax25, pkt->nr);
+ write_lock(&ax25->timer_lock);
+
+ if (pkt->ns == ax25->vr) {
+ /* frame is in sequence */
+ if ((queued = ax25_rx_iframe(ax25, skb)) != 0) {
+ ax25_reseq_update(ax25, skb, pkt->ns);
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ ax25_reseq_out(ax25);
+ ax25_clr_cond(ax25, AX25_COND_REJECT);
+ }
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+ } else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ /* frame is not in sequence */
+ queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf);
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pkt->pf)
+ ax25_enquiry_response(ax25);
+ else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ ax25->ack_timer = 0;
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ ax25_set_cond(ax25, AX25_COND_REJECT);
+ ax25_tx_response(ax25, AX25_REJ, pkt->pf);
+ }
+ }
+
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_nr_error_recovery(ax25);
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+static int ax25_state_nop(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ printk(KERN_DEBUG "ax25_state_nop()\n");
+ return 0;
+}
+
+ax25_statefunc_t ax25_lapb_table[] =
+{
+ ax25_state0_machine,
+ ax25_state1_machine,
+ ax25_state2_machine,
+ ax25_state3_machine,
+ ax25_state4_machine,
+ ax25_state_nop
+};
diff --git a/net/ax25/ax25_lapb.h b/net/ax25/ax25_lapb.h
new file mode 100644
index 000000000..989eab629
--- /dev/null
+++ b/net/ax25/ax25_lapb.h
@@ -0,0 +1,11 @@
+#ifndef _AX25_LAPB_H
+#define _AX25_LAPB_H
+
+
+/*
+ * state machine tables for lapb and dama slave
+ */
+extern ax25_statefunc_t ax25_lapb_table[];
+extern ax25_statefunc_t ax25_dama_table[];
+
+#endif
diff --git a/net/ax25/ax25_netlink.c b/net/ax25/ax25_netlink.c
new file mode 100644
index 000000000..73e25e234
--- /dev/null
+++ b/net/ax25/ax25_netlink.c
@@ -0,0 +1,110 @@
+/*
+ * ax25_netlink.c: NETLINK interface for NEW-AX.25
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
+ *
+ * Comment: It is intended to develop a new AX.25 routing daemon using t
+ * method to communicate with the kernel part. Recent developme
+ * Linux' realtime abilities, however, suggest removing AX.25 c
+ * from kernel space.
+ *
+ * Changelog:
+ *
+ * License: This module 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.
+*/
+
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/ax25.h>
+#include <net/sock.h>
+#include <net/ax25.h>
+
+#include "ax25_netlink.h"
+#include "ax25_route.h"
+
+static struct sock *axrtnl;
+
+static void ax25_netlink_rcv(struct sock *sk, int len)
+{
+ struct ax25_nlmsg *nlmsg;
+ struct sk_buff *skb;
+ struct net_device *dev;
+
+ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ if (skb->len < sizeof(struct ax25_nlmsg)) {
+ kfree_skb(skb);
+ break;
+ }
+ nlmsg = (struct ax25_nlmsg *)skb->data;
+
+ switch (nlmsg->msg_type) {
+ case AX25_MSG_SETRT:
+ if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL
+ && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS)
+ ax25_add_route(&nlmsg->msg.pathmsg.path, dev);
+ break;
+
+ case AX25_MSG_DELRT:
+ if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL)
+ ax25_del_route(&nlmsg->msg.pathmsg.path.addr);
+ break;
+
+ case AX25_MSG_OPTRT:
+ if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL
+ && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS)
+ {
+ ax25_add_route(&nlmsg->msg.pathmsg.path, dev);
+ ax25_ipopt_route(&nlmsg->msg.pathmsg.path.addr, nlmsg->msg.pathmsg.mode);
+ }
+ }
+ kfree_skb(skb);
+ }
+}
+
+void ax25_nlpost_route(ax25_pktinfo *pkt, struct net_device *dev)
+{
+ struct ax25_nlmsg *nlmsg;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC);
+ if (!skb)
+ return;
+ nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg));
+
+ nlmsg->msg_type = AX25_MSG_RTINFO;
+ strncpy(nlmsg->msg.rtmsg.port_name, dev->name, sizeof(nlmsg->msg.rtmsg.port_name));
+ nlmsg->msg.rtmsg.addr = pkt->addr;
+ netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL);
+}
+
+void ax25_nlpost_armsg(unsigned int ip_addr, ax25_address *ax_addr, struct net_device *dev)
+{
+ struct ax25_nlmsg *nlmsg;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC);
+ if (!skb)
+ return;
+ nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg));
+
+ nlmsg->msg_type = AX25_MSG_ARINFO;
+ strncpy(nlmsg->msg.armsg.port_name, dev->name, sizeof(nlmsg->msg.armsg.port_name));
+ nlmsg->msg.armsg.ip_addr = ip_addr;
+ nlmsg->msg.armsg.ax_addr = *ax_addr;
+
+ netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL);
+}
+
+void ax25_netlink_init(void)
+{
+ axrtnl = netlink_kernel_create(NETLINK_AX25, ax25_netlink_rcv);
+}
+
+void ax25_netlink_cleanup(void)
+{
+ sock_release(axrtnl->socket);
+}
diff --git a/net/ax25/ax25_netlink.h b/net/ax25/ax25_netlink.h
new file mode 100644
index 000000000..c100ed212
--- /dev/null
+++ b/net/ax25/ax25_netlink.h
@@ -0,0 +1,10 @@
+#ifndef _AX25_NETLINK_H
+#define _AX25_NETLINK_H
+
+extern void ax25_netlink_init(void);
+extern void ax25_netlink_cleanup(void);
+
+extern void ax25_nlpost_route(ax25_pktinfo*, struct net_device*);
+extern void ax25_nlpost_armsg(unsigned int, ax25_address*, struct net_device*);
+
+#endif
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index 231c170e7..a222ad1e8 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -1,129 +1,74 @@
/*
- * AX.25 release 037
+ * ax25_out.c: Subroutines for outgoing packets
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KL
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * This module:
- * This module 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.
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
*
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
+ * Changelog:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * Jonathan(G4KLX) Only poll when window is full.
- * AX.25 030 Jonathan(G4KLX) Added fragmentation to ax25_output.
- * Added support for extended AX.25.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * Joerg(DL1BKE) Modified fragmenter to fragment vanilla
- * AX.25 I-Frames. Added PACLEN parameter.
- * Joerg(DL1BKE) Fixed a problem with buffer allocation
- * for fragments.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Joerg(DL1BKE) Fixed DAMA Slave mode: will work
- * on non-DAMA interfaces like AX25L2V2
- * again (this behaviour is _required_).
- * Joerg(DL1BKE) ax25_check_iframes_acked() returns a
- * value now (for DAMA n2count handling)
+ * License: This module 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.
*/
#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
-#include <linux/netfilter.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev)
+#include "ax25_core.h"
+#include "ax25_route.h"
+#include "ax25_vj.h"
+#include "ax25_ddi.h"
+#include "ax25_subr.h"
+
+/* ---------------------------------------------------------------------*/
+/*
+ * send an iframe on a connection, maybe establish the connection first.
+ */
+ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_addr_t *addr, struct net_device *dev)
{
- ax25_dev *ax25_dev;
ax25_cb *ax25;
- /*
- * Take the default packet length for the device if zero is
- * specified.
- */
- if (paclen == 0) {
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return NULL;
-
- paclen = ax25_dev->values[AX25_VALUES_PACLEN];
- }
+ if (paclen == 0)
+ paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
/*
* Look for an existing connection.
*/
- if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
+ if ((ax25 = ax25_find_cb(addr, dev)) != NULL) {
+ /* reuse a just disconnected control block */
+ if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) {
+ if (ax25->slcomp) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C';
+ ax25_establish_data_link(ax25);
+ }
ax25_output(ax25, paclen, skb);
return ax25; /* It already existed */
}
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return NULL;
-
if ((ax25 = ax25_create_cb()) == NULL)
return NULL;
- ax25_fillin_cb(ax25, ax25_dev);
+ ax25_fillin_cb(ax25, dev);
- ax25->source_addr = *src;
- ax25->dest_addr = *dest;
-
- if (digi != NULL) {
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- ax25_free_cb(ax25);
- return NULL;
- }
- memcpy(ax25->digipeat, digi, sizeof(ax25_digi));
- }
-
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_establish_data_link(ax25);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25_dev->dama.slave)
- ax25_ds_establish_data_link(ax25);
- else
- ax25_std_establish_data_link(ax25);
- break;
-#endif
- }
-
- ax25_insert_socket(ax25);
-
- ax25->state = AX25_STATE_1;
-
- ax25_start_heartbeat(ax25);
+ ax25->addr = *addr;
+ ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C';
+ ax25_establish_data_link(ax25);
+ ax25_insert_cb(ax25);
ax25_output(ax25, paclen, skb);
-
return ax25; /* We had to create it */
}
@@ -138,7 +83,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
struct sk_buff *skbn;
unsigned char *p;
int frontlen, len, fragno, ka9qfrag, first = 1;
- long flags;
if ((skb->len - 1) > paclen) {
if (*skb->data == AX25_P_TEXT) {
@@ -155,11 +99,7 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
frontlen = skb_headroom(skb); /* Address space + CTRL */
while (skb->len > 0) {
- save_flags(flags);
- cli();
-
if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) {
- restore_flags(flags);
printk(KERN_CRIT "AX.25: ax25_output - out of memory\n");
return;
}
@@ -167,13 +107,11 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);
- restore_flags(flags);
-
len = (paclen > skb->len) ? skb->len : paclen;
if (ka9qfrag == 1) {
skb_reserve(skbn, frontlen + 2);
- skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
+
memcpy(skb_put(skbn, len), skb->data, len);
p = skb_push(skbn, 2);
@@ -186,7 +124,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
}
} else {
skb_reserve(skbn, frontlen + 1);
- skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
memcpy(skb_put(skbn, len), skb->data, len);
p = skb_push(skbn, 1);
*p = AX25_P_TEXT;
@@ -201,209 +138,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */
}
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_kick(ax25);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- /*
- * A DAMA slave is _required_ to work as normal AX.25L2V2
- * if no DAMA master is available.
- */
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25);
- break;
-#endif
- }
-}
-
-/*
- * This procedure is passed a buffer descriptor for an iframe. It builds
- * the rest of the control part of the frame and then writes it out.
- */
-static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
-{
- unsigned char *frame;
-
- if (skb == NULL)
- return;
-
- skb->nh.raw = skb->data;
-
- if (ax25->modulus == AX25_MODULUS) {
- frame = skb_push(skb, 1);
-
- *frame = AX25_I;
- *frame |= (poll_bit) ? AX25_PF : 0;
- *frame |= (ax25->vr << 5);
- *frame |= (ax25->vs << 1);
- } else {
- frame = skb_push(skb, 2);
-
- frame[0] = AX25_I;
- frame[0] |= (ax25->vs << 1);
- frame[1] = (poll_bit) ? AX25_EPF : 0;
- frame[1] |= (ax25->vr << 1);
- }
-
- ax25_start_idletimer(ax25);
-
- ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
-}
-
-void ax25_kick(ax25_cb *ax25)
-{
- struct sk_buff *skb, *skbn;
- int last = 1;
- unsigned short start, end, next;
-
- if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4)
- return;
-
- if (ax25->condition & AX25_COND_PEER_RX_BUSY)
- return;
-
- if (skb_peek(&ax25->write_queue) == NULL)
- return;
-
- start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs;
- end = (ax25->va + ax25->window) % ax25->modulus;
-
- if (start == end)
- return;
-
- ax25->vs = start;
-
- /*
- * Transmit data until either we're out of data to send or
- * the window is full. Send a poll on the final I frame if
- * the window is filled.
- */
-
- /*
- * Dequeue the frame and copy it.
- */
- skb = skb_dequeue(&ax25->write_queue);
-
- do {
- if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- skb_queue_head(&ax25->write_queue, skb);
- break;
- }
-
- if (skb->sk != NULL)
- skb_set_owner_w(skbn, skb->sk);
-
- next = (ax25->vs + 1) % ax25->modulus;
- last = (next == end);
-
- /*
- * Transmit the frame copy.
- * bke 960114: do not set the Poll bit on the last frame
- * in DAMA mode.
- */
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
- break;
-#endif
- }
-
- ax25->vs = next;
-
- /*
- * Requeue the original data frame.
- */
- skb_queue_tail(&ax25->ack_queue, skb);
-
- } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-
- if (!ax25_t1timer_running(ax25)) {
- ax25_stop_t3timer(ax25);
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- }
-}
-
-void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
-{
- struct sk_buff *skbn;
- unsigned char *ptr;
- int headroom;
-
- if (ax25->ax25_dev == NULL) {
- ax25_disconnect(ax25, ENETUNREACH);
- return;
- }
-
- headroom = ax25_addr_size(ax25->digipeat);
-
- if (skb_headroom(skb) < headroom) {
- if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) {
- printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n");
- kfree_skb(skb);
- return;
- }
-
- if (skb->sk != NULL)
- skb_set_owner_w(skbn, skb->sk);
-
- kfree_skb(skb);
- skb = skbn;
- }
-
- ptr = skb_push(skb, headroom);
-
- ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
-
- skb->dev = ax25->ax25_dev->dev;
-
- ax25_queue_xmit(skb);
-}
-
-/*
- * A small shim to dev_queue_xmit to add the KISS control byte, and do
- * any packet forwarding in operation.
- */
-void ax25_queue_xmit(struct sk_buff *skb)
-{
- unsigned char *ptr;
-
- skb->protocol = htons(ETH_P_AX25);
- skb->dev = ax25_fwd_dev(skb->dev);
-
- ptr = skb_push(skb, 1);
- *ptr = 0x00; /* KISS */
-
- dev_queue_xmit(skb);
-}
-
-int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
-{
- if (ax25->vs == nr) {
- ax25_frames_acked(ax25, nr);
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- return 1;
- } else {
- if (ax25->va != nr) {
- ax25_frames_acked(ax25, nr);
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- return 1;
- }
- }
- return 0;
+ ax25_kick(ax25);
}
diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c
index 254ff36fb..cf468c65f 100644
--- a/net/ax25/ax25_route.c
+++ b/net/ax25/ax25_route.c
@@ -1,278 +1,246 @@
/*
- * AX.25 release 037
+ * ax25_route.c: Routing table management for NEW-AX.25
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4K
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * This module:
- * This module 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.
+ * Comment:
*
- * Other kernels modules in this kit are generally BSD derived. See the copyright headers.
+ * Changelog:
*
- *
- * History
- * AX.25 020 Jonathan(G4KLX) First go.
- * AX.25 022 Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list.
- * AX.25 025 Alan(GW4PTS) First cut at autobinding by route scan.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
- * sock structure. Device removal now
- * removes the heard structure.
- * AX.25 029 Steven(GW7RRM) Added /proc information for uid/callsign mapping.
- * Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry.
- * AX.25 030 Jonathan(G4KLX) Added digi-peaters to routing table, and
- * ioctls to manipulate them. Added port
- * configuration.
- * AX.25 031 Jonathan(G4KLX) Added concept of default route.
- * Joerg(DL1BKE) ax25_rt_build_path() find digipeater list and device by
- * destination call. Needed for IP routing via digipeater
- * Jonathan(G4KLX) Added routing for IP datagram packets.
- * Joerg(DL1BKE) Changed routing for IP datagram and VC to use a default
- * route if available. Does not overwrite default routes
- * on route-table overflow anymore.
- * Joerg(DL1BKE) Fixed AX.25 routing of IP datagram and VC, new ioctl()
- * "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
- * on routes.
- * AX.25 033 Jonathan(G4KLX) Remove auto-router.
- * Joerg(DL1BKE) Moved BPQ Ethernet driver to separate device.
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * Jonathan(G4KLX) Support for packet forwarding.
- * Arnaldo C. Melo s/suser/capable/
+ * License: This module 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.
*/
-#include <linux/errno.h>
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
+#include <linux/spinlock.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
+#include <net/sock.h>
+#include <net/ax25_uid.h>
-static ax25_route *ax25_route_list;
+#include "af_ax25.h"
+#include "ax25_core.h"
+#include "ax25_route.h"
-static ax25_route *ax25_find_route(ax25_address *, struct net_device *);
+static struct ax25_route *ax25_route = NULL;
+rwlock_t ax25_rt_lock = RW_LOCK_UNLOCKED;
/*
- * small macro to drop non-digipeated digipeaters and reverse path
+ * delete all routes on a given device
*/
-static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out)
+void ax25_rt_device_down(struct net_device *dev)
{
- int k;
+ struct ax25_route *this, *last;
- for (k = 0; k < in->ndigi; k++)
- if (!in->repeated[k])
- break;
+ write_lock(&ax25_rt_lock);
+
+ this = ax25_route;
+ last = NULL;
- in->ndigi = k;
+ while (this) {
+ if (this->dev != dev) {
+ last = this;
+ this = this->next;
+ continue;
+ }
+ if (!last) {
+ ax25_route = this->next;
+ kfree(this);
+ this = ax25_route;
+ continue;
+ }
+ last->next = this->next;
+ kfree(this);
+ this = last->next;
+ }
- ax25_digi_invert(in, out);
+ write_unlock(&ax25_rt_lock);
}
-void ax25_rt_device_down(struct net_device *dev)
+int ax25_add_route(ax25_path_t *path, struct net_device *dev)
{
- ax25_route *s, *t, *ax25_rt = ax25_route_list;
-
- while (ax25_rt != NULL) {
- s = ax25_rt;
- ax25_rt = ax25_rt->next;
+ struct ax25_route *this;
+
+ write_lock(&ax25_rt_lock);
+
+ for (this = ax25_route; this; this = this->next) {
+ if (ax25cmp(&this->path.addr, &path->addr) == 0) {
+ this->path = *path;
+ this->dev = dev;
+ return 0;
+ }
+ }
+
+ if ((this = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ this->path = *path;
+ this->dev = dev;
+ this->ip_mode = ' ';
+ this->next = ax25_route;
+ ax25_route = this;
+
+ write_unlock(&ax25_rt_lock);
+
+ return 0;
+}
+
+int ax25_del_route(ax25_address *addr)
+{
+ struct ax25_route *this, *last;
+
+ write_lock(&ax25_rt_lock);
+
+ this = ax25_route;
+ last = NULL;
+ while (this != NULL) {
+ if (ax25cmp(&this->path.addr, addr) != 0) {
+ last = this;
+ this = this->next;
+ continue;
+ }
+ if (!last) {
+ ax25_route = this->next;
+ kfree(this);
+ return 0;
+ }
+ last->next = this->next;
+ kfree(this);
+ return 0;
+ }
+
+ write_unlock(&ax25_rt_lock);
+
+ return -EINVAL;
+}
+
+int ax25_ipopt_route(ax25_address *addr, unsigned char opt)
+{
+ struct ax25_route *this;
+ int err = -EINVAL;
+
+ write_lock(&ax25_rt_lock);
- if (s->dev == dev) {
- if (ax25_route_list == s) {
- ax25_route_list = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- } else {
- for (t = ax25_route_list; t != NULL; t = t->next) {
- if (t->next == s) {
- t->next = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- break;
- }
- }
+ for (this = ax25_route; this; this = this->next) {
+ if (ax25cmp(&this->path.addr, addr) == 0) {
+ switch(opt) {
+ case ' ':
+ case 'D':
+ case 'V':
+ case 'C':
+ this->ip_mode = opt;
+ err = 0;
+ break;
}
}
}
+
+ write_unlock(&ax25_rt_lock);
+
+ return err;
}
int ax25_rt_ioctl(unsigned int cmd, void *arg)
{
- unsigned long flags;
- ax25_route *s, *t, *ax25_rt;
struct ax25_routes_struct route;
struct ax25_route_opt_struct rt_option;
- ax25_dev *ax25_dev;
+ struct net_device *dev;
+ ax25_path_t ax25_path;
+ int err = -EINVAL;
int i;
switch (cmd) {
- case SIOCADDRT:
- if (copy_from_user(&route, arg, sizeof(route)))
- return -EFAULT;
- if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
- return -EINVAL;
- if (route.digi_count > AX25_MAX_DIGIS)
- return -EINVAL;
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) {
- if (ax25_rt->digipeat != NULL) {
- kfree(ax25_rt->digipeat);
- ax25_rt->digipeat = NULL;
- }
- if (route.digi_count != 0) {
- if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
- return -ENOMEM;
- ax25_rt->digipeat->lastrepeat = -1;
- ax25_rt->digipeat->ndigi = route.digi_count;
- for (i = 0; i < route.digi_count; i++) {
- ax25_rt->digipeat->repeated[i] = 0;
- ax25_rt->digipeat->calls[i] = route.digi_addr[i];
- }
- }
- return 0;
- }
- }
- if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL)
- return -ENOMEM;
- ax25_rt->callsign = route.dest_addr;
- ax25_rt->dev = ax25_dev->dev;
- ax25_rt->digipeat = NULL;
- ax25_rt->ip_mode = ' ';
- if (route.digi_count != 0) {
- if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- kfree(ax25_rt);
- return -ENOMEM;
- }
- ax25_rt->digipeat->lastrepeat = -1;
- ax25_rt->digipeat->ndigi = route.digi_count;
- for (i = 0; i < route.digi_count; i++) {
- ax25_rt->digipeat->repeated[i] = 0;
- ax25_rt->digipeat->calls[i] = route.digi_addr[i];
- }
- }
- save_flags(flags); cli();
- ax25_rt->next = ax25_route_list;
- ax25_route_list = ax25_rt;
- restore_flags(flags);
+ case SIOCADDRT:
+ /* do some sanity checks */
+ if (copy_from_user(&route, arg, sizeof(route))) {
+ err = -EFAULT;
+ break;
+ }
+ if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
+ break;
+ if (route.digi_count > AX25_MAX_DIGIS)
break;
- case SIOCDELRT:
- if (copy_from_user(&route, arg, sizeof(route)))
- return -EFAULT;
- if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
- return -EINVAL;
- ax25_rt = ax25_route_list;
- while (ax25_rt != NULL) {
- s = ax25_rt;
- ax25_rt = ax25_rt->next;
- if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
- if (ax25_route_list == s) {
- ax25_route_list = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- } else {
- for (t = ax25_route_list; t != NULL; t = t->next) {
- if (t->next == s) {
- t->next = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- break;
- }
- }
- }
- }
- }
+ ax25_path.addr = route.dest_addr;
+ for (i = 0; i < route.digi_count; i++)
+ ax25_path.digipeater[i] = route.digi_addr[i];
+ ax25_path.dcount = route.digi_count;
+ err = ax25_add_route(&ax25_path, dev);
+ break;
+
+ case SIOCDELRT:
+ /* sanity checks */
+ if (copy_from_user(&route, arg, sizeof(route))) {
+ err = -EFAULT;
+ break;
+ }
+ if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
break;
- case SIOCAX25OPTRT:
- if (copy_from_user(&rt_option, arg, sizeof(rt_option)))
- return -EFAULT;
- if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL)
- return -EINVAL;
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
- switch (rt_option.cmd) {
- case AX25_SET_RT_IPMODE:
- switch (rt_option.arg) {
- case ' ':
- case 'D':
- case 'V':
- ax25_rt->ip_mode = rt_option.arg;
- break;
- default:
- return -EINVAL;
- }
- break;
- default:
- return -EINVAL;
- }
- }
- }
+ err = ax25_del_route(&route.dest_addr);
+ break;
+
+ case SIOCAX25OPTRT:
+ /* sanity checks */
+ if (copy_from_user(&rt_option, arg, sizeof(rt_option))) {
+ err = -EFAULT;
+ break;
+ }
+ if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL)
break;
- default:
- return -EINVAL;
+ switch (rt_option.cmd) {
+ case AX25_SET_RT_IPMODE:
+ err = ax25_ipopt_route(&rt_option.dest_addr, rt_option.arg);
+ break;
+ }
}
- return 0;
+ return err;
}
int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
{
- ax25_route *ax25_rt;
+ struct ax25_route *ax25_rt;
int len = 0;
off_t pos = 0;
off_t begin = 0;
char *callsign;
int i;
- cli();
+ read_lock(&ax25_rt_lock);
len += sprintf(buffer, "callsign dev mode digipeaters\n");
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
+ for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0)
callsign = "default";
else
- callsign = ax2asc(&ax25_rt->callsign);
+ callsign = ax2asc(&ax25_rt->path.addr);
len += sprintf(buffer + len, "%-9s %-4s",
- callsign,
- ax25_rt->dev ? ax25_rt->dev->name : "???");
+ callsign,
+ ax25_rt->dev ? ax25_rt->dev->name : "???");
switch (ax25_rt->ip_mode) {
- case 'V':
- len += sprintf(buffer + len, " vc");
- break;
- case 'D':
- len += sprintf(buffer + len, " dg");
- break;
- default:
- len += sprintf(buffer + len, " *");
- break;
+ case 'V':
+ len += sprintf(buffer + len, " vc");
+ break;
+ case 'D':
+ len += sprintf(buffer + len, " dg");
+ break;
+ case 'C':
+ len += sprintf(buffer + len, " vj");
+ break;
+ default:
+ len += sprintf(buffer + len, " *");
+ break;
}
- if (ax25_rt->digipeat != NULL)
- for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
- len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
+ for (i = 0; i < ax25_rt->path.dcount; i++)
+ len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->path.digipeater[i]));
len += sprintf(buffer + len, "\n");
@@ -287,7 +255,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
break;
}
- sti();
+ read_unlock(&ax25_rt_lock);
*start = buffer + (offset - begin);
len -= (offset - begin);
@@ -300,153 +268,149 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
/*
* Find AX.25 route
*/
-static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev)
+struct ax25_route *ax25_find_route(ax25_address *addr)
{
- ax25_route *ax25_spe_rt = NULL;
- ax25_route *ax25_def_rt = NULL;
- ax25_route *ax25_rt;
+ struct ax25_route *ax25_spe_rt = NULL;
+ struct ax25_route *ax25_def_rt = NULL;
+ struct ax25_route *ax25_rt;
- /*
- * Bind to the physical interface we heard them on, or the default
- * route if none is found;
- */
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (dev == NULL) {
- if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
- ax25_spe_rt = ax25_rt;
- if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
- ax25_def_rt = ax25_rt;
- } else {
- if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev)
- ax25_spe_rt = ax25_rt;
- if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev)
- ax25_def_rt = ax25_rt;
- }
+ read_lock(&ax25_rt_lock);
+
+ for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ if (ax25cmp(&ax25_rt->path.addr, addr) == 0)
+ ax25_spe_rt = ax25_rt;
+ if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0)
+ ax25_def_rt = ax25_rt;
}
if (ax25_spe_rt != NULL)
return ax25_spe_rt;
+ read_unlock(&ax25_rt_lock);
+
return ax25_def_rt;
}
/*
- * Adjust path: If you specify a default route and want to connect
- * a target on the digipeater path but w/o having a special route
- * set before, the path has to be truncated from your target on.
+ * MW: This is the core of the digipeating stuff. For a given
+ * src/dest it finds the appropriate device and digipeaterpath
+ * to route to.
*/
-static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
+struct net_device *ax25_rt_set_addr(ax25_addr_t *out, ax25_addr_t *in, struct net_device *dev, struct net_device *out_dev)
{
- int k;
+ struct ax25_route *ax25rt;
+ ax25_address *next_dest;
+ ax25_address *dptr;
+ int more_digis;
- for (k = 0; k < digipeat->ndigi; k++) {
- if (ax25cmp(addr, &digipeat->calls[k]) == 0)
- break;
- }
+ /*
+ * find out where to go next. we route the packet either
+ * to the next digi behind us or to the destination. We
+ * NEVER route to the destination if there are digipeaters
+ * left.
+ */
+ more_digis = in->dcount - (in->lastrepeat+2);
+ if (more_digis > 0)
+ next_dest = &in->digipeater[in->lastrepeat+2];
+ else
+ next_dest = &in->dest;
- digipeat->ndigi = k;
-}
-
+ /*
+ * check for a route.
+ */
+ if ((ax25rt = ax25_find_route(next_dest)) != NULL)
+ out_dev = ax25rt->dev;
-/*
- * Find which interface to use.
- */
-int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
-{
- ax25_route *ax25_rt;
- ax25_address *call;
+ /*
+ * set up the digipeater path.
+ * for now we just copy the path of the incoming SABM
+ * up to the digipeater before us, if any.
+ */
+ out->dest = in->dest;
+ out->src = in->src;
- if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL)
- return -EHOSTUNREACH;
+ if (in->lastrepeat >= 0)
+ memcpy(out->digipeater, in->digipeater, sizeof(ax25_address) * (in->lastrepeat+1));
- if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
- return -EHOSTUNREACH;
+ /*
+ * then we fill in the callsign of the device the frame
+ * came in.
+ */
+ out->lastrepeat = in->lastrepeat+1;
+ out->dcount = out->lastrepeat+1;
+ dptr = &out->digipeater[(int) out->lastrepeat];
+ *dptr++ = *((ax25_address *)dev->dev_addr);
- if ((call = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
- return -EPERM;
- call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
+ /*
+ * insert the route to next_dest, if any.
+ */
+ if (ax25rt != NULL && ax25rt->path.dcount != 0) {
+ memcpy(dptr, ax25rt->path.digipeater, sizeof(ax25_address) * ax25rt->path.dcount);
+ out->dcount += ax25rt->path.dcount;
+ dptr += ax25rt->path.dcount;
}
- ax25->source_addr = *call;
-
- if (ax25_rt->digipeat != NULL) {
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
- return -ENOMEM;
- memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
- ax25_adjust_path(addr, ax25->digipeat);
+ /*
+ * append next_dest if we route to a waypoint
+ */
+ while (more_digis-- > 0 && out->dcount <= AX25_MAX_DIGIS) {
+ *dptr++ = *next_dest++;
+ out->dcount++;
}
-
- if (ax25->sk != NULL)
- ax25->sk->zapped = 0;
-
- return 0;
+ return out_dev;
}
/*
- * dl1bke 960117: build digipeater path
- * dl1bke 960301: use the default route if it exists
+ * Find the device to use
*/
-ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev)
+int ax25_rt_fillin_dev(ax25_cb *ax25, ax25_address *addr)
{
- static ax25_route route;
- ax25_route *ax25_rt;
-
- if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) {
- route.next = NULL;
- route.callsign = *addr;
- route.dev = dev;
- route.digipeat = NULL;
- route.ip_mode = ' ';
- return &route;
- }
+ struct ax25_route *ax25_rt;
- return ax25_rt;
-}
+ if ((ax25_rt = ax25_find_route(addr)) == NULL)
+ return -EHOSTUNREACH;
-struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi)
-{
- struct sk_buff *skbn;
- unsigned char *bp;
- int len;
+/* ax25_remove_cb(ax25); */
+ ax25_fillin_cb(ax25, ax25_rt->dev);
+/* ax25_insert_cb(ax25); */
- len = digi->ndigi * AX25_ADDR_LEN;
+ return 0;
+}
- if (skb_headroom(skb) < len) {
- if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
- printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n");
- return NULL;
- }
+/*
+ * Return the IP mode of a given callsign/device pair.
+ */
+char ax25_rt_mode_get(ax25_address *callsign)
+{
+ struct ax25_route *ax25_rt;
- if (skb->sk != NULL)
- skb_set_owner_w(skbn, skb->sk);
+ read_lock(&ax25_rt_lock);
- kfree_skb(skb);
+ for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next)
+ if (ax25cmp(&ax25_rt->path.addr, callsign) == 0)
+ return ax25_rt->ip_mode;
- skb = skbn;
- }
+ read_unlock(&ax25_rt_lock);
- bp = skb_push(skb, len);
+ return ' ';
+}
- ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
- return skb;
-}
/*
- * Free all memory associated with routing structures.
+ * Free all memory associated with routing and device structures.
*/
-void __exit ax25_rt_free(void)
+void ax25_rt_free(void)
{
- ax25_route *s, *ax25_rt = ax25_route_list;
+ struct ax25_route *s, *ax25_rt = ax25_route;
+
+ write_lock(&ax25_rt_lock);
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
-
- if (s->digipeat != NULL)
- kfree(s->digipeat);
-
kfree(s);
}
+
+ write_unlock(&ax25_rt_lock);
}
diff --git a/net/ax25/ax25_route.h b/net/ax25/ax25_route.h
new file mode 100644
index 000000000..edcb41a6e
--- /dev/null
+++ b/net/ax25/ax25_route.h
@@ -0,0 +1,25 @@
+#ifndef _AX25_ROUTE_H
+#define _AX25_ROUTE_H
+
+struct ax25_route {
+ struct ax25_route *next;
+ ax25_path_t path;
+ struct net_device *dev;
+ char ip_mode;
+};
+
+extern void ax25_rt_device_down(struct net_device*);
+extern int ax25_rt_ioctl(unsigned int, void*);
+extern int ax25_rt_get_info(char*, char**, off_t, int);
+extern struct net_device* ax25_rt_set_addr(ax25_addr_t*, ax25_addr_t*, struct net_device*, struct net_device*);
+extern int ax25_rt_autobind(ax25_cb*, ax25_address*);
+extern int ax25_rt_fillin_dev(ax25_cb*, ax25_address*);
+extern char ax25_rt_mode_get(ax25_address*);
+extern struct ax25_route* ax25_find_route(ax25_address*);
+
+extern int ax25_add_route(ax25_path_t*, struct net_device*);
+extern int ax25_del_route(ax25_address*);
+extern int ax25_ipopt_route(ax25_address*, unsigned char);
+void ax25_rt_free(void);
+
+#endif
diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c
deleted file mode 100644
index d1cfc3ff9..000000000
--- a/net/ax25/ax25_std_in.c
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from
- * the sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * Jonathan(G4KLX) Added IP mode registration.
- * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception.
- * Upgraded state machine for SABME.
- * Added arbitrary protocol id support.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * HaJo(DD8NE) Added Idle Disc Timer T5
- * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly
- * different behaviour. Fixed defrag
- * routine (I hope)
- * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed.
- * AX.25 033 Jonathan(G4KLX) Remove auto-router.
- * Modularisation changes.
- * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/ip.h> /* For ip_rcv */
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * State machine for state 1, Awaiting Connection State.
- * The handling of the timer(s) is in file ax25_std_timer.c.
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_SABME:
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
- break;
-
- case AX25_UA:
- if (pf) {
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_ESTABLISHED;
- /* For WAIT_SABM connections we will produce an accept ready socket here */
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- }
- }
- break;
-
- case AX25_DM:
- if (pf) {
- if (ax25->modulus == AX25_MODULUS) {
- ax25_disconnect(ax25, ECONNREFUSED);
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- }
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 2, Awaiting Release State.
- * The handling of the timer(s) is in file ax25_std_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- case AX25_UA:
- if (pf) ax25_disconnect(ax25, 0);
- break;
-
- case AX25_I:
- case AX25_REJ:
- case AX25_RNR:
- case AX25_RR:
- if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 3, Connected State.
- * The handling of the timer(s) is in file ax25_std_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
-{
- int queued = 0;
-
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- if (frametype == AX25_SABM) {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- } else {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- }
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->condition = 0x00;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25_requeue_frames(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- ax25_disconnect(ax25, ECONNRESET);
- break;
-
- case AX25_RR:
- case AX25_RNR:
- if (frametype == AX25_RR)
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- else
- ax25->condition |= AX25_COND_PEER_RX_BUSY;
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_check_iframes_acked(ax25, nr);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_REJ:
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_requeue_frames(ax25);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_I:
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- break;
- }
- if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
- ax25_frames_acked(ax25, nr);
- } else {
- ax25_check_iframes_acked(ax25, nr);
- }
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
- if (pf) ax25_std_enquiry_response(ax25);
- break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25->vr = ns; /* ax25->vr - 1 */
- ax25->condition &= ~AX25_COND_REJECT;
- if (pf) {
- ax25_std_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->condition |= AX25_COND_ACK_PENDING;
- ax25_start_t2timer(ax25);
- }
- }
- } else {
- if (ax25->condition & AX25_COND_REJECT) {
- if (pf) ax25_std_enquiry_response(ax25);
- } else {
- ax25->condition |= AX25_COND_REJECT;
- ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- }
- }
- break;
-
- case AX25_FRMR:
- case AX25_ILLEGAL:
- ax25_std_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
-
- default:
- break;
- }
-
- return queued;
-}
-
-/*
- * State machine for state 4, Timer Recovery State.
- * The handling of the timer(s) is in file ax25_std_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
-{
- int queued = 0;
-
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- if (frametype == AX25_SABM) {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- } else {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- }
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->condition = 0x00;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- ax25_requeue_frames(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- ax25_disconnect(ax25, ECONNRESET);
- break;
-
- case AX25_RR:
- case AX25_RNR:
- if (frametype == AX25_RR)
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- else
- ax25->condition |= AX25_COND_PEER_RX_BUSY;
- if (type == AX25_RESPONSE && pf) {
- ax25_stop_t1timer(ax25);
- ax25->n2count = 0;
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if (ax25->vs == ax25->va) {
- ax25_start_t3timer(ax25);
- ax25->state = AX25_STATE_3;
- } else {
- ax25_requeue_frames(ax25);
- }
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
- }
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_REJ:
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- if (pf && type == AX25_RESPONSE) {
- ax25_stop_t1timer(ax25);
- ax25->n2count = 0;
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if (ax25->vs == ax25->va) {
- ax25_start_t3timer(ax25);
- ax25->state = AX25_STATE_3;
- } else {
- ax25_requeue_frames(ax25);
- }
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
- }
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- ax25_requeue_frames(ax25);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_I:
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- break;
- }
- ax25_frames_acked(ax25, nr);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
- if (pf) ax25_std_enquiry_response(ax25);
- break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25->vr = ns; /* ax25->vr - 1 */
- ax25->condition &= ~AX25_COND_REJECT;
- if (pf) {
- ax25_std_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->condition |= AX25_COND_ACK_PENDING;
- ax25_start_t2timer(ax25);
- }
- }
- } else {
- if (ax25->condition & AX25_COND_REJECT) {
- if (pf) ax25_std_enquiry_response(ax25);
- } else {
- ax25->condition |= AX25_COND_REJECT;
- ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- }
- }
- break;
-
- case AX25_FRMR:
- case AX25_ILLEGAL:
- ax25_std_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
-
- default:
- break;
- }
-
- return queued;
-}
-
-/*
- * Higher level upcall for a LAPB frame
- */
-int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
-{
- int queued = 0, frametype, ns, nr, pf;
-
- frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
-
- switch (ax25->state) {
- case AX25_STATE_1:
- queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_2:
- queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_3:
- queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
- break;
- case AX25_STATE_4:
- queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
- break;
- }
-
- ax25_kick(ax25);
-
- return queued;
-}
diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c
deleted file mode 100644
index c868e0507..000000000
--- a/net/ax25/ax25_std_subr.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_out.c.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * The following routines are taken from page 170 of the 7th ARRL Computer
- * Networking Conference paper, as is the whole state machine.
- */
-
-void ax25_std_nr_error_recovery(ax25_cb *ax25)
-{
- ax25_std_establish_data_link(ax25);
-}
-
-void ax25_std_establish_data_link(ax25_cb *ax25)
-{
- ax25->condition = 0x00;
- ax25->n2count = 0;
-
- if (ax25->modulus == AX25_MODULUS)
- ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
-
- ax25_calculate_t1(ax25);
- ax25_stop_idletimer(ax25);
- ax25_stop_t3timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t1timer(ax25);
-}
-
-void ax25_std_transmit_enquiry(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
-}
-
-void ax25_std_enquiry_response(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE);
- else
- ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-}
-
-void ax25_std_timeout_response(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE);
- else
- ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-}
diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c
deleted file mode 100644
index 5a2d8771c..000000000
--- a/net/ax25/ax25_std_timer.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * This module:
- * This module 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.
- *
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
- * sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
- * AX.25 033 Jonathan(G4KLX) Modularisation functions.
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-void ax25_std_heartbeat_expiry(ax25_cb *ax25)
-{
- switch (ax25->state) {
-
- case AX25_STATE_0:
- /* Magic here: If we listen() and a new link dies before it
- is accepted() it isn't 'dead' so doesn't get removed. */
- if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
- ax25_destroy_socket(ax25);
- return;
- }
- break;
-
- case AX25_STATE_3:
- case AX25_STATE_4:
- /*
- * Check the state of the receive buffer.
- */
- if (ax25->sk != NULL) {
- if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) &&
- (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
- ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
- break;
- }
- }
- }
-
- ax25_start_heartbeat(ax25);
-}
-
-void ax25_std_t2timer_expiry(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_ACK_PENDING) {
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- ax25_std_timeout_response(ax25);
- }
-}
-
-void ax25_std_t3timer_expiry(ax25_cb *ax25)
-{
- ax25->n2count = 0;
- ax25_std_transmit_enquiry(ax25);
- ax25->state = AX25_STATE_4;
-}
-
-void ax25_std_idletimer_expiry(ax25_cb *ax25)
-{
- ax25_clear_queues(ax25);
-
- ax25->n2count = 0;
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25->state = AX25_STATE_2;
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_stop_t3timer(ax25);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
-}
-
-void ax25_std_t1timer_expiry(ax25_cb *ax25)
-{
- switch (ax25->state) {
- case AX25_STATE_1:
- if (ax25->n2count == ax25->n2) {
- if (ax25->modulus == AX25_MODULUS) {
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25->n2count = 0;
- ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- }
- } else {
- ax25->n2count++;
- if (ax25->modulus == AX25_MODULUS)
- ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
- }
- break;
-
- case AX25_STATE_2:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- }
- break;
-
- case AX25_STATE_3:
- ax25->n2count = 1;
- ax25_std_transmit_enquiry(ax25);
- ax25->state = AX25_STATE_4;
- break;
-
- case AX25_STATE_4:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- ax25_std_transmit_enquiry(ax25);
- }
- break;
- }
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
-}
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index 669789646..f8700bd8f 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -1,223 +1,77 @@
/*
- * AX.25 release 037
+ * ax25_subr.c: Subroutines for NEW-AX.25 state machine
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KL
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * This module:
- * This module 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.
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
*
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
+ * Changelog:
*
- * History
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. Removed
- * old BSD code.
- * AX.25 030 Jonathan(G4KLX) Added support for extended AX.25.
- * Added fragmentation support.
- * Darryl(G7LED) Added function ax25_requeue_frames() to split
- * it up from ax25_frames_acked().
- * AX.25 031 Joerg(DL1BKE) DAMA needs KISS Fullduplex ON/OFF.
- * Thus we have ax25_kiss_cmd() now... ;-)
- * Dave Brown(N2RJT)
- * Killed a silly bug in the DAMA code.
- * Joerg(DL1BKE) Found the real bug in ax25.h, sri.
- * AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of
- * enqueued buffers of a socket..
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
+ * License: This module 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.
*/
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
+#include <linux/tcp.h>
#include <linux/mm.h>
-#include <linux/interrupt.h>
+#include <net/sock.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_core.h"
+#include "ax25_vj.h"
+#include "ax25_timer.h"
/*
* This routine purges all the queues of frames.
*/
void ax25_clear_queues(ax25_cb *ax25)
{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&ax25->write_queue)) != NULL)
- kfree_skb(skb);
+ int i;
- while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL)
- kfree_skb(skb);
+ skb_queue_purge(&ax25->frag_queue);
+ skb_queue_purge(&ax25->rcv_queue);
+ skb_queue_purge(&ax25->write_queue);
+ skb_queue_purge(&ax25->ack_queue);
- while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL)
- kfree_skb(skb);
+ for (i = 0; i <= AX25_SEQMASK; i++) {
+ struct sk_buff *skb;
- while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL)
- kfree_skb(skb);
-}
-
-/*
- * This routine purges the input queue of those frames that have been
- * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
- * SDL diagram.
- */
-void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
-{
- struct sk_buff *skb;
-
- /*
- * Remove all the ack-ed frames from the ack queue.
- */
- if (ax25->va != nr) {
- while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) {
- skb = skb_dequeue(&ax25->ack_queue);
+ if ((skb = ax25->reseq[i].skb) != NULL) {
+ ax25->reseq[i].skb = NULL;
+ ax25->reseq[i].csum = 0;
kfree_skb(skb);
- ax25->va = (ax25->va + 1) % ax25->modulus;
}
}
}
-void ax25_requeue_frames(ax25_cb *ax25)
-{
- struct sk_buff *skb, *skb_prev = NULL;
-
- /*
- * Requeue all the un-ack-ed frames on the output queue to be picked
- * up by ax25_kick called from the timer. This arrangement handles the
- * possibility of an empty output queue.
- */
- while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
- if (skb_prev == NULL)
- skb_queue_head(&ax25->write_queue, skb);
- else
- skb_append(skb_prev, skb);
- skb_prev = skb;
- }
-}
-
/*
- * Validate that the value of nr is between va and vs. Return true or
- * false for testing.
+ * this is new
*/
-int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
+void ax25_tx_command(ax25_cb *ax25, int frametype, int poll_bit)
{
- unsigned short vc = ax25->va;
-
- while (vc != ax25->vs) {
- if (nr == vc) return 1;
- vc = (vc + 1) % ax25->modulus;
- }
-
- if (nr == ax25->vs) return 1;
-
- return 0;
+ if (poll_bit)
+ frametype |= 0x100;
+ ax25->tx_cmd = frametype;
+ ax25_kick(ax25);
}
-/*
- * This routine is the centralised routine for parsing the control
- * information for the different frame formats.
- */
-int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf)
+void ax25_tx_response(ax25_cb *ax25, int frametype, int poll_bit)
{
- unsigned char *frame;
- int frametype = AX25_ILLEGAL;
-
- frame = skb->data;
- *ns = *nr = *pf = 0;
-
- if (ax25->modulus == AX25_MODULUS) {
- if ((frame[0] & AX25_S) == 0) {
- frametype = AX25_I; /* I frame - carries NR/NS/PF */
- *ns = (frame[0] >> 1) & 0x07;
- *nr = (frame[0] >> 5) & 0x07;
- *pf = frame[0] & AX25_PF;
- } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
- frametype = frame[0] & 0x0F;
- *nr = (frame[0] >> 5) & 0x07;
- *pf = frame[0] & AX25_PF;
- } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
- frametype = frame[0] & ~AX25_PF;
- *pf = frame[0] & AX25_PF;
- }
- skb_pull(skb, 1);
- } else {
- if ((frame[0] & AX25_S) == 0) {
- frametype = AX25_I; /* I frame - carries NR/NS/PF */
- *ns = (frame[0] >> 1) & 0x7F;
- *nr = (frame[1] >> 1) & 0x7F;
- *pf = frame[1] & AX25_EPF;
- skb_pull(skb, 2);
- } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
- frametype = frame[0] & 0x0F;
- *nr = (frame[1] >> 1) & 0x7F;
- *pf = frame[1] & AX25_EPF;
- skb_pull(skb, 2);
- } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
- frametype = frame[0] & ~AX25_PF;
- *pf = frame[0] & AX25_PF;
- skb_pull(skb, 1);
- }
- }
-
- return frametype;
-}
-
-/*
- * This routine is called when the HDLC layer internally generates a
- * command or response for the remote machine ( eg. RR, UA etc. ).
- * Only supervisory or unnumbered frames are processed.
- */
-void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
-{
- struct sk_buff *skb;
- unsigned char *dptr;
-
- if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL)
- return;
-
- skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat));
-
- skb->nh.raw = skb->data;
-
- /* Assume a response - address structure for DTE */
- if (ax25->modulus == AX25_MODULUS) {
- dptr = skb_put(skb, 1);
- *dptr = frametype;
- *dptr |= (poll_bit) ? AX25_PF : 0;
- if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */
- *dptr |= (ax25->vr << 5);
- } else {
- if ((frametype & AX25_U) == AX25_U) {
- dptr = skb_put(skb, 1);
- *dptr = frametype;
- *dptr |= (poll_bit) ? AX25_PF : 0;
- } else {
- dptr = skb_put(skb, 2);
- dptr[0] = frametype;
- dptr[1] = (ax25->vr << 1);
- dptr[1] |= (poll_bit) ? AX25_EPF : 0;
- }
- }
-
- ax25_transmit_buffer(ax25, skb, type);
+ if (poll_bit)
+ frametype |= 0x100;
+ ax25->tx_rsp = frametype;
+ ax25_kick(ax25);
}
/*
@@ -225,44 +79,39 @@ void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
*
* Note: src here is the sender, thus it's the target of the DM
*/
-void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi)
+void ax25_return_dm(struct net_device *dev, ax25_pktinfo *pkt)
{
struct sk_buff *skb;
- char *dptr;
- ax25_digi retdigi;
+ unsigned char *dptr;
+ int sizeof_addr;
+ ax25_addr_t addr;
- if (dev == NULL)
- return;
+ sizeof_addr = ax25_sizeof_addr(&pkt->addr);
- if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(digi) + 1, GFP_ATOMIC)) == NULL)
+ if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + sizeof_addr + 1, GFP_ATOMIC)) == NULL)
return; /* Next SABM will get DM'd */
- skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(digi));
- skb->nh.raw = skb->data;
-
- ax25_digi_invert(digi, &retdigi);
-
- dptr = skb_put(skb, 1);
+ skb_reserve(skb, AX25_BPQ_HEADER_LEN + sizeof_addr);
+ ax25_invert_addr(&pkt->addr, &addr);
- *dptr = AX25_DM | AX25_PF;
+ *skb_put(skb, 1) = AX25_DM|AX25_PF;
/*
* Do the address ourselves
*/
- dptr = skb_push(skb, ax25_addr_size(digi));
- dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS);
-
- skb->dev = dev;
-
- ax25_queue_xmit(skb);
+ skb->nh.raw = skb->data;
+ dptr = skb_push(skb, sizeof_addr);
+ dptr += ax25_build_addr(dptr, &addr, AX25_RESPONSE, AX25_SEQMASK);
+ ax25_send_unproto(skb, dev);
}
/*
* Exponential backoff for AX.25
*/
-void ax25_calculate_t1(ax25_cb *ax25)
+unsigned short ax25_calculate_t1(ax25_cb *ax25)
{
- int n, t = 2;
+ int n;
+ int t = 1;
switch (ax25->backoff) {
case 0:
@@ -273,52 +122,103 @@ void ax25_calculate_t1(ax25_cb *ax25)
break;
case 2:
- for (n = 0; n < ax25->n2count; n++)
- t *= 2;
- if (t > 8) t = 8;
+ t <<= (ax25->n2count < 8 ? ax25->n2count : 8);
break;
}
- ax25->t1 = t * ax25->rtt;
-}
+ n = (t * ax25->rtt);
-/*
- * Calculate the Round Trip Time
- */
-void ax25_calculate_rtt(ax25_cb *ax25)
-{
- if (ax25->backoff == 0)
- return;
+ if (n > AX25_T1CLAMPHI)
+ return AX25_T1CLAMPHI;
- if (ax25_t1timer_running(ax25) && ax25->n2count == 0)
- ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10;
+ if (n < AX25_T1CLAMPLO)
+ return AX25_T1CLAMPLO;
- if (ax25->rtt < AX25_T1CLAMPLO)
- ax25->rtt = AX25_T1CLAMPLO;
+ return n;
+}
- if (ax25->rtt > AX25_T1CLAMPHI)
- ax25->rtt = AX25_T1CLAMPHI;
+void ax25_close_socket(struct sock *sk, int reason)
+{
+ sk->err = reason;
+ sk->shutdown = SHUTDOWN_MASK;
+ sk->state = TCP_CLOSE;
+ sk->state_change(sk);
+ sk->dead = 1;
}
void ax25_disconnect(ax25_cb *ax25, int reason)
{
ax25_clear_queues(ax25);
+ ax25_link_failed(ax25, reason);
+ write_lock(&ax25->timer_lock);
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->idletimer = 0;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+ ax25->killtimer = 90 * AX25_SLOWHZ;
+ ax25->state = AX25_STATE_0;
+ write_unlock(&ax25->timer_lock);
+ if (ax25->peer && ax25->peer->state > AX25_STATE_2)
+ ax25_set_cond(ax25->peer, AX25_COND_RELEASE);
+ if (ax25->slcomp) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+}
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_stop_t3timer(ax25);
- ax25_stop_idletimer(ax25);
+void ax25_nr_error_recovery(ax25_cb *ax25)
+{
+ if (ax25->peer != NULL)
+ ax25->peer->condition |= AX25_COND_RELEASE;
- ax25->state = AX25_STATE_0;
+ ax25_clear_queues(ax25);
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_2;
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+}
- ax25_link_failed(ax25, reason);
+void ax25_establish_data_link(ax25_cb *ax25)
+{
+ ax25->timer_lock= RW_LOCK_UNLOCKED;
+ ax25->state = AX25_STATE_1;
+ ax25->n2count = 0;
+ ax25->ack_timer = 0;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+ ax25_set_cond(ax25, AX25_COND_SETUP);
+ ax25_start_t1(ax25);
+
+ if (ax25->seqmask == AX25_SEQMASK)
+ ax25_tx_command(ax25, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(ax25, AX25_SABME, AX25_POLLON);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = reason;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+}
+
+void ax25_transmit_enquiry(ax25_cb *ax25)
+{
+ int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR;
+ ax25_tx_command(ax25, ft, AX25_POLLON);
+ ax25_start_t1(ax25);
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ if (DAMA_STATE(ax25) & DAMA_SLAVE) AX25_PTR(ax25->device)->dama_polled = 1;
+}
+
+void ax25_enquiry_response(ax25_cb *ax25)
+{
+ int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR;
+ ax25_tx_response(ax25, ft, AX25_POLLON);
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+}
+
+void ax25_timeout_response(ax25_cb *ax25)
+{
+ int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR;
+ ax25_tx_response(ax25, ft, AX25_POLLOFF);
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
}
diff --git a/net/ax25/ax25_subr.h b/net/ax25/ax25_subr.h
new file mode 100644
index 000000000..026217ae1
--- /dev/null
+++ b/net/ax25/ax25_subr.h
@@ -0,0 +1,38 @@
+#ifndef _AX25_SUBR_H
+#define _AX25_SUBR_H
+
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+extern unsigned char *ax25_parse_addr(unsigned char*, int, ax25_pktinfo*);
+extern unsigned short ax25_calculate_t1(ax25_cb*);
+extern void ax25_return_dm(struct net_device*, ax25_pktinfo*);
+extern void ax25_tx_command(ax25_cb*, int, int);
+extern void ax25_tx_response(ax25_cb*, int, int);
+extern void ax25_clear_queues(ax25_cb*);
+extern void ax25_disconnect(ax25_cb*, int);
+extern void ax25_nr_error_recovery(ax25_cb*);
+extern void ax25_establish_data_link(ax25_cb*);
+extern void ax25_transmit_enquiry(ax25_cb*);
+extern void ax25_enquiry_response(ax25_cb*);
+extern void ax25_timeout_response(ax25_cb*);
+extern void ax25_close_socket(struct sock*, int);
+
+
+extern inline void ax25_set_cond(ax25_cb* ax25, unsigned int cond)
+{
+ ax25->condition |= cond;
+}
+
+extern inline void ax25_clr_cond(ax25_cb* ax25, unsigned int cond)
+{
+ ax25->condition &= ~cond;
+}
+
+extern inline void ax25_start_t1(ax25_cb* ax25)
+{
+ ax25_set_cond(ax25, AX25_COND_START_T1);
+ ax25->wrt_timer = 0;
+}
+
+#endif
diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c
index 01e7596f3..fdc871bc1 100644
--- a/net/ax25/ax25_timer.c
+++ b/net/ax25/ax25_timer.c
@@ -1,256 +1,455 @@
/*
- * AX.25 release 037
+ * ax25_timer.c: timer subroutines for NEW-AX.25
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KLX
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * This module:
- * This module 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.
+ * Comment:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
- * sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
- * AX.25 033 Jonathan(G4KLX) Modularisation functions.
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into separate files.
- * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with
- * standard AX.25 mode.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Tomi(OH2BNS) Fixed heartbeat expiry (check ax25_dev).
+ * Changelog:
+ *
+ * License: This module 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.
*/
+
#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
#include <linux/netdevice.h>
-#include <linux/skbuff.h>
+#include <linux/tcp.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-static void ax25_heartbeat_expiry(unsigned long);
-static void ax25_t1timer_expiry(unsigned long);
-static void ax25_t2timer_expiry(unsigned long);
-static void ax25_t3timer_expiry(unsigned long);
-static void ax25_idletimer_expiry(unsigned long);
-
-void ax25_start_heartbeat(ax25_cb *ax25)
-{
- del_timer(&ax25->timer);
+#include <net/ax25dev.h>
+#include <net/ax25.h>
- ax25->timer.data = (unsigned long)ax25;
- ax25->timer.function = &ax25_heartbeat_expiry;
- ax25->timer.expires = jiffies + 5 * HZ;
+#include "ax25_core.h"
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
- add_timer(&ax25->timer);
-}
+static void ax25_wrt_timeout(ax25_cb *);
-void ax25_start_t1timer(ax25_cb *ax25)
+/*
+ * AX.25 TIMER
+ *
+ * This routine is called every 100ms. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+void ax25_timer(ax25_cb *ax25)
{
- del_timer(&ax25->t1timer);
+ int wrt_timeout;
+
+ switch (ax25->state) {
+ case AX25_LISTEN:
+ /*
+ * never kill listening sockets. let the be moved to
+ * AX25_STATE_0 first. ax25_release() does this.
+ */
+ return;
+
+ case AX25_STATE_0:
+ /*
+ * don't kill if a frame signalling a state change is
+ * still pending
+ */
+ if (ax25->condition & AX25_COND_STATE_CHANGE)
+ break;
+
+ if (ax25->sk) {
+ /*
+ * ax25_release() sets ax25->sk = NULL, releasing the
+ * connection between the socket and the underlying control
+ * structure, we handle that further down
+ */
+
+ /* almost dead, notify socket */
+ if (!ax25->sk->dead)
+ ax25_close_socket(ax25->sk, 0);
+ break;
+ }
+
+ /*
+ * wait a certain time before destroying the control block
+ */
+ if (ax25->killtimer > 0 && --ax25->killtimer > 0)
+ break;
+
+ /*
+ * if a peer exists in STATE [12], disconnect it. this handles
+ * "connection timed out" for digipeated connections.
+ *
+ * else if no peer exists, destroy this control block.
+ */
+ if (ax25->peer) {
+ if (ax25->peer->state < AX25_STATE_3)
+ ax25_destroy_cb(ax25->peer);
+ else
+ break;
+ }
+
+ ax25_destroy_cb(ax25);
+ return;
+
+ case AX25_STATE_3:
+ write_lock(&ax25->timer_lock);
+ if (!ax25->peer && !ax25->sk && ax25->idletimer > 0 && --ax25->idletimer == 0)
+ ax25_set_cond(ax25, AX25_COND_RELEASE);
+ write_unlock(&ax25->timer_lock);
+ /* fall through */
+ case AX25_STATE_4:
+ write_lock(&ax25->timer_lock);
+ if (ax25->condition & AX25_COND_ACK_PENDING) {
+ if (ax25->ack_timer > 0)
+ ax25->ack_timer--;
+ else
+ ax25_timeout_response(ax25);
+ }
+
+ /*
+ * Our peer connection has seen a DISC or DM and we're about to change to
+ * state 2. We don't DISC until we've delivered all queued data
+ */
+ if (ax25->condition & AX25_COND_RELEASE) {
+ if (!skb_queue_len(&ax25->write_queue) && !skb_queue_len(&ax25->ack_queue)) {
+ ax25->n2count = 1;
+ ax25->ack_timer = 0;
+ ax25_start_t1(ax25);
+ ax25_clr_cond(ax25, AX25_COND_RELEASE);
+ ax25->state = AX25_STATE_2;
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ }
+
+ /*
+ * Check the state of the receive buffer. This is part of the flow control
+ * and should be done in ax25_rcvmsg();
+ */
+ } else if (ax25->sk && skb_queue_len(&ax25->rcv_queue)) {
+ struct sk_buff *skb;
+
+ while ((skb = skb_peek(&ax25->rcv_queue)) != NULL) {
+ if (ax25->sk->shutdown & RCV_SHUTDOWN)
+ break;
+ if (atomic_read(&ax25->sk->rmem_alloc) + skb->truesize < ax25->sk->rcvbuf) {
+ skb_dequeue(&ax25->rcv_queue);
+ sock_queue_rcv_skb(ax25->sk, skb);
+ } else
+ break;
+ }
+ if (skb == NULL) {
+ ax25_clr_cond(ax25, AX25_COND_OWN_RX_BUSY);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25->state = AX25_STATE_4;
+ ax25_transmit_enquiry(ax25);
+ }
+ }
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ default: /* state 1/2 */
+ break;
+ }
+
+ /* ax25_wrt_timout() must be called unlocked as ax25_disconnect()
+ * sets ax25->timer_lock
+ */
+
+ write_lock(&ax25->timer_lock);
+ wrt_timeout = (ax25->wrt_timer > 0 && --ax25->wrt_timer == 0);
+ write_unlock(&ax25->timer_lock);
+
+ if (wrt_timeout)
+ ax25_wrt_timeout(ax25);
- ax25->t1timer.data = (unsigned long)ax25;
- ax25->t1timer.function = &ax25_t1timer_expiry;
- ax25->t1timer.expires = jiffies + ax25->t1;
- add_timer(&ax25->t1timer);
}
-void ax25_start_t2timer(ax25_cb *ax25)
+static void ax25_wrt_timeout(ax25_cb *ax25)
{
- del_timer(&ax25->t2timer);
+ ax25_cb *peer = ax25->peer;
+ switch (ax25->state) {
+ case AX25_STATE_1:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->seqmask == AX25_SEQMASK) {
+ ax25_disconnect(ax25, ETIMEDOUT);
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, ETIMEDOUT);
+ else if (peer) {
+ ax25_disconnect(peer, 0);
+ }
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ ax25->n2count = 0;
+ ax25_start_t1(ax25);
+ ax25_tx_command(ax25, AX25_SABM, AX25_POLLON);
+ }
+ } else {
+ ax25->n2count++;
+ ax25_start_t1(ax25);
+ if (ax25->seqmask == AX25_SEQMASK)
+ ax25_tx_command(ax25, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(ax25, AX25_SABME, AX25_POLLON);
+ }
+ break;
+
+ case AX25_STATE_2:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_disconnect(ax25, 0);
+ } else {
+ ax25->n2count++;
+ ax25_start_t1(ax25);
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ }
+ break;
+
+ case AX25_STATE_3:
+ ax25->vs_rtt = -1;
+ ax25->n2count = 1;
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_4;
+
+ /*
+ * We are angry now.
+ * Use CSMA. If master responds to poll, circuit will be saved
+ * and dama_mode will be re-enabled automagically in ax25_in.c
+ */
+ ax25_dev_set_dama(ax25->device, 0);
+ ax25_transmit_enquiry(ax25);
+ break;
+
+ case AX25_STATE_4:
+ if (ax25->n2count == ax25->n2) {
+ if (peer) {
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ ax25->killtimer = 90 * AX25_SLOWHZ;
+ } else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ETIMEDOUT);
+ ax25_disconnect(ax25, ETIMEDOUT);
+ } else {
+ ax25->n2count++;
+ ax25_transmit_enquiry(ax25);
+ }
+ break;
+ }
+}
- ax25->t2timer.data = (unsigned long)ax25;
- ax25->t2timer.function = &ax25_t2timer_expiry;
- ax25->t2timer.expires = jiffies + ax25->t2;
- add_timer(&ax25->t2timer);
-}
+/* -------------------------------------------------------------------- */
+
+/************************************************************************/
+/* Module support functions follow. */
+/************************************************************************/
+
+static struct protocol_struct {
+ struct protocol_struct *next;
+ unsigned int pid;
+ int (*func)(struct sk_buff *, ax25_cb *);
+} *protocol_list = NULL;
+
+static struct linkfail_struct {
+ struct linkfail_struct *next;
+ void (*func)(ax25_cb *, int);
+} *linkfail_list = NULL;
-void ax25_start_t3timer(ax25_cb *ax25)
+static struct listen_struct {
+ struct listen_struct *next;
+ ax25_address callsign;
+ struct net_device *dev;
+} *listen_list = NULL;
+
+int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
{
- del_timer(&ax25->t3timer);
+ struct protocol_struct *protocol;
+ unsigned long flags;
- if (ax25->t3 > 0) {
- ax25->t3timer.data = (unsigned long)ax25;
- ax25->t3timer.function = &ax25_t3timer_expiry;
- ax25->t3timer.expires = jiffies + ax25->t3;
+ if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
+ return 0;
- add_timer(&ax25->t3timer);
- }
+ if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ protocol->pid = pid;
+ protocol->func = func;
+
+ save_flags(flags);
+ cli();
+
+ protocol->next = protocol_list;
+ protocol_list = protocol;
+
+ restore_flags(flags);
+
+ return 1;
}
-void ax25_start_idletimer(ax25_cb *ax25)
+void ax25_protocol_release(unsigned int pid)
{
- del_timer(&ax25->idletimer);
+ struct protocol_struct *s, *protocol = protocol_list;
+ unsigned long flags;
+
+ if (protocol == NULL)
+ return;
- if (ax25->idle > 0) {
- ax25->idletimer.data = (unsigned long)ax25;
- ax25->idletimer.function = &ax25_idletimer_expiry;
- ax25->idletimer.expires = jiffies + ax25->idle;
+ save_flags(flags);
+ cli();
- add_timer(&ax25->idletimer);
+ if (protocol->pid == pid) {
+ protocol_list = protocol->next;
+ restore_flags(flags);
+ kfree(protocol);
+ return;
}
-}
-void ax25_stop_heartbeat(ax25_cb *ax25)
-{
- del_timer(&ax25->timer);
-}
+ while (protocol != NULL && protocol->next != NULL) {
+ if (protocol->next->pid == pid) {
+ s = protocol->next;
+ protocol->next = protocol->next->next;
+ restore_flags(flags);
+ kfree(s);
+ return;
+ }
-void ax25_stop_t1timer(ax25_cb *ax25)
-{
- del_timer(&ax25->t1timer);
-}
+ protocol = protocol->next;
+ }
-void ax25_stop_t2timer(ax25_cb *ax25)
-{
- del_timer(&ax25->t2timer);
+ restore_flags(flags);
}
-void ax25_stop_t3timer(ax25_cb *ax25)
+int ax25_linkfail_register(void (*func)(ax25_cb *, int))
{
- del_timer(&ax25->t3timer);
-}
+ struct linkfail_struct *linkfail;
+ unsigned long flags;
-void ax25_stop_idletimer(ax25_cb *ax25)
-{
- del_timer(&ax25->idletimer);
+ if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ linkfail->func = func;
+
+ save_flags(flags);
+ cli();
+
+ linkfail->next = linkfail_list;
+ linkfail_list = linkfail;
+
+ restore_flags(flags);
+
+ return 1;
}
-int ax25_t1timer_running(ax25_cb *ax25)
+void ax25_linkfail_release(void (*func)(ax25_cb *, int))
{
- return timer_pending(&ax25->t1timer);
+ struct linkfail_struct *s, *linkfail = linkfail_list;
+ unsigned long flags;
+
+ if (linkfail == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ if (linkfail->func == func) {
+ linkfail_list = linkfail->next;
+ restore_flags(flags);
+ kfree(linkfail);
+ return;
+ }
+
+ while (linkfail != NULL && linkfail->next != NULL) {
+ if (linkfail->next->func == func) {
+ s = linkfail->next;
+ linkfail->next = linkfail->next->next;
+ restore_flags(flags);
+ kfree(s);
+ return;
+ }
+
+ linkfail = linkfail->next;
+ }
+
+ restore_flags(flags);
}
-unsigned long ax25_display_timer(struct timer_list *timer)
+static char empty_addr[AX25_ADDR_LEN] = {0, 0, 0, 0, 0, 0, 0};
+
+int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
{
- if (!timer_pending(timer))
+ ax25_cb *ax25;
+
+ ax25_addr_t addr;
+
+ addr.dcount = 0;
+ addr.src = *callsign;
+ memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN);
+
+ if (ax25_find_cb(&addr, dev) != NULL)
+ return 0;
+
+ if ((ax25 = ax25_create_cb()) == NULL)
return 0;
- return timer->expires - jiffies;
+ ax25->addr.src = *callsign;
+
+ ax25_fillin_cb(ax25, dev);
+ ax25_insert_cb(ax25);
+
+ return 1;
}
-static void ax25_heartbeat_expiry(unsigned long param)
+void ax25_listen_release(ax25_address *callsign, struct net_device *dev)
{
- ax25_cb *ax25 = (ax25_cb *)param;
- int proto = AX25_PROTO_STD_SIMPLEX;
+ ax25_cb *ax25;
- if (ax25->ax25_dev)
- proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL];
+ ax25_addr_t addr;
- switch (proto) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_heartbeat_expiry(ax25);
- break;
+ addr.dcount = 0;
+ addr.src = *callsign;
+ memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN);
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_heartbeat_expiry(ax25);
- else
- ax25_std_heartbeat_expiry(ax25);
- break;
-#endif
- }
+ if ((ax25 = ax25_find_cb(&addr, dev)) != NULL)
+ ax25_destroy_cb(ax25);
}
-static void ax25_t1timer_expiry(unsigned long param)
+int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct protocol_struct *protocol;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t1timer_expiry(ax25);
- break;
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return protocol->func;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave)
- ax25_std_t1timer_expiry(ax25);
- break;
-#endif
- }
+ return NULL;
}
-static void ax25_t2timer_expiry(unsigned long param)
+int ax25_listen_mine(ax25_address *callsign, struct net_device *dev)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct listen_struct *listen;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t2timer_expiry(ax25);
- break;
+ for (listen = listen_list; listen != NULL; listen = listen->next)
+ if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
+ return 1;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave)
- ax25_std_t2timer_expiry(ax25);
- break;
-#endif
- }
+ return 0;
}
-static void ax25_t3timer_expiry(unsigned long param)
+void ax25_link_failed(ax25_cb *ax25, int reason)
{
- ax25_cb *ax25 = (ax25_cb *)param;
-
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t3timer_expiry(ax25);
- break;
+ struct linkfail_struct *linkfail;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_t3timer_expiry(ax25);
- else
- ax25_std_t3timer_expiry(ax25);
- break;
-#endif
- }
+ for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
+ (linkfail->func)(ax25, reason);
}
-static void ax25_idletimer_expiry(unsigned long param)
+int ax25_protocol_is_registered(unsigned int pid)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct protocol_struct *protocol;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_idletimer_expiry(ax25);
- break;
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return 1;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_idletimer_expiry(ax25);
- else
- ax25_std_idletimer_expiry(ax25);
- break;
-#endif
- }
+ return 0;
}
diff --git a/net/ax25/ax25_timer.h b/net/ax25/ax25_timer.h
new file mode 100644
index 000000000..010e8e876
--- /dev/null
+++ b/net/ax25/ax25_timer.h
@@ -0,0 +1,11 @@
+/*
+ * Interface declaration for timer functions
+ *
+ * Joerg Reuter DL1BKE 2000-07-06
+ *
+ */
+
+
+#ifndef _AX25_TIMER_H
+#define _AX25_TIMER_H
+#endif
diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c
index 603d8b8cc..4a41313de 100644
--- a/net/ax25/ax25_uid.c
+++ b/net/ax25/ax25_uid.c
@@ -1,124 +1,146 @@
/*
- * AX.25 release 037
+ * ax25_uid.c: Callsign/UID mapper. This is in kernel space for security on mul
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Matthias Welwarsky (DG2FEF)
+ * Joerg Reuter (DL1BKE)
*
- * This module:
- * This module 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.
+ * Comment:
*
- * History
- * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
+ * Changelog:
+ * 2001-02-06 Joerg Reuter DL1BKE <jreuter@yaina.de>
+ * extended policy scheme implemented
+ *
+ * License: This module 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.
*/
-#include <linux/errno.h>
#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
+#include <linux/errno.h>
#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/netfilter.h>
-#include <linux/sysctl.h>
-#include <net/ip.h>
-#include <net/arp.h>
+#include <linux/netdevice.h>
+#include <linux/ax25.h>
-/*
- * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
- */
+#include <net/ax25_uid.h>
+
+#include "af_ax25.h"
+t_ax25_uid_policy ax25_uid_policy = AX25_UID_POLICY_ARBITRARY_CALLSIGN;
static ax25_uid_assoc *ax25_uid_list;
-int ax25_uid_policy = 0;
+ax25_address *ax25_find_by_uid(uid_t uid)
+{
+ ax25_uid_assoc *a;
-ax25_address *ax25_findbyuid(uid_t uid)
+ for (a = ax25_uid_list; a != NULL; a = a->next) {
+ if (a->uid == uid)
+ return &a->call;
+ }
+
+ return NULL;
+}
+
+ax25_address *ax25_find_match_for_uid(uid_t uid, ax25_address *provided, char *device)
{
- ax25_uid_assoc *ax25_uid;
+ int res;
+ ax25_uid_assoc *a;
+
+ /* no callsign given? find one! */
+ if (ax25cmp(provided, &null_ax25_address) == 0)
+ {
+ for (a = ax25_uid_list; a != NULL; a = a->next) {
+ if (a->uid == uid)
+ {
+ if (device != NULL && strcmp(device, a->device))
+ continue;
+ return &a->call;
+ }
+ }
+ }
+
+ /* any user can choose an arbitrary callsign? */
+ if (ax25_uid_policy == 0)
+ return provided;
+
+ /* walk the list... */
+ for (a = ax25_uid_list; a != NULL ; a = a->next) {
+ if (a->uid == uid)
+ {
+ /* limited to a device? */
+ if (device != NULL && strcmp(device, a->device))
+ continue;
- for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
- if (ax25_uid->uid == uid)
- return &ax25_uid->call;
+ /* user can choose any callsign? */
+ if (ax25cmp(&a->call, &null_ax25_address) == 0)
+ return provided;
+
+ res = ax25cmp(provided, &a->call);
+
+ /* exact match, or only SSID differ (when allowed): okay */
+ if ( res == 0 ||
+ (res == 2 && ax25_uid_policy == AX25_UID_POLICY_ANY_SSID) )
+ return provided;
+ }
}
return NULL;
}
+/*
+ * TODO: move this stuff to procfs
+ * general idea: echo "add 503 dl1bke scc0" >/proc/net/ax25_calls
+ * echo "del 502 dk0tux *" >/proc/net/ax25_calls
+ */
+
int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
{
- ax25_uid_assoc *s, *ax25_uid;
- unsigned long flags;
+ ax25_uid_assoc *a;
switch (cmd) {
case SIOCAX25GETUID:
- for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
- if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
- return ax25_uid->uid;
+ for (a = ax25_uid_list; a != NULL; a = a->next) {
+ if (ax25cmp(&sax->sax25_call, &a->call) == 0)
+ return a->uid;
}
return -ENOENT;
case SIOCAX25ADDUID:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (ax25_findbyuid(sax->sax25_uid))
+ if (ax25_find_by_uid(sax->sax25_uid))
return -EEXIST;
if (sax->sax25_uid == 0)
return -EINVAL;
- if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL)
+ a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL);
+ if (a == NULL)
return -ENOMEM;
- ax25_uid->uid = sax->sax25_uid;
- ax25_uid->call = sax->sax25_call;
- save_flags(flags); cli();
- ax25_uid->next = ax25_uid_list;
- ax25_uid_list = ax25_uid;
- restore_flags(flags);
+ a->uid = sax->sax25_uid;
+ a->call = sax->sax25_call;
+ a->device[0] = '\0';
+ a->next = ax25_uid_list;
+ ax25_uid_list = a;
return 0;
- case SIOCAX25DELUID:
+ case SIOCAX25DELUID: {
+ ax25_uid_assoc **l;
+
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
- if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
- break;
- }
- if (ax25_uid == NULL)
- return -ENOENT;
- save_flags(flags); cli();
- if ((s = ax25_uid_list) == ax25_uid) {
- ax25_uid_list = s->next;
- restore_flags(flags);
- kfree(ax25_uid);
- return 0;
- }
- while (s != NULL && s->next != NULL) {
- if (s->next == ax25_uid) {
- s->next = ax25_uid->next;
- restore_flags(flags);
- kfree(ax25_uid);
+ l = &ax25_uid_list;
+ while ((*l) != NULL) {
+ if (ax25cmp(&((*l)->call), &(sax->sax25_call)) == 0) {
+ a = *l;
+ *l = (*l)->next;
+ kfree(a);
return 0;
}
- s = s->next;
+
+ l = &((*l)->next);
}
- restore_flags(flags);
return -ENOENT;
+ }
default:
return -EINVAL;
@@ -127,19 +149,17 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
return -EINVAL; /*NOTREACHED */
}
-int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
+int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length)
{
ax25_uid_assoc *pt;
int len = 0;
off_t pos = 0;
off_t begin = 0;
- cli();
-
len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
- len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
+ len += sprintf(buffer + len, "%6d %-9s %s\n", pt->uid, ax2asc(&pt->call), pt->device);
pos = begin + len;
@@ -152,7 +172,6 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
break;
}
- sti();
*start = buffer + (offset - begin);
len -= offset - begin;
@@ -162,17 +181,3 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
return len;
}
-/*
- * Free all memory associated with UID/Callsign structures.
- */
-void __exit ax25_uid_free(void)
-{
- ax25_uid_assoc *s, *ax25_uid = ax25_uid_list;
-
- while (ax25_uid != NULL) {
- s = ax25_uid;
- ax25_uid = ax25_uid->next;
-
- kfree(s);
- }
-}
diff --git a/net/ax25/ax25_vj.c b/net/ax25/ax25_vj.c
new file mode 100644
index 000000000..4fa2dc1ff
--- /dev/null
+++ b/net/ax25/ax25_vj.c
@@ -0,0 +1,724 @@
+/*
+ * ax25_vj.c: VJ compression routines for VJ-compressed IP via NEW-AX.25
+ *
+ * Authors: Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Routines to compress TCP/IP packets according to RFC 1144 and to
+ * suppress redundant retransmissions on a reliable virtual circuit
+ * Largely based on code by Van Jacobson, Phil Karn et.al. Directly
+ * derived from WAMPES' slhc.c written by Dieter Deyke, DK5SG.
+ *
+ * Changelog:
+ * 1998-02-25 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * Adapted to Linux. contains code from drivers/net/slhc.c
+ *
+ * 1998-03-04 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * fixed problem with nonatomically calling kmalloc from interrupt
+ *
+ * 1998-03-08 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * fixed problem in axhc_recv_vjc() that lead to a system panic when
+ * the incoming sk_buff didn't contain enough headroom to rebuild the
+ * TCP/IP header after decompression
+ *
+ * License: This module 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.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_INET)
+#include <linux/skbuff.h>
+#include <asm/unaligned.h>
+#include <linux/kernel.h>
+
+#include "ax25_vj.h"
+
+#ifdef DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x)
+#endif
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+static int axhc_toss(struct axvj_slcomp *);
+
+/* Put a short in host order into a char array in network order */
+static inline unsigned char *
+put16(unsigned char *cp, unsigned short x)
+{
+ *cp++ = x >> 8;
+ *cp++ = x;
+
+ return cp;
+}
+
+/* Pull a 16-bit integer in host order from buffer in network byte order */
+static unsigned short pull16(unsigned char **cpp)
+{
+ short rval;
+
+ rval = *(*cpp)++;
+ rval <<= 8;
+ rval |= *(*cpp)++;
+ return rval;
+}
+
+/* Encode a number */
+static unsigned char *
+encode(unsigned char *cp, unsigned short n)
+{
+ if (n >= 256 || n == 0) {
+ *cp++ = 0;
+ cp = put16(cp, n);
+ } else {
+ *cp++ = n;
+ }
+ return cp;
+}
+
+/* Decode a number */
+static long decode(unsigned char **cpp)
+{
+ register int x;
+
+ x = *(*cpp)++;
+ if (x == 0) {
+ return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
+ } else {
+ return x & 0xff; /* -1 if PULLCHAR returned error */
+ }
+}
+
+
+/* Initialize compression data structure
+ * slots must be in range 0 to 255 (zero meaning no compression)
+ */
+struct axvj_slcomp *
+axhc_init(int rslots, int tslots)
+{
+ register short i;
+ register struct axvj_cstate *ts;
+ struct axvj_slcomp *comp;
+
+ comp = (struct axvj_slcomp *)kmalloc(sizeof(struct axvj_slcomp),
+ GFP_ATOMIC);
+ if (! comp)
+ return NULL;
+
+ memset(comp, 0, sizeof(struct axvj_slcomp));
+
+ if ( rslots > 0 && rslots < 256 ) {
+ size_t rsize = rslots * sizeof(struct axvj_cstate);
+ comp->rstate = (struct axvj_cstate *) kmalloc(rsize, GFP_ATOMIC);
+ if (! comp->rstate)
+ {
+ kfree((unsigned char *)comp);
+ return NULL;
+ }
+ memset(comp->rstate, 0, rsize);
+ comp->rslot_limit = rslots - 1;
+ }
+
+ if ( tslots > 0 && tslots < 256 ) {
+ size_t tsize = tslots * sizeof(struct axvj_cstate);
+ comp->tstate = (struct axvj_cstate *) kmalloc(tsize, GFP_ATOMIC);
+ if (! comp->tstate)
+ {
+ kfree((unsigned char *)comp->rstate);
+ kfree((unsigned char *)comp);
+ return NULL;
+ }
+ memset(comp->tstate, 0, tsize);
+ comp->tslot_limit = tslots - 1;
+ }
+
+ comp->xmit_oldest = 0;
+ comp->xmit_current = 255;
+ comp->recv_current = 255;
+ /*
+ * don't accept any packets with implicit index until we get
+ * one with an explicit index. Otherwise the uncompress code
+ * will try to use connection 255, which is almost certainly
+ * out of range
+ */
+ comp->flags |= SLF_TOSS;
+
+ if ( tslots > 0 ) {
+ ts = comp->tstate;
+ for(i = comp->tslot_limit; i > 0; --i){
+ ts[i].cs_this = i;
+ ts[i].next = &(ts[i - 1]);
+ }
+ ts[0].next = &(ts[comp->tslot_limit]);
+ ts[0].cs_this = 0;
+ }
+ return comp;
+}
+
+
+/* Free a compression data structure */
+void
+axhc_free(struct axvj_slcomp *comp)
+{
+ if ( comp == NULL )
+ return;
+
+ if ( comp->rstate != NULL )
+ kfree( comp->rstate );
+
+ if ( comp->tstate != NULL )
+ kfree( comp->tstate );
+
+ kfree( comp );
+}
+
+/* Dear hacker. I assume that you have read and understood RFC 1144
+ * and the original slhc_compress() procedure before tinkering with
+ * this code.
+ *
+ * procedure is as follows:
+ * 1. check if packet is TCP. return AX25_P_IP if not.
+ * 2. check if SYN, FIN, MSS, WSCALE, TSTAMP or RST is set, or if ACK is not
+ * set. deny compression for these packets (do_compression = 0).
+ * 3. try to find the appopriate slot, reuse an old one if no match is found
+ * 4. attempt to compress the packet and check the following rules:
+ * - if the packet contains an old (outdated) seq and no new ack or
+ * window or urgent data, drop it (return 0).
+ * - if nothing changed since the last frame sent (no new seq, ack,
+ * window, urgent data, or changing TCP flags), drop it.
+ * - before dropping a packet, check if any packet made it through the
+ * filter within the last 120sec. If not, assume a packet loss and
+ * transmit the packet.
+ * 5. transmit a compressed, uncompressed or regular packet, depending
+ * on do_compression and cs->deny_compression.
+ */
+
+int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression)
+{
+ struct axvj_cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
+ struct axvj_cstate *lcs = ocs;
+ struct axvj_cstate *cs = lcs->next;
+ unsigned int hlen;
+ struct tcphdr *th, *oth;
+ struct iphdr *iph;
+ unsigned long deltaS, deltaA;
+ unsigned short changes = 0;
+ unsigned char new_seq[16];
+ unsigned char *cp = new_seq;
+
+ /* Peek at IP header */
+ iph = (struct iphdr *) skb->data;
+
+ /* Bail if this packet isn't TCP, or is an IP fragment */
+ if (iph->protocol != IPPROTO_TCP ||
+ (ntohs(iph->frag_off) & 0x1fff) ||
+ (iph->frag_off & 32)) {
+ /* Send as regular IP */
+ if (iph->protocol != IPPROTO_TCP)
+ comp->sls_o_nontcp++;
+ else
+ comp->sls_o_tcp++;
+ return AX25_P_IP;
+ }
+ /* Extract TCP header */
+ th = (struct tcphdr *) (((unsigned char *) iph) + iph->ihl * 4);
+ hlen = iph->ihl * 4 + th->doff * 4;
+
+ PRINTK((KERN_DEBUG "ax25_vj.c: th.seq=%0x\n", ntohl(th->seq)));
+ /*
+ * check if packet may be compressed.
+ */
+ if (th->syn || th->fin || th->rst || !th->ack) {
+ comp->sls_o_tcp++;
+ do_compression = 0;
+ }
+ /*
+ * locate the connection state slot
+ */
+ for (;;) {
+ if (iph->saddr == cs->cs_ip.saddr
+ && iph->daddr == cs->cs_ip.daddr
+ && th->source == cs->cs_tcp.source
+ && th->dest == cs->cs_tcp.dest)
+ goto found;
+
+ /* if current equal oldest, at end of list */
+ if (cs == ocs)
+ break;
+ lcs = cs;
+ cs = cs->next;
+ comp->sls_o_searches++;
+ }
+ /*
+ * Didn't find it -- re-use oldest axvj_cstate. Send an
+ * uncompressed packet that tells the other side what
+ * connection number we're using for this conversation.
+ *
+ * Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set
+ * xmit_oldest to update the lru linkage.
+ */
+ comp->sls_o_misses++;
+ comp->xmit_oldest = lcs->cs_this;
+ cs->deny_compression = 0;
+ cs->lastdropped = 0;
+ PRINTK((KERN_DEBUG "ax25_vj.c: new slot %d\n", cs->cs_this));
+ goto uncompressed;
+
+ found:
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (lcs == ocs) {
+ /* found at most recently used */
+ } else if (cs == ocs) {
+ /* found at least recently used */
+ comp->xmit_oldest = lcs->cs_this;
+ } else {
+ /* more than 2 elements */
+ lcs->next = cs->next;
+ cs->next = ocs->next;
+ ocs->next = cs;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: found slot %d\n", cs->cs_this));
+ /*
+ * Make sure that only what we expect to change changed.
+ * Check the following:
+ * IP protocol version, header length & type of service.
+ * The "Don't fragment" bit.
+ * The time-to-live field.
+ * The TCP header length.
+ * IP options, if any.
+ * TCP options, if any.
+ * If any of these things are different between the previous &
+ * current datagram, we send the current datagram `uncompressed'.
+ */
+ oth = &cs->cs_tcp;
+
+ if (iph->version != cs->cs_ip.version || iph->ihl != cs->cs_ip.ihl
+ || iph->tos != cs->cs_ip.tos
+ || (iph->frag_off & 64) != (cs->cs_ip.frag_off & 64)
+ || iph->ttl != cs->cs_ip.ttl
+ || th->doff != cs->cs_tcp.doff
+ || (iph->ihl > 5 && memcmp(iph + 1, cs->cs_ipopt, ((iph->ihl) - 5) * 4) != 0)
+ || (th->doff > 5 && memcmp(th + 1, cs->cs_tcpopt, ((th->doff) - 5) * 4) != 0)) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: packet uncompressable\n"));
+ goto uncompressed;
+ }
+ /*
+ * Figure out which of the changing fields changed. The
+ * receiver expects changes in the order: urgent, window,
+ * ack, seq (the order minimizes the number of temporaries
+ * needed in this section of code).
+ */
+ if (th->urg) {
+ deltaS = ntohs(th->urg_ptr);
+ cp = encode(cp, deltaS);
+ changes |= NEW_U;
+ } else if (th->urg_ptr != oth->urg_ptr) {
+ /* argh! URG not set but urp changed -- a sensible
+ * implementation should never do this but RFC793
+ * doesn't prohibit the change so we have to deal
+ * with it. */
+ goto uncompressed;
+ }
+ if ((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0) {
+ cp = encode(cp, deltaS);
+ changes |= NEW_W;
+ }
+ if ((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L) {
+ if (deltaA > 0x0000ffff)
+ goto uncompressed;
+ cp = encode(cp, deltaA);
+ changes |= NEW_A;
+ }
+ if ((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L) {
+ if (deltaS > 0x0000ffff) {
+
+ /*
+ * - if the packet contains an old (outdated) seq and no
+ * new ack or window or urgent data, drop it (return 0)
+ */
+ if (before(ntohl(th->seq), ntohl(oth->seq)) && !changes) {
+ if (cs->lastdropped != 0) {
+ if (jiffies - cs->lastdropped > 120 * HZ) {
+ goto uncompressed;
+ }
+ } else {
+ cs->lastdropped = jiffies;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: old packet, dS=%0x th.seq=%0x oth.seq=%0x\n", deltaS, ntohl(th->seq), ntohl(oth->seq)));
+ return 0;
+ }
+ goto uncompressed;
+ }
+ cp = encode(cp, deltaS);
+ changes |= NEW_S;
+ }
+ switch (changes) {
+ case 0: /* Nothing changed. If this packet contains data and the
+ * last one didn't, this is probably a data packet following
+ * an ack (normal on an interactive connection) and we send
+ * it compressed. Otherwise it's probably a retransmit,
+ * retransmitted ack or window probe. Send it uncompressed
+ * in case the other side missed the compressed version. */
+ if (iph->tot_len != cs->cs_ip.tot_len
+ && ntohs(cs->cs_ip.tot_len) == hlen) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: data following ack\n"));
+ break;
+ }
+ /*
+ * MW: drop retransmitted packet. seq and ack did not change,
+ * check if flags have changed.
+ */
+ if (th->fin != oth->fin || th->syn != oth->syn || th->rst != oth->rst
+ || th->ack != oth->ack) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: tcp flags changed\n"));
+ goto uncompressed;
+ }
+ if (cs->lastdropped != 0) {
+ if (jiffies - cs->lastdropped > 120 * HZ) {
+ goto uncompressed;
+ }
+ } else {
+ cs->lastdropped = jiffies;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: no changes detected\n"));
+ return 0;
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+ /* actual changes match one of our special case encodings --
+ * send packet uncompressed.
+ */
+ goto uncompressed;
+ case NEW_S | NEW_A:
+ if (deltaS == deltaA &&
+ deltaS == ntohs(cs->cs_ip.tot_len) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+ case NEW_S:
+ if (deltaS == ntohs(cs->cs_ip.tot_len) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ /*
+ * The Packet contains new information, it has not been dropped
+ * until here. But compression has been denied, so we transmit an
+ * uncompressed packet instead.
+ */
+ if (cs->deny_compression) {
+ goto uncompressed;
+ }
+ deltaS = ntohs(iph->id) - ntohs(cs->cs_ip.id);
+ if (deltaS != 1) {
+ cp = encode(cp, deltaS);
+ changes |= NEW_I;
+ }
+ if (th->psh)
+ changes |= TCP_PUSH_BIT;
+ /* Grab the cksum before we overwrite it below. Then update our
+ * state with this packet's header.
+ */
+ deltaA = ntohs(th->check);
+ memcpy(&cs->cs_ip, iph, 20);
+ memcpy(&cs->cs_tcp, th, 20);
+ cs->lastdropped = 0;
+
+ /*
+ * MW: We don't actually perform the compression if we run on an
+ * uncompressible stream.
+ */
+ if (!do_compression) {
+ cs->deny_compression = 1;
+ return AX25_P_IP;
+ }
+ /* We want to use the original packet as our compressed packet.
+ * (cp - new_seq) is the number of bytes we need for compressed
+ * sequence numbers. In addition we need one byte for the change
+ * mask, one for the connection id and two for the tcp checksum.
+ * So, (cp - new_seq) + 4 bytes of header are needed.
+ */
+ deltaS = cp - new_seq;
+ skb_pull(skb, hlen); /* Strip TCP/IP headers */
+ if (comp->xmit_current != cs->cs_this) {
+ cp = skb_push(skb, deltaS + 4);
+ *cp++ = changes | NEW_C;
+ *cp++ = cs->cs_this;
+ comp->xmit_current = cs->cs_this;
+ } else {
+ cp = skb_push(skb, deltaS + 3);
+ *cp++ = changes;
+ }
+ cp = put16(cp, (short) deltaA); /* Write TCP checksum */
+ memcpy(cp, new_seq, deltaS); /* Write list of deltas */
+ comp->sls_o_compressed++;
+ return AX25_P_VJCOMP;
+
+ /* Update connection state cs & send uncompressed packet (i.e.,
+ * a regular ip/tcp packet but with the 'conversation id' we hope
+ * to use on future compressed packets in the protocol field).
+ */
+ uncompressed:
+ memcpy(&cs->cs_ip, iph, 20);
+ memcpy(&cs->cs_tcp, th, 20);
+ if (iph->ihl > 5)
+ memcpy(cs->cs_ipopt, iph + 1, ((iph->ihl) - 5) * 4);
+ if (th->doff > 5)
+ memcpy(cs->cs_tcpopt, th + 1, ((th->doff) - 5) * 4);
+ comp->xmit_current = cs->cs_this;
+ cs->lastdropped = 0;
+
+ if (!do_compression) {
+ cs->deny_compression = 1;
+ return AX25_P_IP;
+ }
+ iph->protocol = cs->cs_this;
+ cs->deny_compression = 0;
+ comp->sls_o_uncompressed++;
+ return AX25_P_VJUNCOMP;
+}
+
+int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb)
+{
+ register int changes;
+ long x;
+ register struct tcphdr *thp;
+ register struct iphdr *ip;
+ register struct axvj_cstate *cs;
+ int len, hdrlen;
+
+ int isize = skb->len;
+ unsigned char *icp = skb->data;
+ unsigned char *cp = icp;
+
+ /* We've got a compressed packet; read the change byte */
+ comp->sls_i_compressed++;
+ if (isize < 3) {
+ comp->sls_i_error++;
+ return 0;
+ }
+ changes = *cp++;
+ if (changes & NEW_C) {
+ /* Make sure the state index is in range, then grab the state.
+ * If we have a good state index, clear the 'discard' flag.
+ */
+ x = *cp++; /* Read conn index */
+ if (x < 0 || x > comp->rslot_limit)
+ goto bad;
+
+ comp->flags &= ~SLF_TOSS;
+ comp->recv_current = x;
+ } else {
+ /* this packet has an implicit state index. If we've
+ * had a line error since the last time we got an
+ * explicit state index, we have to toss the packet. */
+ if (comp->flags & SLF_TOSS) {
+ comp->sls_i_tossed++;
+ return 0;
+ }
+ }
+ cs = &comp->rstate[comp->recv_current];
+ thp = &cs->cs_tcp;
+ ip = &cs->cs_ip;
+
+ if ((x = pull16(&cp)) == -1) { /* Read the TCP checksum */
+ goto bad;
+ }
+ thp->check = htons(x);
+
+ thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
+/*
+ * we can use the same number for the length of the saved header and
+ * the current one, because the packet wouldn't have been sent
+ * as compressed unless the options were the same as the previous one
+ */
+
+ hdrlen = ip->ihl * 4 + thp->doff * 4;
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I: /* Echoed terminal traffic */
+ {
+ register short i;
+ i = ntohs(ip->tot_len) - hdrlen;
+ thp->ack_seq = htonl(ntohl(thp->ack_seq) + i);
+ thp->seq = htonl(ntohl(thp->seq) + i);
+ }
+ break;
+
+ case SPECIAL_D: /* Unidirectional data */
+ thp->seq = htonl(ntohl(thp->seq) +
+ ntohs(ip->tot_len) - hdrlen);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ thp->urg = 1;
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->urg_ptr = htons(x);
+ } else
+ thp->urg = 0;
+ if (changes & NEW_W) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->window = htons(ntohs(thp->window) + x);
+ }
+ if (changes & NEW_A) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->ack_seq = htonl(ntohl(thp->ack_seq) + x);
+ }
+ if (changes & NEW_S) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->seq = htonl(ntohl(thp->seq) + x);
+ }
+ break;
+ }
+ if (changes & NEW_I) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ ip->id = htons(ntohs(ip->id) + x);
+ } else
+ ip->id = htons(ntohs(ip->id) + 1);
+
+ /*
+ * At this point, cp points to the first byte of data in the
+ * packet. Put the reconstructed TCP and IP headers back on the
+ * packet. Recalculate IP checksum (but not TCP checksum).
+ */
+
+ len = isize - (cp - icp);
+ if (len < 0)
+ goto bad;
+ len += hdrlen;
+ ip->tot_len = htons(len);
+ ip->check = 0;
+
+ /*
+ * MW:
+ * we are working on sk_buffs here, so we can spare the memmove()
+ * and simply skb_push() the hdrlen
+ */
+
+ skb_push(skb, hdrlen - (cp - icp));
+
+ cp = icp = skb->data;
+ memcpy(cp, ip, 20);
+ cp += 20;
+
+ if (ip->ihl > 5) {
+ memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
+ cp += (ip->ihl - 5) * 4;
+ }
+ put_unaligned(ip_fast_csum(icp, ip->ihl),
+ &((struct iphdr *) icp)->check);
+
+ memcpy(cp, thp, 20);
+ cp += 20;
+
+ if (thp->doff > 5) {
+ memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
+ cp += ((thp->doff) - 5) * 4;
+ }
+ return len;
+ bad:
+ comp->sls_i_error++;
+ return axhc_toss(comp);
+}
+
+
+int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb)
+{
+ register struct axvj_cstate *cs;
+ unsigned ihl;
+
+ unsigned char index;
+
+ int isize = skb->len;
+ unsigned char *icp = skb->data;
+
+ if (isize < 20) {
+ /* The packet is shorter than a legal IP header */
+ printk(KERN_DEBUG "axhc_remember: short packet from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_runt++;
+ return axhc_toss(comp);
+ }
+ /* Peek at the IP header's IHL field to find its length */
+ ihl = icp[0] & 0xf;
+ if (ihl < 20 / 4) {
+ /* The IP header length field is too small */
+ printk(KERN_DEBUG "axhc_remember: ihl too small from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_runt++;
+ return axhc_toss(comp);
+ }
+ index = icp[9];
+ icp[9] = IPPROTO_TCP;
+
+ if (ip_fast_csum(icp, ihl)) {
+ /* Bad IP header checksum; discard */
+ printk(KERN_DEBUG "axhc_remember: bad ip header checksum from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_badcheck++;
+ return axhc_toss(comp);
+ }
+ if (index > comp->rslot_limit) {
+ printk(KERN_DEBUG "axhc_remember: illegal slot from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_error++;
+ return axhc_toss(comp);
+ }
+ /* Update local state */
+ cs = &comp->rstate[comp->recv_current = index];
+ comp->flags &= ~SLF_TOSS;
+ memcpy(&cs->cs_ip, icp, 20);
+ memcpy(&cs->cs_tcp, icp + ihl * 4, 20);
+ if (ihl > 5)
+ memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
+ if (cs->cs_tcp.doff > 5)
+ memcpy(cs->cs_tcpopt, icp + ihl * 4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
+ cs->cs_hsize = ihl * 2 + cs->cs_tcp.doff * 2;
+ /* Put headers back on packet
+ * Neither header checksum is recalculated
+ */
+ comp->sls_i_uncompressed++;
+ return isize;
+}
+
+static int
+axhc_toss(struct axvj_slcomp *comp)
+{
+ if ( comp == NULL )
+ return 0;
+
+ comp->flags |= SLF_TOSS;
+ return 0;
+}
+
+#endif
diff --git a/net/ax25/ax25_vj.h b/net/ax25/ax25_vj.h
new file mode 100644
index 000000000..da059392c
--- /dev/null
+++ b/net/ax25/ax25_vj.h
@@ -0,0 +1,73 @@
+/*
+ * Interface declaration of the VJ compression code
+ *
+ * Matthias Welwarsky (DG2FEF) 05/25/98
+ */
+
+#ifndef _AX25_VJ_H
+#define _AX25_VJ_H
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+/*
+ * MW: copied this into ax25.h to unclobber the original struct cstate.
+ * "state" data for each active tcp conversation on the "wire". This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct axvj_cstate {
+ unsigned char cs_this; /* connection id number (xmit) */
+ struct axvj_cstate *next; /* next in ring (xmit) */
+ struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
+ struct tcphdr cs_tcp;
+ unsigned char cs_ipopt[64];
+ unsigned char cs_tcpopt[64];
+ int cs_hsize;
+ unsigned char deny_compression; /* MW: for vj compression via AX.25 */
+ unsigned long lastdropped;
+};
+
+/*
+ * all the state data for one VC (we need one of these per VC).
+ */
+struct axvj_slcomp {
+ struct axvj_cstate *tstate; /* transmit connection states (array)*/
+ struct axvj_cstate *rstate; /* receive connection states (array)*/
+
+ unsigned char tslot_limit; /* highest transmit slot id (0-l)*/
+ unsigned char rslot_limit; /* highest receive slot id (0-l)*/
+
+ unsigned char xmit_oldest; /* oldest xmit in ring */
+ unsigned char xmit_current; /* most recent xmit id */
+ unsigned char recv_current; /* most recent rcvd id */
+
+ unsigned char flags;
+#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */
+
+ int sls_o_nontcp; /* outbound non-TCP packets */
+ int sls_o_tcp; /* outbound TCP packets */
+ int sls_o_uncompressed; /* outbound uncompressed packets */
+ int sls_o_compressed; /* outbound compressed packets */
+ int sls_o_searches; /* searches for connection state */
+ int sls_o_misses; /* times couldn't find conn. state */
+
+ int sls_i_uncompressed; /* inbound uncompressed packets */
+ int sls_i_compressed; /* inbound compressed packets */
+ int sls_i_error; /* inbound error packets */
+ int sls_i_tossed; /* inbound packets tossed because of error */
+
+ int sls_i_runt;
+ int sls_i_badcheck;
+};
+
+extern struct axvj_slcomp* axhc_init(int, int);
+extern void axhc_free(struct axvj_slcomp*);
+extern int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression);
+extern void ax25_vj_init(void);
+extern void ax25_vj_cleanup(void);
+extern int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb);
+extern int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb);
+
+#endif
diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c
index e6016f93d..416f4cc80 100644
--- a/net/ax25/sysctl_net_ax25.c
+++ b/net/ax25/sysctl_net_ax25.c
@@ -1,14 +1,31 @@
-/* -*- linux-c -*-
- * sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem.
+/*
+ * sysctl_net_ax25.c: sysctl interface for NEW-AX.25
*
- * Begun April 1, 1996, Mike Shaver.
- * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS]
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Mike Shaver
+ *
+ * Comment: Needs cleanup, most parameters are not needed and/or should be au
+ * adjusting.
+ *
+ * Changelog:
+ *
+ * License: This module 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.
*/
-#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include "ax25_ddi.h"
+
+int ax25_proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp);
+int ax25_sysctl_intvec(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context);
static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1};
static int min_axdefmode[] = {0}, max_axdefmode[] = {1};
@@ -16,22 +33,29 @@ static int min_backoff[] = {0}, max_backoff[] = {2};
static int min_conmode[] = {0}, max_conmode[] = {2};
static int min_window[] = {1}, max_window[] = {7};
static int min_ewindow[] = {1}, max_ewindow[] = {63};
-static int min_t1[] = {1}, max_t1[] = {30 * HZ};
-static int min_t2[] = {1}, max_t2[] = {20 * HZ};
-static int min_t3[] = {0}, max_t3[] = {3600 * HZ};
-static int min_idle[] = {0}, max_idle[] = {65535 * HZ};
+static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ * 10};
+static int min_t2[] = {0}, max_t2[] = {10 * AX25_SLOWHZ * 10};
+static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ * 10};
+static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ * 10};
static int min_n2[] = {1}, max_n2[] = {31};
static int min_paclen[] = {1}, max_paclen[] = {512};
-static int min_proto[] = {0}, max_proto[] = {3};
-static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ};
+static int min_protocol[] = {0}, max_protocol[] = {2};
+static int min_dama_slave_timeout[] = {0}, max_dama_slave_timeout[] = {3600 * AX25_SLOWHZ};
+static int min_media_duplex[] = {0}, max_media_duplex[] = {2};
+static int min_media_txdelay[] = {0}, max_media_txdelay[] = {10000};
+static int min_media_txtail[] = {0}, max_media_txtail[] = {10000};
+static int min_media_txbitrate[] = {0}, max_media_txbitrate[] = {80000000};
+static int min_media_rxbitrate[] = {0}, max_media_rxbitrate[] = {80000000};
+static int min_media_slottime[] = {0}, max_media_slottime[] = {1000};
+static int min_media_ppersist[] = {0}, max_media_ppersist[] = {255};
+static int min_media_autoadj[] = {0}, max_media_autoadj[] = {1};
static struct ctl_table_header *ax25_table_header;
-static ctl_table *ax25_table;
-static int ax25_table_size;
+static ctl_table ax25_table[AX25_MAX_DEVICES + 1];
static ctl_table ax25_dir_table[] = {
- {NET_AX25, "ax25", NULL, 0, 0555, NULL},
+ {NET_AX25, "ax25", NULL, 0, 0555, ax25_table},
{0}
};
@@ -92,71 +116,121 @@ static const ctl_table ax25_param_table[] = {
{NET_AX25_PROTOCOL, "protocol",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
- &min_proto, &max_proto},
+ &min_protocol, &max_protocol},
{NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
- &min_ds_timeout, &max_ds_timeout},
+ &min_dama_slave_timeout, &max_dama_slave_timeout},
+ {NET_AX25_MEDIA_DUPLEX, "media_duplex",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_duplex, &max_media_duplex},
+ {NET_AX25_MEDIA_TXDELAY, "media_txdelay",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_txdelay, &max_media_txdelay},
+ {NET_AX25_MEDIA_TXTAIL, "media_txtail",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_txtail, &max_media_txtail},
+ {NET_AX25_MEDIA_TXBITRATE, "media_txbitrate",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_txbitrate, &max_media_txbitrate},
+ {NET_AX25_MEDIA_RXBITRATE, "media_rxbitrate",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_rxbitrate, &max_media_rxbitrate},
+ {NET_AX25_MEDIA_SLOTTIME, "media_slottime",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_slottime, &max_media_slottime},
+ {NET_AX25_MEDIA_PPERSISTENCE, "media_ppersistence",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_ppersist, &max_media_ppersist},
+ {NET_AX25_MEDIA_AUTO_ADJUST, "media_autoadjust",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_autoadj, &max_media_autoadj},
{0} /* that's all, folks! */
};
-void ax25_register_sysctl(void)
+int ax25_proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
{
- ax25_dev *ax25_dev;
- int n, k;
+ int id = table->ctl_name;
+ int ret;
+ int oldval, newval;
+ struct net_device *dev;
+
+ oldval = * (int *) table->data;
+ ret = proc_dointvec_minmax(table, write, filp, buffer, lenp);
+ newval = * (int *) table->data;
+ if (oldval != newval) {
+ dev = dev_get(table->de->parent->name);
+ if (!dev) return ret; /* paranoia */
+ ax25_notify_dispatcher(dev, id-1, oldval, newval);
+ }
+ return ret;
+}
+
+int ax25_sysctl_intvec(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context)
+{
+ int id = table->ctl_name;
+ int ret;
+ struct net_device *dev;
+
+ ret = sysctl_intvec(table, name, nlen, oldval, oldlenp, newval,
+ newlen, context);
+ if (* (int *) oldval != * (int *) newval) {
+ dev = dev_get(table->de->parent->name);
+ if (!dev) return ret; /* paranoia */
+ ax25_notify_dispatcher(dev, id-1, * (int *) oldval, * (int *) newval);
+ }
+ return ret;
+}
- for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
- ax25_table_size += sizeof(ctl_table);
+void ax25_register_sysctl(void)
+{
+ struct net_device *dev;
+ struct ax25_dev *ax25_device;
- if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL)
- return;
+ int n, k, i;
- memset(ax25_table, 0x00, ax25_table_size);
+ memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table));
- for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) {
- ctl_table *child = kmalloc(sizeof(ax25_param_table), GFP_ATOMIC);
- if (!child) {
- while (n--)
- kfree(ax25_table[n].child);
- kfree(ax25_table);
- return;
- }
- memcpy(child, ax25_param_table, sizeof(ax25_param_table));
- ax25_table[n].child = ax25_dev->systable = child;
- ax25_table[n].ctl_name = n + 1;
- ax25_table[n].procname = ax25_dev->dev->name;
- ax25_table[n].mode = 0555;
+ n = 0;
-#ifndef CONFIG_AX25_DAMA_SLAVE
- /*
- * We do not wish to have a representation of this parameter
- * in /proc/sys/ when configured *not* to include the
- * AX.25 DAMA slave code, do we?
- */
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if ((dev = ax25_devices[i]) != NULL) {
+ ax25_device = AX25_PTR(dev);
+ if (n <= AX25_MAX_DEVICES) {
+ ax25_table[n].ctl_name = n + 1;
+ ax25_table[n].procname = dev->name;
+ ax25_table[n].data = NULL;
+ ax25_table[n].maxlen = 0;
+ ax25_table[n].mode = 0555;
+ ax25_table[n].child = ax25_device->systable;
+ ax25_table[n].proc_handler = NULL;
- child[AX25_VALUES_DS_TIMEOUT].procname = NULL;
-#endif
+ memcpy(ax25_device->systable, ax25_param_table, sizeof(ax25_device->systable));
- child[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */
+ ax25_device->systable[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */
- for (k = 0; k < AX25_MAX_VALUES; k++)
- child[k].data = &ax25_dev->values[k];
+ for (k = 0; k < AX25_MAX_VALUES; k++)
+ ax25_device->systable[k].data = &ax25_device->values[k];
- n++;
+ n++;
+ }
+ }
}
- ax25_dir_table[0].child = ax25_table;
-
ax25_table_header = register_sysctl_table(ax25_root_table, 1);
}
void ax25_unregister_sysctl(void)
{
- ctl_table *p;
unregister_sysctl_table(ax25_table_header);
-
- ax25_dir_table[0].child = NULL;
- for (p = ax25_table; p->ctl_name; p++)
- kfree(p->child);
- kfree(ax25_table);
}