diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2015-06-24 04:23:46 +0200 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-06-24 10:03:18 +0200 |
commit | e5067d7cd967cb17067de24a162306b79f432b20 (patch) | |
tree | 541f101762df32a5742bec354009986a96d8e564 /net | |
parent | 86a981e836404006efc35881ebf3d5ae36925e82 (diff) |
Import newax25-2.4.3.patch.1.bz2HEADnewax25-2.4.3-1
And cleanup the *.orig and *.rej files and whitespace errors that are part
of the original patch.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'net')
47 files changed, 7103 insertions, 5357 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); } diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index c43892ea5..e3f98e671 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -257,33 +257,6 @@ static int arp_constructor(struct neighbour *neigh) in old paradigm. */ -#if 1 - /* So... these "amateur" devices are hopeless. - The only thing, that I can say now: - It is very sad that we need to keep ugly obsolete - code to make them happy. - - They should be moved to more reasonable state, now - they use rebuild_header INSTEAD OF hard_start_xmit!!! - Besides that, they are sort of out of date - (a lot of redundant clones/copies, useless in 2.1), - I wonder why people believe that they work. - */ - switch (dev->type) { - default: - break; - case ARPHRD_ROSE: -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - case ARPHRD_AX25: -#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) - case ARPHRD_NETROM: -#endif - neigh->ops = &arp_broken_ops; - neigh->output = neigh->ops->output; - return 0; -#endif - ;} -#endif if (neigh->type == RTN_MULTICAST) { neigh->nud_state = NUD_NOARP; arp_mc_map(addr, neigh->ha, dev, 1); diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index a4b89d294..3ba0b8c81 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -47,6 +47,7 @@ #include <linux/net.h> #include <linux/stat.h> #include <net/ax25.h> +#include <net/ax25_uid.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/if_arp.h> @@ -347,6 +348,8 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { struct sock *sk = sock->sk; + char devname[IFNAMSIZ]; + struct net_device *dev; int opt; if (level != SOL_NETROM) @@ -389,6 +392,21 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, sk->protinfo.nr->idle = opt * 60 * HZ; return 0; + case SO_BINDTODEVICE: + 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 && + (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) + return -EADDRNOTAVAIL; + + sk->protinfo.nr->device=dev; + return 0; + default: return -ENOPROTOOPT; } @@ -398,15 +416,24 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; + struct net_device *dev; + char devname[IFNAMSIZ]; + void *valptr; int val = 0; - int len; + int maxlen, length; if (level != SOL_NETROM) return -ENOPROTOOPT; - if (get_user(len, optlen)) + if (get_user(maxlen, optlen)) return -EFAULT; + if (maxlen < 1) + return -EFAULT; + + valptr = (void *) &val; + length = min(maxlen, sizeof(int)); + switch (optname) { case NETROM_T1: val = sk->protinfo.nr->t1 / HZ; @@ -428,16 +455,33 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, val = sk->protinfo.nr->idle / (60 * HZ); break; + case SO_BINDTODEVICE: + dev = sk->protinfo.nr->device; + + if (dev != NULL) { + strncpy(devname, dev->name, IFNAMSIZ); + length = min(strlen(dev->name)+1, maxlen); + devname[length-1] = '\0'; + } else { + *devname = '\0'; + length = 1; + } + + valptr = (void *) devname; + break; + + default: return -ENOPROTOOPT; } - len = min(len, sizeof(int)); + if (put_user(length, optlen)) + return -EFAULT; - if (put_user(len, optlen)) + if (copy_to_user(optval, valptr, length)) return -EFAULT; - return copy_to_user(optval, &val, len) ? -EFAULT : 0; + return 0; } static int nr_listen(struct socket *sock, int backlog) @@ -589,13 +633,12 @@ static int nr_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; struct net_device *dev; - ax25_address *user, *source; + ax25_address *user = NULL, *source = NULL; if (sk->zapped == 0) return -EINVAL; - if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct -full_sockaddr_ax25)) + if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) return -EINVAL; if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) @@ -604,32 +647,42 @@ full_sockaddr_ax25)) if (addr->fsa_ax25.sax25_family != AF_NETROM) return -EINVAL; - if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) { - SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n"); - return -EADDRNOTAVAIL; - } - /* - * Only the super user can set an arbitrary user callsign. + * User did not set the interfave with SO_BINDTODEVICE, + * use compatibility code. */ - if (addr->fsa_ax25.sax25_ndigis == 1) { - if (!capable(CAP_NET_BIND_SERVICE)) - return -EACCES; - sk->protinfo.nr->user_addr = addr->fsa_digipeater[0]; - sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call; - } else { - source = &addr->fsa_ax25.sax25_call; - if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) - return -EPERM; - user = source; + dev = sk->protinfo.nr->device; + if (dev == NULL) { + if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) { + /* device callsign provided */ + if(ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) && + (dev = nr_dev_get(&addr->fsa_digipeater[0])) == NULL) + return -EADDRNOTAVAIL; + source = &addr->fsa_digipeater[0]; + } else { + if ((dev = nr_dev_get(&addr->fsax25_call)) == NULL) + return -EADDRNOTAVAIL; + source = &addr->fsa_ax25.sax25_call; } - sk->protinfo.nr->user_addr = *user; - sk->protinfo.nr->source_addr = *source; + sk->protinfo.nr->device = dev; + } else { + source = (ax25_address *) dev->dev_addr; } + if (!capable(CAP_NET_BIND_SERVICE)) + { + /* FIXME: should not be coupled with AX.25 */ + user = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name); + if (user == NULL && ax25_uid_policy) + return -EACCES; + } + + if (user == NULL) user = &addr->fsax25_call; + sk->protinfo.nr->user_addr = *user; + sk->protinfo.nr->source_addr = *source; + sk->protinfo.nr->device = dev; nr_insert_socket(sk); @@ -643,7 +696,6 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, { struct sock *sk = sock->sk; struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr; - ax25_address *user, *source = NULL; struct net_device *dev; if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { @@ -668,26 +720,8 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, if (addr->sax25_family != AF_NETROM) return -EINVAL; - if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ - sk->zapped = 0; - - if ((dev = nr_dev_first()) == NULL) - return -ENETUNREACH; - - source = (ax25_address *)dev->dev_addr; - - if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) - return -EPERM; - user = source; - } - - sk->protinfo.nr->user_addr = *user; - sk->protinfo.nr->source_addr = *source; - sk->protinfo.nr->device = dev; - - nr_insert_socket(sk); /* Finish the bind */ - } + if (sk->zapped) + return -EADDRNOTAVAIL; sk->protinfo.nr->dest_addr = addr->sax25_call; diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c index 5290ae024..519ca55da 100644 --- a/net/netrom/nr_loopback.c +++ b/net/netrom/nr_loopback.c @@ -44,10 +44,10 @@ int nr_loopback_queue(struct sk_buff *skb) { struct sk_buff *skbn; - if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) { - memcpy(skb_put(skbn, skb->len), skb->data, skb->len); - skbn->h.raw = skbn->data; + skbn = skb_clone(skb, GFP_ATOMIC); + kfree(skb); + if (skbn != NULL) { skb_queue_tail(&loopback_queue, skbn); if (!nr_loopback_running()) diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index ec0578b51..e6793fb0b 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -60,8 +60,8 @@ static void nr_remove_neigh(struct nr_neigh *); * Add a new route to a node, and in the process add the node and the * neighbour if it is new. */ -static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25, - ax25_digi *ax25_digi, struct net_device *dev, int quality, int obs_count) +static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_addr_t *ax25_addr, + struct net_device *dev, int quality, int obs_count) { struct nr_node *nr_node; struct nr_neigh *nr_neigh; @@ -77,7 +77,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 break; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; /* @@ -106,8 +106,8 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - nr_neigh->callsign = *ax25; - nr_neigh->digipeat = NULL; + nr_neigh->addr = *ax25_addr; + nr_neigh->addr.src = *((ax25_address *) dev->dev_addr); nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = sysctl_netrom_default_path_quality; @@ -116,13 +116,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); - } + /* FIXME: not SMP safe! */ save_flags(flags); cli(); @@ -133,7 +127,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 restore_flags(flags); } - if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) + if (quality != 0 && ax25cmp(nr, &ax25_addr->dest) == 0 && !nr_neigh->locked) nr_neigh->quality = quality; if (nr_node == NULL) { @@ -294,8 +288,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) if ((s = nr_neigh_list) == nr_neigh) { nr_neigh_list = nr_neigh->next; restore_flags(flags); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); kfree(nr_neigh); return; } @@ -304,8 +296,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) if (s->next == nr_neigh) { s->next = nr_neigh->next; restore_flags(flags); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); kfree(nr_neigh); return; } @@ -333,7 +323,7 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n if (nr_node == NULL) return -EINVAL; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(neighbour, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; if (nr_neigh == NULL) return -EINVAL; @@ -370,13 +360,13 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n /* * Lock a neighbour with a quality. */ -static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality) +static int nr_add_neigh(ax25_addr_t *ax25_addr, struct net_device *dev, unsigned int quality) { struct nr_neigh *nr_neigh; unsigned long flags; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) { + if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) { nr_neigh->quality = quality; nr_neigh->locked = 1; return 0; @@ -386,8 +376,8 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - nr_neigh->callsign = *callsign; - nr_neigh->digipeat = NULL; + nr_neigh->addr = *ax25_addr; + nr_neigh->addr.src = *((ax25_address *) dev->dev_addr); nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = quality; @@ -396,14 +386,6 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); - } - save_flags(flags); cli(); @@ -424,7 +406,7 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned struct nr_neigh *nr_neigh; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(callsign, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; if (nr_neigh == NULL) return -EINVAL; @@ -586,31 +568,26 @@ struct net_device *nr_dev_get(ax25_address *addr) for (dev = dev_base; dev != NULL; dev = dev->next) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) { dev_hold(dev); - goto out; + break; } } -out: + read_unlock(&dev_base_lock); return dev; } -static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters) +static ax25_addr_t *nr_neigh_to_addr(ax25_address *callsign, int ndigis, ax25_address *digipeater) { - static ax25_digi ax25_digi; + static ax25_addr_t ax25_addr; int i; - if (ndigis == 0) - return NULL; + ax25_addr.dest = *callsign; + ax25_addr.dcount = ndigis; - for (i = 0; i < ndigis; i++) { - ax25_digi.calls[i] = digipeaters[i]; - ax25_digi.repeated[i] = 0; - } + for (i = 0; i < ndigis; i++) + ax25_addr.digipeater[i] = digipeater[i]; - ax25_digi.ndigi = ndigis; - ax25_digi.lastrepeat = -1; - - return &ax25_digi; + return &ax25_addr; } /* @@ -632,16 +609,13 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) return -EINVAL; switch (nr_route.type) { case NETROM_NODE: - return nr_add_node(&nr_route.callsign, - nr_route.mnemonic, - &nr_route.neighbour, - nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), + return nr_add_node(&nr_route.callsign, nr_route.mnemonic, + nr_neigh_to_addr(&nr_route.neighbour, nr_route.ndigis, nr_route.digipeaters), dev, nr_route.quality, nr_route.obs_count); case NETROM_NEIGH: - return nr_add_neigh(&nr_route.callsign, - nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), - dev, nr_route.quality); + return nr_add_neigh(nr_neigh_to_addr(&nr_route.callsign, nr_route.ndigis, nr_route.digipeaters), + dev, nr_route.quality); default: return -EINVAL; } @@ -713,8 +687,8 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) nr_dest = (ax25_address *)(skb->data + 7); if (ax25 != NULL) - nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, - ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser); + nr_add_node(nr_src, "", &ax25->addr, ax25->device, 0, + sysctl_netrom_obsolescence_count_initialiser); if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */ if (ax25 == NULL) /* Its from me */ @@ -745,7 +719,13 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) dptr = skb_push(skb, 1); *dptr = AX25_P_NETROM; - nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + /* The next line is dirty hotfix. There is a bug in nr_add_neigh() - + * src callsign is taken from dev->dev_addr of device on which the + * broadcast was received. But this is AX25 device (and callsign), + * not the netrom device, so the callsign is bad. + */ + nr_neigh->addr.src = *((ax25_address*)dev->dev_addr); + nr_neigh->ax25 = ax25_send_frame(skb, 256, &nr_neigh->addr, nr_neigh->dev); return (nr_neigh->ax25 != NULL); } @@ -814,16 +794,16 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d", nr_neigh->number, - ax2asc(&nr_neigh->callsign), + ax2asc(&nr_neigh->addr.dest), nr_neigh->dev ? nr_neigh->dev->name : "???", nr_neigh->quality, nr_neigh->locked, nr_neigh->count, nr_neigh->failed); - if (nr_neigh->digipeat != NULL) { - for (i = 0; i < nr_neigh->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i])); + if (nr_neigh->addr.dcount) { + for (i = 0; i < nr_neigh->addr.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->addr.digipeater[i])); } len += sprintf(buffer + len, "\n"); diff --git a/net/netsyms.c b/net/netsyms.c index 8adcef849..5d56062f3 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -246,6 +246,7 @@ EXPORT_SYMBOL(ip_dev_find); EXPORT_SYMBOL(inetdev_by_index); EXPORT_SYMBOL(in_dev_finish_destroy); EXPORT_SYMBOL(ip_defrag); +EXPORT_SYMBOL(ip_options_fragment); /* Route manipulation */ EXPORT_SYMBOL(ip_rt_ioctl); diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index a92bf86f5..07acd3946 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -39,6 +39,7 @@ #include <linux/net.h> #include <linux/stat.h> #include <net/ax25.h> +#include <net/ax25_uid.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/if_arp.h> @@ -404,6 +405,8 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { struct sock *sk = sock->sk; + struct net_device *dev; + char devname[IFNAMSIZ]; int opt; if (level != SOL_ROSE) @@ -454,6 +457,22 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, sk->protinfo.rose->qbitincl = opt ? 1 : 0; return 0; + + case SO_BINDTODEVICE: + 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 && + (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) + return -EADDRNOTAVAIL; + + sk->protinfo.rose->device = dev; + return 0; + default: return -ENOPROTOOPT; } @@ -463,14 +482,23 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; + struct net_device *dev; + char devname[IFNAMSIZ]; + void *valptr; int val = 0; - int len; + int maxlen, length; if (level != SOL_ROSE) return -ENOPROTOOPT; - if (get_user(len, optlen)) + if (get_user(maxlen, optlen)) return -EFAULT; + + if (maxlen < 1) + return -EFAULT; + + valptr = (void *) &val; + length = min(maxlen, sizeof(int)); switch (optname) { case ROSE_DEFER: @@ -501,16 +529,32 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, val = sk->protinfo.rose->qbitincl; break; + case SO_BINDTODEVICE: + dev = sk->protinfo.rose->device; + + if (dev != NULL) { + strncpy(devname, dev->name, IFNAMSIZ); + length = min(strlen(dev->name)+1, maxlen); + devname[length-1] = '\0'; + } else { + *devname = '\0'; + length = 1; + } + + valptr = (void *) devname; + break; + default: return -ENOPROTOOPT; } - len = min(len, sizeof(int)); + if (put_user(length, optlen)) + return -EFAULT; - if (put_user(len, optlen)) + if (copy_to_user(optval, valptr, length)) return -EFAULT; - return copy_to_user(optval, &val, len) ? -EFAULT : 0; + return 0; } static int rose_listen(struct socket *sock, int backlog) @@ -666,7 +710,8 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct sock *sk = sock->sk; struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; struct net_device *dev; - ax25_address *user, *source; + ax25_address *user = NULL; + rose_address *source = NULL; int n; if (sk->zapped == 0) @@ -683,21 +728,36 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (addr->srose_ndigis > ROSE_MAX_DIGIS) return -EINVAL; + /* + * User did not set interface with SO_BINDTODEVICE + * thus we'll use the compatibility code + */ + + dev = sk->protinfo.rose->device; + if (dev == NULL) { + if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) { + SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n"); + return -EADDRNOTAVAIL; + } - if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) { - SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n"); - return -EADDRNOTAVAIL; + source = &addr->srose_addr; + sk->protinfo.rose->device = dev; + } else { + source = (rose_address *) dev->dev_addr; } - source = &addr->srose_call; - - if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) + /* root can do whatever (s)he likes, but anyone else... */ + if (!capable(CAP_NET_BIND_SERVICE)) + { + /* FIXME: strictly speaking this has nothing to do with AX.25 */ + user = ax25_find_match_for_uid(current->euid, &addr->srose_call, dev->name); + if (user == NULL && ax25_uid_policy) return -EACCES; - user = source; } - sk->protinfo.rose->source_addr = addr->srose_addr; + if (user == NULL) user = &addr->srose_call; + + sk->protinfo.rose->source_addr = *source; sk->protinfo.rose->source_call = *user; sk->protinfo.rose->device = dev; sk->protinfo.rose->source_ndigis = addr->srose_ndigis; @@ -766,21 +826,8 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) return -ENETUNREACH; - if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ - sk->zapped = 0; - - if ((dev = rose_dev_first()) == NULL) - return -ENETUNREACH; - - if ((user = ax25_findbyuid(current->euid)) == NULL) - return -EINVAL; - - memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN); - sk->protinfo.rose->source_call = *user; - sk->protinfo.rose->device = dev; - - rose_insert_socket(sk); /* Finish the bind */ - } + if (sk->zapped) + return -EINVAL; sk->protinfo.rose->dest_addr = addr->srose_addr; sk->protinfo.rose->dest_call = addr->srose_call; diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 2c793e4e6..a4e5f71bd 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -104,14 +104,12 @@ static void rose_t0timer_expiry(unsigned long param) */ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) { - ax25_address *rose_call; - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (ax25_address *)neigh->dev->dev_addr; + neigh->addr.src = *((ax25_address *) neigh->dev->dev_addr); else - rose_call = &rose_callsign; + neigh->addr.src = rose_callsign; - neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_send_frame(skb, 260, &neigh->addr, neigh->dev); return (neigh->ax25 != NULL); } @@ -123,14 +121,12 @@ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) */ static int rose_link_up(struct rose_neigh *neigh) { - ax25_address *rose_call; - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (ax25_address *)neigh->dev->dev_addr; + neigh->addr.src = *((ax25_address *) neigh->dev->dev_addr); else - rose_call = &rose_callsign; + neigh->addr.src = rose_callsign; - neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_find_cb(&neigh->addr, neigh->dev); return (neigh->ax25 != NULL); } diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 1bedfdf0d..4de549586 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -77,23 +77,23 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device return -EINVAL; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) + if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev) break; if (rose_neigh == NULL) { if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - rose_neigh->callsign = rose_route->neighbour; - rose_neigh->digipeat = NULL; - rose_neigh->ax25 = NULL; - rose_neigh->dev = dev; - rose_neigh->count = 0; - rose_neigh->use = 0; - rose_neigh->dce_mode = 0; - rose_neigh->loopback = 0; - rose_neigh->number = rose_neigh_no++; - rose_neigh->restarted = 0; + rose_neigh->addr.dest = rose_route->neighbour; + rose_neigh->addr.dcount = 0; + rose_neigh->ax25 = NULL; + rose_neigh->dev = dev; + rose_neigh->count = 0; + rose_neigh->use = 0; + rose_neigh->dce_mode = 0; + rose_neigh->loopback = 0; + rose_neigh->number = rose_neigh_no++; + rose_neigh->restarted = 0; skb_queue_head_init(&rose_neigh->queue); @@ -101,18 +101,11 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device init_timer(&rose_neigh->t0timer); if (rose_route->ndigis != 0) { - if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { - kfree(rose_neigh); - return -ENOMEM; - } - - rose_neigh->digipeat->ndigi = rose_route->ndigis; - rose_neigh->digipeat->lastrepeat = -1; + rose_neigh->addr.dcount = rose_route->ndigis; + rose_neigh->addr.lastrepeat = -1; - for (i = 0; i < rose_route->ndigis; i++) { - rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; - rose_neigh->digipeat->repeated[i] = 0; - } + for (i = 0; i < rose_route->ndigis; i++) + rose_neigh->addr.digipeater[i] = rose_route->digipeaters[i]; } save_flags(flags); cli(); @@ -234,8 +227,6 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) if ((s = rose_neigh_list) == rose_neigh) { rose_neigh_list = rose_neigh->next; restore_flags(flags); - if (rose_neigh->digipeat != NULL) - kfree(rose_neigh->digipeat); kfree(rose_neigh); return; } @@ -244,8 +235,6 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) if (s->next == rose_neigh) { s->next = rose_neigh->next; restore_flags(flags); - if (rose_neigh->digipeat != NULL) - kfree(rose_neigh->digipeat); kfree(rose_neigh); return; } @@ -309,7 +298,7 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device if (rose_node->loopback) return -EINVAL; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) + if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev) break; if (rose_neigh == NULL) return -EINVAL; @@ -353,16 +342,16 @@ int rose_add_loopback_neigh(void) if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - rose_loopback_neigh->callsign = null_ax25_address; - rose_loopback_neigh->digipeat = NULL; - rose_loopback_neigh->ax25 = NULL; - rose_loopback_neigh->dev = NULL; - rose_loopback_neigh->count = 0; - rose_loopback_neigh->use = 0; - rose_loopback_neigh->dce_mode = 1; - rose_loopback_neigh->loopback = 1; - rose_loopback_neigh->number = rose_neigh_no++; - rose_loopback_neigh->restarted = 1; + rose_loopback_neigh->addr.dest = null_ax25_address; + rose_loopback_neigh->addr.dcount = 0; + rose_loopback_neigh->ax25 = NULL; + rose_loopback_neigh->dev = NULL; + rose_loopback_neigh->count = 0; + rose_loopback_neigh->use = 0; + rose_loopback_neigh->dce_mode = 1; + rose_loopback_neigh->loopback = 1; + rose_loopback_neigh->number = rose_neigh_no++; + rose_loopback_neigh->restarted = 1; skb_queue_head_init(&rose_loopback_neigh->queue); @@ -787,11 +776,11 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) dest_addr = (rose_address *)(skb->data + 4); for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev) + if (ax25cmp(&ax25->addr.dest, &rose_neigh->addr.dest) == 0 && ax25->device == rose_neigh->dev) break; if (rose_neigh == NULL) { - printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr)); + printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->addr.dest)); return 0; } @@ -1034,7 +1023,7 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) /* if (!rose_neigh->loopback) { */ len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", rose_neigh->number, - (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->callsign), + (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->addr.dest), rose_neigh->dev ? rose_neigh->dev->name : "???", rose_neigh->count, rose_neigh->use, @@ -1043,9 +1032,9 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) ax25_display_timer(&rose_neigh->t0timer) / HZ, ax25_display_timer(&rose_neigh->ftimer) / HZ); - if (rose_neigh->digipeat != NULL) { - for (i = 0; i < rose_neigh->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i])); + if (rose_neigh->addr.dcount != 0) { + for (i = 0; i < rose_neigh->addr.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->addr.digipeater[i])); } len += sprintf(buffer + len, "\n"); diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 384347a0e..f881e5da5 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -119,8 +119,9 @@ void rose_write_internal(struct sock *sk, int frametype) unsigned char lci1, lci2; char buffer[100]; int len, faclen = 0; + int ax25_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1; - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1; + len = ax25_header_len + ROSE_MIN_LEN; switch (frametype) { case ROSE_CALL_REQUEST: @@ -141,9 +142,9 @@ void rose_write_internal(struct sock *sk, int frametype) /* * Space for AX.25 header and PID. */ - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1); + skb_reserve(skb, ax25_header_len); - dptr = skb_put(skb, skb_tailroom(skb)); + dptr = skb_put(skb, len - ax25_header_len); lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F; lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF; |