diff options
| author | David S. Miller <davem@nuts.ninka.net> | 2002-09-18 02:14:02 -0700 |
|---|---|---|
| committer | David S. Miller <davem@nuts.ninka.net> | 2002-09-18 02:14:02 -0700 |
| commit | c2edf82620682e0384b7d95fea359b082a626f7e (patch) | |
| tree | d39fcabf56d9cb37c58059f375169c1e4c6875e3 | |
| parent | 5f761bd3d04f12d0cf163544b8ef09677a0b0c2e (diff) | |
| parent | e5e1cba9ef21644d4f550620717c835b430f2b26 (diff) | |
Merge http://linux-ham.bkbits.net/linux-2.5
into nuts.ninka.net:/home/davem/src/BK/ham-2.5
41 files changed, 3971 insertions, 3987 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 46a74039e0e4..d2a92cd31728 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -240,8 +240,8 @@ L: linux-net@vger.kernel.org S: Maintained AX.25 NETWORK LAYER -P: Matthias Welwarsky -M: dg2fef@afthd.tu-darmstadt.de +P: Ralf Baechle +M: ralf@linux-mips.org L: linux-hams@vger.kernel.org S: Maintained @@ -1121,8 +1121,8 @@ L: netfilter-devel@lists.netfilter.org S: Supported NETROM NETWORK LAYER -P: Tomi Manninen -M: Tomi.Manninen@hut.fi +P: Ralf Baechle +M: ralf@linux-mips.org L: linux-hams@vger.kernel.org S: Maintained @@ -1371,8 +1371,8 @@ W: http://www.namesys.com S: Supported ROSE NETWORK LAYER -P: Jean-Paul Roubelat -M: jpr@f6fbb.org +P: Ralf Baechle +M: ralf@linux-mips.org L: linux-hams@vger.kernel.org S: Maintained diff --git a/include/net/ax25.h b/include/net/ax25.h index 334127932384..c34da24f1902 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -3,11 +3,14 @@ * * Alan Cox (GW4PTS) 10/11/93 */ - #ifndef _AX25_H #define _AX25_H + #include <linux/config.h> #include <linux/ax25.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <asm/atomic.h> #define AX25_T1CLAMPLO 1 #define AX25_T1CLAMPHI (30 * HZ) @@ -66,6 +69,8 @@ #define AX25_UA 0x63 /* Unnumbered acknowledge */ #define AX25_FRMR 0x87 /* Frame reject */ #define AX25_UI 0x03 /* Unnumbered information */ +#define AX25_XID 0xaf /* Exchange information */ +#define AX25_TEST 0xe3 /* Test */ #define AX25_PF 0x10 /* Poll/final bit for standard AX.25 */ #define AX25_EPF 0x01 /* Poll/final bit for extended AX.25 */ @@ -147,10 +152,12 @@ typedef struct { typedef struct ax25_route { struct ax25_route *next; + atomic_t ref; ax25_address callsign; - struct net_device *dev; + struct net_device *dev; ax25_digi *digipeat; char ip_mode; + struct timer_list timer; } ax25_route; typedef struct { @@ -197,11 +204,12 @@ typedef struct ax25_cb { #define ax25_sk(__sk) ((ax25_cb *)(__sk)->protinfo) /* af_ax25.c */ -extern ax25_cb *volatile ax25_list; +extern ax25_cb *ax25_list; +extern spinlock_t ax25_list_lock; extern void ax25_free_cb(ax25_cb *); extern void ax25_insert_socket(ax25_cb *); struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int); -struct sock *ax25_find_socket(ax25_address *, ax25_address *, int); +struct sock *ax25_get_socket(ax25_address *, ax25_address *, int); extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct net_device *); extern struct sock *ax25_addr_match(ax25_address *); extern void ax25_send_to_raw(struct sock *, struct sk_buff *, int); @@ -224,6 +232,7 @@ extern void ax25_digi_invert(ax25_digi *, ax25_digi *); /* ax25_dev.c */ extern ax25_dev *ax25_dev_list; +extern spinlock_t ax25_dev_lock; extern ax25_dev *ax25_dev_ax25dev(struct net_device *); extern ax25_dev *ax25_addr_ax25dev(ax25_address *); extern void ax25_dev_device_up(struct net_device *); @@ -286,10 +295,16 @@ 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 int ax25_rt_autobind(ax25_cb *, ax25_address *); -extern ax25_route *ax25_rt_find_route(ax25_address *, struct net_device *); +extern ax25_route *ax25_rt_find_route(ax25_route *, ax25_address *, + struct net_device *); extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); extern void ax25_rt_free(void); +static inline void ax25_put_route(ax25_route *ax25_rt) +{ + atomic_dec(&ax25_rt->ref); +} + /* ax25_std_in.c */ extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); diff --git a/net/ax25/Config.in b/net/ax25/Config.in index b8e5d7333052..783d86605e77 100644 --- a/net/ax25/Config.in +++ b/net/ax25/Config.in @@ -1,7 +1,7 @@ # # Amateur Radio protocols and AX.25 device configuration # -# 19971130 Now in an own category to make correct compilation of the +# 19971130 Now in an own category to make correct compilation of the # AX.25 stuff easier... # Joerg Reuter DL1BKE <jreuter@yaina.de> # 19980129 Moved to net/ax25/Config.in, sourcing device drivers. diff --git a/net/ax25/TODO b/net/ax25/TODO new file mode 100644 index 000000000000..4089c49e45cc --- /dev/null +++ b/net/ax25/TODO @@ -0,0 +1,24 @@ +Do the ax25_list_lock, ax25_dev_lock, linkfail_lockreally, ax25_frag_lock and +listen_lock have to be bh-safe? + +Do the netrom and rose locks have to be bh-safe? + +A device might be deleted after lookup in the SIOCADDRT ioctl but before it's +being used. + +Routes to a device being taken down might be deleted by ax25_rt_device_down +but added by somebody else before the device has been deleted fully. + +Massive amounts of lock_kernel / unlock_kernel are just a temporary solution to +get around the removal of SOCKOPS_WRAP. A serious locking strategy has to be +implemented. + +The ax25_rt_find_route synopsys is pervert but I somehow had to deal with +the race caused by the static variable in it's previous implementation. + +Implement proper socket locking in netrom and rose. + +Check socket locking when ax25_rcv is sending to raw sockets. In particular +ax25_send_to_raw() seems fishy. Heck - ax25_rcv is fishy. + +Handle XID and TEST frames properly. diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 661335502baf..9b250f0da085 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1,109 +1,18 @@ /* - * AX.25 release 038 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 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. + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk) + * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) + * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) + * Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl) + * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) */ - #include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> @@ -114,6 +23,7 @@ #include <linux/sched.h> #include <linux/timer.h> #include <linux/string.h> +#include <linux/smp_lock.h> #include <linux/sockios.h> #include <linux/net.h> #include <net/ax25.h> @@ -134,13 +44,15 @@ #include <linux/netfilter.h> #include <linux/sysctl.h> #include <linux/init.h> +#include <linux/spinlock.h> #include <net/tcp.h> #include <net/ip.h> #include <net/arp.h> -ax25_cb *volatile ax25_list; +ax25_cb *ax25_list; +spinlock_t ax25_list_lock = SPIN_LOCK_UNLOCKED; static struct proto_ops ax25_proto_ops; @@ -171,27 +83,24 @@ static void ax25_free_sock(struct sock *sk) static void ax25_remove_socket(ax25_cb *ax25) { ax25_cb *s; - unsigned long flags; - - save_flags(flags); cli(); + spin_lock_bh(&ax25_list_lock); if ((s = ax25_list) == ax25) { ax25_list = s->next; - restore_flags(flags); + spin_unlock_bh(&ax25_list_lock); return; } while (s != NULL && s->next != NULL) { if (s->next == ax25) { s->next = ax25->next; - restore_flags(flags); + spin_unlock_bh(&ax25_list_lock); return; } s = s->next; } - - restore_flags(flags); + spin_unlock_bh(&ax25_list_lock); } /* @@ -205,18 +114,21 @@ static void ax25_kill_by_device(struct net_device *dev) if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) return; + spin_lock_bh(&ax25_list_lock); for (s = ax25_list; s != NULL; s = s->next) { if (s->ax25_dev == ax25_dev) { s->ax25_dev = NULL; ax25_disconnect(s, ENETUNREACH); } } + spin_unlock_bh(&ax25_list_lock); } /* * Handle device status changes. */ -static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr) +static int ax25_device_event(struct notifier_block *this, unsigned long event, + void *ptr) { struct net_device *dev = (struct net_device *)ptr; @@ -225,16 +137,16 @@ static int ax25_device_event(struct notifier_block *this,unsigned long event, vo 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; + 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; @@ -245,64 +157,59 @@ static int ax25_device_event(struct notifier_block *this,unsigned long event, vo */ void ax25_insert_socket(ax25_cb *ax25) { - unsigned long flags; - - save_flags(flags); - cli(); - + spin_lock_bh(&ax25_list_lock); ax25->next = ax25_list; ax25_list = ax25; - - restore_flags(flags); + spin_unlock_bh(&ax25_list_lock); } /* * Find a socket that wants to accept the SABM we have just * received. */ -struct sock *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev, int type) +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(); - + spin_lock_bh(&ax25_list_lock); 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); + spin_unlock_bh(&ax25_list_lock); + return s->sk; } } } + spin_unlock_bh(&ax25_list_lock); - restore_flags(flags); return NULL; } /* * Find an AX.25 socket given both ends. */ -struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type) +struct sock *ax25_get_socket(ax25_address *my_addr, ax25_address *dest_addr, + int type) { + struct sock *sk = NULL; ax25_cb *s; - unsigned long flags; - - save_flags(flags); - cli(); + spin_lock_bh(&ax25_list_lock); 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; + sk = s->sk; + lock_sock(sk); + break; } } - restore_flags(flags); +out: + spin_unlock_bh(&ax25_list_lock); return NULL; } @@ -311,14 +218,12 @@ struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, in * 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. */ -ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct net_device *dev) +ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, + ax25_digi *digi, struct net_device *dev) { ax25_cb *s; - unsigned long flags; - - save_flags(flags); - cli(); + spin_lock_bh(&ax25_list_lock); for (s = ax25_list; s != NULL; s = s->next) { if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) continue; @@ -334,12 +239,12 @@ ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi if (s->digipeat != NULL && s->digipeat->ndigi != 0) continue; } - restore_flags(flags); + spin_unlock_bh(&ax25_list_lock); + return s; } } - - restore_flags(flags); + spin_unlock_bh(&ax25_list_lock); return NULL; } @@ -349,22 +254,21 @@ ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi */ struct sock *ax25_addr_match(ax25_address *addr) { - unsigned long flags; + struct sock *sk = NULL; ax25_cb *s; - save_flags(flags); - cli(); - + spin_lock_bh(&ax25_list_lock); 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; + if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && + s->sk->type == SOCK_RAW) { + sk = s->sk; + lock_sock(sk); + break; } } + spin_unlock_bh(&ax25_list_lock); - restore_flags(flags); - - return NULL; + return sk; } void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) @@ -400,17 +304,16 @@ static void ax25_destroy_timer(unsigned long 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. + * 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 */ +void ax25_destroy_socket(ax25_cb *ax25) { struct sk_buff *skb; - unsigned long flags; - save_flags(flags); cli(); + ax25_remove_socket(ax25); ax25_stop_heartbeat(ax25); ax25_stop_t1timer(ax25); @@ -418,15 +321,17 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer ax25_stop_t3timer(ax25); ax25_stop_idletimer(ax25); - 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 */ + if (skb->sk != ax25->sk) { + /* A pending connection */ ax25_cb *sax25 = ax25_sk(skb->sk); - skb->sk->dead = 1; /* Queue the unaccepted socket for death */ + /* Queue the unaccepted socket for death */ + skb->sk->dead = 1; + ax25_start_heartbeat(sax25); sax25->state = AX25_STATE_0; } @@ -450,8 +355,6 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer } else { ax25_free_cb(ax25); } - - restore_flags(flags); } /* @@ -484,66 +387,66 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) return -ENOTCONN; switch (ax25_ctl.cmd) { - case AX25_KILL: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + 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); + 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_disconnect(ax25, ENETRESET); + break; - 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) + 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->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; + } + 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; @@ -631,13 +534,14 @@ ax25_cb *ax25_create_cb(void) * AX25 socket object */ -static int ax25_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) +static int ax25_setsockopt(struct socket *sock, int level, int optname, + char *optval, int optlen) { struct sock *sk = sock->sk; - ax25_cb *ax25 = ax25_sk(sk); + ax25_cb *ax25; struct net_device *dev; char devname[IFNAMSIZ]; - int opt; + int opt, res = 0; if (level != SOL_AX25) return -ENOPROTOOPT; @@ -648,98 +552,131 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op if (get_user(opt, (int *)optval)) return -EFAULT; + lock_sock(sk); + ax25 = ax25_sk(sk); + switch (optname) { - case AX25_WINDOW: - if (ax25->modulus == AX25_MODULUS) { - if (opt < 1 || opt > 7) - return -EINVAL; - } else { - if (opt < 1 || opt > 63) - return -EINVAL; + case AX25_WINDOW: + if (ax25->modulus == AX25_MODULUS) { + if (opt < 1 || opt > 7) { + res = -EINVAL; + break; } - ax25->window = opt; - return 0; - - case AX25_T1: - if (opt < 1) - return -EINVAL; - ax25->rtt = (opt * HZ) / 2; - ax25->t1 = opt * HZ; - return 0; - - case AX25_T2: - if (opt < 1) - return -EINVAL; - ax25->t2 = opt * HZ; - return 0; - - case AX25_N2: - if (opt < 1 || opt > 31) - return -EINVAL; - ax25->n2 = opt; - return 0; - - case AX25_T3: - if (opt < 1) - return -EINVAL; - ax25->t3 = opt * HZ; - return 0; - - case AX25_IDLE: - if (opt < 0) - return -EINVAL; - ax25->idle = opt * 60 * HZ; - return 0; - - case AX25_BACKOFF: - if (opt < 0 || opt > 2) - return -EINVAL; - ax25->backoff = opt; - return 0; - - case AX25_EXTSEQ: - ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; - return 0; - - case AX25_PIDINCL: - ax25->pidincl = opt ? 1 : 0; - return 0; - - case AX25_IAMDIGI: - ax25->iamdigi = opt ? 1 : 0; - return 0; - - case AX25_PACLEN: - if (opt < 16 || opt > 65535) - return -EINVAL; - ax25->paclen = opt; - 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; - - ax25->ax25_dev = ax25_dev_ax25dev(dev); - ax25_fillin_cb(ax25, ax25->ax25_dev); - return 0; + } else { + if (opt < 1 || opt > 63) { + res = -EINVAL; + break; + } + } + ax25->window = opt; + break; - default: - return -ENOPROTOOPT; + case AX25_T1: + if (opt < 1) { + res = -EINVAL; + break; + } + ax25->rtt = (opt * HZ) / 2; + ax25->t1 = opt * HZ; + break; + + case AX25_T2: + if (opt < 1) { + res = -EINVAL; + break; + } + ax25->t2 = opt * HZ; + break; + + case AX25_N2: + if (opt < 1 || opt > 31) { + res = -EINVAL; + break; + } + ax25->n2 = opt; + break; + + case AX25_T3: + if (opt < 1) { + res = -EINVAL; + break; + } + ax25->t3 = opt * HZ; + break; + + case AX25_IDLE: + if (opt < 0) { + res = -EINVAL; + break; + } + ax25->idle = opt * 60 * HZ; + break; + + case AX25_BACKOFF: + if (opt < 0 || opt > 2) { + res = -EINVAL; + break; + } + ax25->backoff = opt; + break; + + case AX25_EXTSEQ: + ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; + break; + + case AX25_PIDINCL: + ax25->pidincl = opt ? 1 : 0; + break; + + case AX25_IAMDIGI: + ax25->iamdigi = opt ? 1 : 0; + break; + + case AX25_PACLEN: + if (opt < 16 || opt > 65535) { + res = -EINVAL; + break; + } + ax25->paclen = opt; + break; + + case SO_BINDTODEVICE: + if (optlen > IFNAMSIZ) + optlen=IFNAMSIZ; + if (copy_from_user(devname, optval, optlen)) { + res = -EFAULT; + break; + } + + dev = dev_get_by_name(devname); + if (dev == NULL) { + res = -ENODEV; + break; + } + + if (sk->type == SOCK_SEQPACKET && + (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) { + res = -EADDRNOTAVAIL; + break; + } + + ax25->ax25_dev = ax25_dev_ax25dev(dev); + ax25_fillin_cb(ax25, ax25->ax25_dev); + break; + + default: + res = -ENOPROTOOPT; } + release_sock(sk); + + return res; } -static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) +static int ax25_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen) { struct sock *sk = sock->sk; - ax25_cb *ax25 = ax25_sk(sk); + ax25_cb *ax25; struct ax25_dev *ax25_dev; char devname[IFNAMSIZ]; void *valptr; @@ -751,76 +688,81 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op if (get_user(maxlen, optlen)) return -EFAULT; - + if (maxlen < 1) return -EFAULT; valptr = (void *) &val; length = min_t(unsigned int, maxlen, sizeof(int)); + lock_sock(sk); + ax25 = ax25_sk(sk); + switch (optname) { - case AX25_WINDOW: - val = ax25->window; - break; + case AX25_WINDOW: + val = ax25->window; + break; - case AX25_T1: - val = ax25->t1 / HZ; - break; + case AX25_T1: + val = ax25->t1 / HZ; + break; - case AX25_T2: - val = ax25->t2 / HZ; - break; + case AX25_T2: + val = ax25->t2 / HZ; + break; - case AX25_N2: - val = ax25->n2; - break; + case AX25_N2: + val = ax25->n2; + break; - case AX25_T3: - val = ax25->t3 / HZ; - break; + case AX25_T3: + val = ax25->t3 / HZ; + break; - case AX25_IDLE: - val = ax25->idle / (60 * HZ); - break; + case AX25_IDLE: + val = ax25->idle / (60 * HZ); + break; - case AX25_BACKOFF: - val = ax25->backoff; - break; + case AX25_BACKOFF: + val = ax25->backoff; + break; - case AX25_EXTSEQ: - val = (ax25->modulus == AX25_EMODULUS); - break; + case AX25_EXTSEQ: + val = (ax25->modulus == AX25_EMODULUS); + break; - case AX25_PIDINCL: - val = ax25->pidincl; - break; + case AX25_PIDINCL: + val = ax25->pidincl; + break; - case AX25_IAMDIGI: - val = ax25->iamdigi; - break; + case AX25_IAMDIGI: + val = ax25->iamdigi; + break; - case AX25_PACLEN: - val = ax25->paclen; - break; - - case SO_BINDTODEVICE: - ax25_dev = ax25->ax25_dev; - - if (ax25_dev != NULL && ax25_dev->dev != NULL) { - strncpy(devname, ax25_dev->dev->name, IFNAMSIZ); - length = min_t(unsigned int, strlen(ax25_dev->dev->name)+1, maxlen); - devname[length-1] = '\0'; - } else { - *devname = '\0'; - length = 1; - } + case AX25_PACLEN: + val = ax25->paclen; + break; - valptr = (void *) devname; - break; + case SO_BINDTODEVICE: + ax25_dev = ax25->ax25_dev; - default: - return -ENOPROTOOPT; + if (ax25_dev != NULL && ax25_dev->dev != NULL) { + strncpy(devname, ax25_dev->dev->name, IFNAMSIZ); + length = min_t(unsigned int, strlen(ax25_dev->dev->name)+1, maxlen); + devname[length-1] = '\0'; + } else { + *devname = '\0'; + length = 1; + } + + valptr = (void *) devname; + break; + + default: + release_sock(sk); + return -ENOPROTOOPT; } + release_sock(sk); if (put_user(length, optlen)) return -EFAULT; @@ -831,14 +773,20 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op static int ax25_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; + int res = 0; + lock_sock(sk); if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) { sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; - return 0; + goto out; } + res = -EOPNOTSUPP; - return -EOPNOTSUPP; +out: + release_sock(sk); + + return res; } int ax25_create(struct socket *sock, int protocol) @@ -847,46 +795,48 @@ int ax25_create(struct socket *sock, int protocol) 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: + case AX25_P_IP: #endif #ifdef CONFIG_NETROM - case AX25_P_NETROM: + case AX25_P_NETROM: #endif #ifdef CONFIG_ROSE - case AX25_P_ROSE: + case AX25_P_ROSE: #endif - return -ESOCKTNOSUPPORT; + return -ESOCKTNOSUPPORT; #ifdef CONFIG_NETROM_MODULE - case AX25_P_NETROM: - if (ax25_protocol_is_registered(AX25_P_NETROM)) - return -ESOCKTNOSUPPORT; + case AX25_P_NETROM: + if (ax25_protocol_is_registered(AX25_P_NETROM)) + return -ESOCKTNOSUPPORT; #endif #ifdef CONFIG_ROSE_MODULE - case AX25_P_ROSE: - if (ax25_protocol_is_registered(AX25_P_ROSE)) - return -ESOCKTNOSUPPORT; + 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; + break; + } + break; + + case SOCK_RAW: + break; + default: + return -ESOCKTNOSUPPORT; } if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1, NULL)) == NULL) @@ -923,14 +873,14 @@ struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) } switch (osk->type) { - case SOCK_DGRAM: - break; - case SOCK_SEQPACKET: - break; - default: - sk_free(sk); - ax25_free_cb(ax25); - return NULL; + case SOCK_DGRAM: + break; + case SOCK_SEQPACKET: + break; + default: + sk_free(sk); + ax25_free_cb(ax25); + return NULL; } sock_init_data(NULL, sk); @@ -985,58 +935,61 @@ static int ax25_release(struct socket *sock) struct sock *sk = sock->sk; ax25_cb *ax25; - if (sk == NULL) return 0; + if (sk == NULL) + return 0; + lock_sock(sk); ax25 = ax25_sk(sk); if (sk->type == SOCK_SEQPACKET) { switch (ax25->state) { - case AX25_STATE_0: - ax25_disconnect(ax25, 0); - ax25_destroy_socket(ax25); - break; + case AX25_STATE_0: + ax25_disconnect(ax25, 0); + ax25_destroy_socket(ax25); + break; - case AX25_STATE_1: - case AX25_STATE_2: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, 0); - ax25_destroy_socket(ax25); - break; + case AX25_STATE_1: + case AX25_STATE_2: + ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + ax25_disconnect(ax25, 0); + ax25_destroy_socket(ax25); + break; - case AX25_STATE_3: - case AX25_STATE_4: - ax25_clear_queues(ax25); - ax25->n2count = 0; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_control(ax25, - AX25_DISC, - AX25_POLLON, - AX25_COMMAND); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - break; + case AX25_STATE_3: + case AX25_STATE_4: + ax25_clear_queues(ax25); + ax25->n2count = 0; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_send_control(ax25, + AX25_DISC, + AX25_POLLON, + AX25_COMMAND); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); + break; #ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - break; -#endif - } - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25->state = AX25_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; + case AX25_PROTO_DAMA_SLAVE: + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); break; +#endif + } + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + 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; + default: + break; } } else { sk->state = TCP_CLOSE; @@ -1046,8 +999,9 @@ static int ax25_release(struct socket *sock) ax25_destroy_socket(ax25); } - sock->sk = NULL; + sock->sk = NULL; sk->socket = NULL; /* Not used, but we should do this */ + release_sock(sk); return 0; } @@ -1055,26 +1009,25 @@ static int ax25_release(struct socket *sock) /* * 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 + * that we've implemented support for SO_BINDTODEVICE. It is however small * and trivially backward compatible. */ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; - ax25_cb *ax25 = ax25_sk(sk); struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; - ax25_address *call; ax25_dev *ax25_dev = NULL; + ax25_address *call; + ax25_cb *ax25; + int err = 0; - if (sk->zapped == 0) - return -EINVAL; - - if (addr_len != sizeof(struct sockaddr_ax25) && + 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))) + (addr_len > sizeof(struct full_sockaddr_ax25))) { return -EINVAL; + } printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n", current->comm); @@ -1084,8 +1037,17 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EINVAL; call = ax25_findbyuid(current->euid); - if (call == NULL && ax25_uid_policy && !capable(CAP_NET_ADMIN)) + if (call == NULL && ax25_uid_policy && !capable(CAP_NET_ADMIN)) { return -EACCES; + } + + lock_sock(sk); + + ax25 = ax25_sk(sk); + if (sk->zapped == 0) { + err = -EINVAL; + goto out; + } if (call == NULL) ax25->source_addr = addr->fsa_ax25.sax25_call; @@ -1095,17 +1057,20 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) /* * User already set interface with SO_BINDTODEVICE */ - if (ax25->ax25_dev != NULL) goto done; 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; + (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) { + err = -EADDRNOTAVAIL; + goto out; + } + } else { + if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) { + err = -EADDRNOTAVAIL; + goto out; + } } if (ax25_dev != NULL) @@ -1114,41 +1079,24 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) done: ax25_insert_socket(ax25); sk->zapped = 0; + +out: + release_sock(sk); + 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; ax25_cb *ax25 = ax25_sk(sk); struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; ax25_digi *digi = NULL; - int ct = 0, err; - - /* deal with restarts */ - if (sock->state == SS_CONNECTING) { - switch (sk->state) { - case TCP_SYN_SENT: /* still trying */ - return -EINPROGRESS; - - case TCP_ESTABLISHED: /* connection established */ - sock->state = SS_CONNECTED; - return 0; - - case TCP_CLOSE: /* connection refused */ - sock->state = SS_UNCONNECTED; - return -ECONNREFUSED; - } - } - - if (sk->state == TCP_ESTABLISHED && sk->type == SOCK_SEQPACKET) - return -EISCONN; /* No reconnect on a seqpacket socket */ - - sk->state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; + int ct = 0, err = 0; /* * some sanity checks. code further down depends on this @@ -1162,8 +1110,9 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le 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))) + (addr_len > sizeof(struct full_sockaddr_ax25))) { return -EINVAL; + } printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n", current->comm); @@ -1172,21 +1121,54 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (fsa->fsa_ax25.sax25_family != AF_AX25) return -EINVAL; + lock_sock(sk); + + /* deal with restarts */ + if (sock->state == SS_CONNECTING) { + switch (sk->state) { + case TCP_SYN_SENT: /* still trying */ + err = -EINPROGRESS; + goto out; + + case TCP_ESTABLISHED: /* connection established */ + sock->state = SS_CONNECTED; + goto out; + + case TCP_CLOSE: /* connection refused */ + sock->state = SS_UNCONNECTED; + err = -ECONNREFUSED; + goto out; + } + } + + if (sk->state == TCP_ESTABLISHED && sk->type == SOCK_SEQPACKET) { + err = -EISCONN; /* No reconnect on a seqpacket socket */ + goto out; + } + + sk->state = TCP_CLOSE; + sock->state = SS_UNCONNECTED; + if (ax25->digipeat != NULL) { kfree(ax25->digipeat); ax25->digipeat = NULL; } - + /* * Handle digi-peaters to be used. */ - if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) { + if (addr_len > sizeof(struct sockaddr_ax25) && + fsa->fsa_ax25.sax25_ndigis != 0) { /* Valid number of digipeaters ? */ - if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) - return -EINVAL; + if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) { + err = -EINVAL; + goto out; + } - if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) - return -ENOBUFS; + if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { + err = -ENOBUFS; + goto out; + } digi->ndigi = fsa->fsa_ax25.sax25_ndigis; digi->lastrepeat = -1; @@ -1214,19 +1196,24 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n", current->comm); if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0) - return err; + goto out; + ax25_fillin_cb(ax25, ax25->ax25_dev); ax25_insert_socket(ax25); } else { - if (ax25->ax25_dev == NULL) - return -EHOSTUNREACH; + if (ax25->ax25_dev == NULL) { + err = -EHOSTUNREACH; + goto out; + } } if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, ax25->ax25_dev->dev)) { - if (digi != NULL) kfree(digi); - return -EADDRINUSE; /* Already such a connection */ + if (digi != NULL) + kfree(digi); + err = -EADDRINUSE; /* Already such a connection */ + goto out; } ax25->dest_addr = fsa->fsa_ax25.sax25_call; @@ -1236,7 +1223,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (sk->type != SOCK_SEQPACKET) { sock->state = SS_CONNECTED; sk->state = TCP_ESTABLISHED; - return 0; + goto out; } /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ @@ -1244,21 +1231,20 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le sk->state = TCP_SYN_SENT; 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; + 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: - ax25->modulus = AX25_MODULUS; - ax25->window = - ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - if (ax25->ax25_dev->dama.slave) - ax25_ds_establish_data_link(ax25); - else - ax25_std_establish_data_link(ax25); - break; + case AX25_PROTO_DAMA_SLAVE: + ax25->modulus = AX25_MODULUS; + ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; + if (ax25->ax25_dev->dama.slave) + ax25_ds_establish_data_link(ax25); + else + ax25_std_establish_data_link(ax25); + break; #endif } @@ -1267,30 +1253,43 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ax25_start_heartbeat(ax25); /* Now the loop */ - if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) - return -EINPROGRESS; + if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { + err = -EINPROGRESS; + goto out; + } - cli(); /* To avoid races on the sleep */ + if (sk->state == TCP_SYN_SENT) { + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); - /* A DM or timeout will go to closed, a UA will go to ABM */ - while (sk->state == TCP_SYN_SENT) { - interruptible_sleep_on(sk->sleep); - if (signal_pending(current)) { - sti(); + add_wait_queue(sk->sleep, &wait); + for (;;) { + if (sk->state != TCP_SYN_SENT) + break; + set_current_state(TASK_INTERRUPTIBLE); + release_sock(sk); + if (!signal_pending(tsk)) { + schedule(); + lock_sock(sk); + continue; + } return -ERESTARTSYS; } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); } if (sk->state != TCP_ESTABLISHED) { /* Not in ABM, not in WAIT_UA -> failed */ - sti(); sock->state = SS_UNCONNECTED; - return sock_error(sk); /* Always set at this point */ + err = sock_error(sk); /* Always set at this point */ + goto out; } sock->state = SS_CONNECTED; - sti(); +out: + release_sock(sk); return 0; } @@ -1298,9 +1297,12 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) { - struct sock *sk; - struct sock *newsk; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); struct sk_buff *skb; + struct sock *newsk; + struct sock *sk; + int err = 0; if (sock->state != SS_UNCONNECTED) return -EINVAL; @@ -1308,26 +1310,40 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) if ((sk = sock->sk) == NULL) return -EINVAL; - if (sk->type != SOCK_SEQPACKET) - return -EOPNOTSUPP; + lock_sock(sk); + if (sk->type != SOCK_SEQPACKET) { + err = -EOPNOTSUPP; + goto out; + } - if (sk->state != TCP_LISTEN) - return -EINVAL; + if (sk->state != TCP_LISTEN) { + err = -EINVAL; + goto out; + } /* * The read queue this time is holding sockets ready to use * hooked into the SABM we saved */ - do { - if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { - if (flags & O_NONBLOCK) - return -EWOULDBLOCK; - - interruptible_sleep_on(sk->sleep); - if (signal_pending(current)) - return -ERESTARTSYS; + add_wait_queue(sk->sleep, &wait); + for (;;) { + skb = skb_dequeue(&sk->receive_queue); + if (skb) + break; + + current->state = TASK_INTERRUPTIBLE; + release_sock(sk); + if (flags & O_NONBLOCK) + return -EWOULDBLOCK; + if (!signal_pending(tsk)) { + schedule(); + lock_sock(sk); + continue; } - } while (skb == NULL); + return -ERESTARTSYS; + } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); newsk = skb->sk; newsk->pair = NULL; @@ -1340,19 +1356,29 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) newsock->sk = newsk; newsock->state = SS_CONNECTED; - return 0; +out: + release_sock(sk); + + return err; } -static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) +static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) { - struct sock *sk = sock->sk; - ax25_cb *ax25 = ax25_sk(sk); struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; + struct sock *sk = sock->sk; unsigned char ndigi, i; + ax25_cb *ax25; + int err = 0; + + lock_sock(sk); + ax25 = ax25_sk(sk); if (peer != 0) { - if (sk->state != TCP_ESTABLISHED) - return -ENOTCONN; + if (sk->state != TCP_ESTABLISHED) { + err = -ENOTCONN; + goto out; + } fsa->fsa_ax25.sax25_family = AF_AX25; fsa->fsa_ax25.sax25_call = ax25->dest_addr; @@ -1377,41 +1403,53 @@ static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_ } } *uaddr_len = sizeof (struct full_sockaddr_ax25); - return 0; + +out: + release_sock(sk); + + return err; } -static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) +static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, + struct scm_cookie *scm) { - struct sock *sk = sock->sk; - ax25_cb *ax25 = ax25_sk(sk); struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name; - int err; + struct sock *sk = sock->sk; struct sockaddr_ax25 sax; struct sk_buff *skb; + ax25_digi dtmp, *dp; unsigned char *asmptr; - int size; - ax25_digi *dp; - ax25_digi dtmp; - int lv; - int addr_len = msg->msg_namelen; + ax25_cb *ax25; + int lv, size, err, addr_len = msg->msg_namelen; - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) { return -EINVAL; + } + + lock_sock(sk); + ax25 = ax25_sk(sk); - if (sk->zapped) - return -EADDRNOTAVAIL; + if (sk->zapped) { + err = -EADDRNOTAVAIL; + goto out; + } if (sk->shutdown & SEND_SHUTDOWN) { send_sig(SIGPIPE, current, 0); - return -EPIPE; + err = -EPIPE; + goto out; } - if (ax25->ax25_dev == NULL) - return -ENETUNREACH; + if (ax25->ax25_dev == NULL) { + err = -ENETUNREACH; + goto out; + } if (usax != NULL) { - if (usax->sax25_family != AF_AX25) - return -EINVAL; + if (usax->sax25_family != AF_AX25) { + err = -EINVAL; + goto out; + } if (addr_len == sizeof(struct sockaddr_ax25)) { printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n", @@ -1420,8 +1458,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct 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; + (addr_len > sizeof(struct full_sockaddr_ax25))) { + err = -EINVAL; + goto out; + } printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n", current->comm); @@ -1432,8 +1472,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; /* Valid number of digipeaters ? */ - if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) - return -EINVAL; + if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) { + err = -EINVAL; + goto out; + } dtmp.ndigi = usax->sax25_ndigis; @@ -1447,8 +1489,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct } sax = *usax; - if (sk->type == SOCK_SEQPACKET && ax25cmp(&ax25->dest_addr, &sax.sax25_call) != 0) - return -EISCONN; + if (sk->type == SOCK_SEQPACKET && ax25cmp(&ax25->dest_addr, &sax.sax25_call) != 0) { + err = -EISCONN; + goto out; + } if (usax->sax25_ndigis == 0) dp = NULL; else @@ -1459,8 +1503,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct * it has become closed (not started closed) and is VC * we ought to SIGPIPE, EPIPE */ - if (sk->state != TCP_ESTABLISHED) - return -ENOTCONN; + if (sk->state != TCP_ESTABLISHED) { + err = -ENOTCONN; + goto out; + } sax.sax25_family = AF_AX25; sax.sax25_call = ax25->dest_addr; dp = ax25->digipeat; @@ -1474,8 +1520,9 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct /* Assume the worst case */ size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN; - if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) - return err; + skb = sock_alloc_send_skb(sk, size, msg->msg_flags&MSG_DONTWAIT, &err); + if (skb == NULL) + goto out; skb_reserve(skb, size - len); @@ -1497,60 +1544,73 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct /* Connected mode sockets go via the LAPB machine */ if (sk->state != TCP_ESTABLISHED) { kfree_skb(skb); - return -ENOTCONN; + err = -ENOTCONN; + goto out; } /* Shove it onto the queue and kick */ ax25_output(ax25, ax25->paclen, skb); - return len; - } else { - asmptr = skb_push(skb, 1 + ax25_addr_size(dp)); + err = len; + goto out; + } - SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp); + asmptr = skb_push(skb, 1 + ax25_addr_size(dp)); - if (dp != NULL) - SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi); + SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp); - /* Build an AX.25 header */ - asmptr += (lv = ax25_addr_build(asmptr, &ax25->source_addr, - &sax.sax25_call, dp, - AX25_COMMAND, AX25_MODULUS)); + if (dp != NULL) + SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi); - SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); + /* Build an AX.25 header */ + asmptr += (lv = ax25_addr_build(asmptr, &ax25->source_addr, + &sax.sax25_call, dp, + AX25_COMMAND, AX25_MODULUS)); - skb->h.raw = asmptr; + SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); - SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); + skb->h.raw = asmptr; - *asmptr = AX25_UI; + SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); - /* Datagram frames go straight out of the door as UI */ - skb->dev = ax25->ax25_dev->dev; + *asmptr = AX25_UI; - ax25_queue_xmit(skb); + /* Datagram frames go straight out of the door as UI */ + skb->dev = ax25->ax25_dev->dev; - return len; - } + ax25_queue_xmit(skb); + + err = len; + +out: + release_sock(sk); + + return err; } -static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) +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 copied; + int err = 0; + lock_sock(sk); /* * This works for seqpacket too. The receiver has ordered the * queue for us! We do one quick check first though */ - if (sk->type == SOCK_SEQPACKET && sk->state != TCP_ESTABLISHED) - return -ENOTCONN; + if (sk->type == SOCK_SEQPACKET && sk->state != TCP_ESTABLISHED) { + err = -ENOTCONN; + goto out; + } /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) - return er; + skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, + flags & MSG_DONTWAIT, &err); + if (skb == NULL) + goto out; if (!ax25_sk(sk)->pidincl) skb_pull(skb, 1); /* Remove PID */ @@ -1561,7 +1621,7 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f if (copied > size) { copied = size; msg->msg_flags |= MSG_TRUNC; - } + } skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); @@ -1590,8 +1650,12 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f } skb_free_datagram(sk, skb); + err = copied; - return copied; +out: + release_sock(sk); + + return err; } static int ax25_shutdown(struct socket *sk, int how) @@ -1603,137 +1667,173 @@ static int ax25_shutdown(struct socket *sk, int how) static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; + int res = 0; + lock_sock(sk); switch (cmd) { - case TIOCOUTQ: { - long amount; - amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); - if (amount < 0) - amount = 0; - return put_user(amount, (int *)arg); - } + case TIOCOUTQ: { + long amount; + amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); + if (amount < 0) + amount = 0; + res = put_user(amount, (int *)arg); + break; + } - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - /* 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); - } + case TIOCINQ: { + struct sk_buff *skb; + long amount = 0L; + /* 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; + res = put_user(amount, (int *)arg); + break; + } - 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; - } - return -EINVAL; + case SIOCGSTAMP: + if (sk != NULL) { + if (sk->stamp.tv_sec == 0) { + res = -ENOENT; + break; + } + res = copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0; + break; + } + res = -EINVAL; + break; + + case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ + case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ + case SIOCAX25GETUID: { + struct sockaddr_ax25 sax25; + if (copy_from_user(&sax25, (void *)arg, sizeof(sax25))) { + res = -EFAULT; + break; + } + res = ax25_uid_ioctl(cmd, &sax25); + break; + } - case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ - case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ - case SIOCAX25GETUID: { - struct sockaddr_ax25 sax25; - if (copy_from_user(&sax25, (void *)arg, sizeof(sax25))) - return -EFAULT; - return ax25_uid_ioctl(cmd, &sax25); + case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ + long amount; + if (!capable(CAP_NET_ADMIN)) { + res = -EPERM; + break; } + if (get_user(amount, (long *)arg)) { + res = -EFAULT; + break; + } + if (amount > AX25_NOUID_BLOCK) { + res = -EINVAL; + break; + } + ax25_uid_policy = amount; + res = 0; + break; + } - case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ - long amount; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (get_user(amount, (long *)arg)) - return -EFAULT; - if (amount > AX25_NOUID_BLOCK) - return -EINVAL; - ax25_uid_policy = amount; - return 0; + case SIOCADDRT: + case SIOCDELRT: + case SIOCAX25OPTRT: + if (!capable(CAP_NET_ADMIN)) { + res = -EPERM; + break; } + res = ax25_rt_ioctl(cmd, (void *)arg); + break; - case SIOCADDRT: - case SIOCDELRT: - case SIOCAX25OPTRT: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return ax25_rt_ioctl(cmd, (void *)arg); - - case SIOCAX25CTLCON: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return ax25_ctl_ioctl(cmd, (void *)arg); - - case SIOCAX25GETINFO: - case SIOCAX25GETINFOOLD: { - ax25_cb *ax25 = ax25_sk(sk); - struct ax25_info_struct ax25_info; - - ax25_info.t1 = ax25->t1 / HZ; - ax25_info.t2 = ax25->t2 / HZ; - ax25_info.t3 = ax25->t3 / HZ; - ax25_info.idle = ax25->idle / (60 * HZ); - ax25_info.n2 = ax25->n2; - ax25_info.t1timer = ax25_display_timer(&ax25->t1timer) / HZ; - ax25_info.t2timer = ax25_display_timer(&ax25->t2timer) / HZ; - ax25_info.t3timer = ax25_display_timer(&ax25->t3timer) / HZ; - ax25_info.idletimer = ax25_display_timer(&ax25->idletimer) / (60 * HZ); - ax25_info.n2count = ax25->n2count; - ax25_info.state = ax25->state; - ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); - ax25_info.snd_q = atomic_read(&sk->wmem_alloc); - ax25_info.vs = ax25->vs; - ax25_info.vr = ax25->vr; - ax25_info.va = ax25->va; - ax25_info.vs_max = ax25->vs; /* reserved */ - ax25_info.paclen = ax25->paclen; - ax25_info.window = ax25->window; - - /* old structure? */ - if (cmd == SIOCAX25GETINFOOLD) { - static int warned = 0; - if (!warned) { - printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n", - current->comm); - warned=1; - } - - 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))) - return -EINVAL; - } - return 0; + case SIOCAX25CTLCON: + if (!capable(CAP_NET_ADMIN)) { + res = -EPERM; + break; } + res = ax25_ctl_ioctl(cmd, (void *)arg); + break; + + case SIOCAX25GETINFO: + case SIOCAX25GETINFOOLD: { + ax25_cb *ax25 = ax25_sk(sk); + struct ax25_info_struct ax25_info; + + ax25_info.t1 = ax25->t1 / HZ; + ax25_info.t2 = ax25->t2 / HZ; + ax25_info.t3 = ax25->t3 / HZ; + ax25_info.idle = ax25->idle / (60 * HZ); + ax25_info.n2 = ax25->n2; + ax25_info.t1timer = ax25_display_timer(&ax25->t1timer) / HZ; + ax25_info.t2timer = ax25_display_timer(&ax25->t2timer) / HZ; + ax25_info.t3timer = ax25_display_timer(&ax25->t3timer) / HZ; + ax25_info.idletimer = ax25_display_timer(&ax25->idletimer) / (60 * HZ); + ax25_info.n2count = ax25->n2count; + ax25_info.state = ax25->state; + ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); + ax25_info.snd_q = atomic_read(&sk->wmem_alloc); + ax25_info.vs = ax25->vs; + ax25_info.vr = ax25->vr; + ax25_info.va = ax25->va; + ax25_info.vs_max = ax25->vs; /* reserved */ + ax25_info.paclen = ax25->paclen; + ax25_info.window = ax25->window; + + /* old structure? */ + if (cmd == SIOCAX25GETINFOOLD) { + static int warned = 0; + if (!warned) { + printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n", + current->comm); + warned=1; + } - case SIOCAX25ADDFWD: - case SIOCAX25DELFWD: { - struct ax25_fwd_struct ax25_fwd; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) - return -EFAULT; - return ax25_fwd_ioctl(cmd, &ax25_fwd); + if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) { + res = -EFAULT; + break; + } + } else { + if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct))) { + res = -EINVAL; + break; + } } + res = 0; + break; + } - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - return -EINVAL; + case SIOCAX25ADDFWD: + case SIOCAX25DELFWD: { + struct ax25_fwd_struct ax25_fwd; + if (!capable(CAP_NET_ADMIN)) { + res = -EPERM; + break; + } + if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) { + res = -EFAULT; + break; + } + res = ax25_fwd_ioctl(cmd, &ax25_fwd); + break; + } - default: - return dev_ioctl(cmd, (void *)arg); + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + res = -EINVAL; + break; + + default: + res = dev_ioctl(cmd, (void *)arg); + break; } + release_sock(sk); - /*NOTREACHED*/ - return 0; + return res; } static int ax25_get_info(char *buffer, char **start, off_t offset, int length) @@ -1744,28 +1844,28 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length) off_t pos = 0; off_t begin = 0; - cli(); + spin_lock_bh(&ax25_list_lock); /* * 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 + * 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) { - len += sprintf(buffer+len, "%8.8lx %s %s%s ", - (long) ax25, + 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++) { len += sprintf(buffer+len, ",%s%s", ax2asc(&ax25->digipeat->calls[k]), ax25->digipeat->repeated[k]? "*":""); } - + len += sprintf(buffer+len, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d", ax25->state, ax25->vs, ax25->vr, ax25->va, @@ -1799,7 +1899,7 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length) break; } - sti(); + spin_unlock_bh(&ax25_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); @@ -1814,7 +1914,7 @@ static struct net_proto_family ax25_family_ops = { .create = ax25_create, }; -static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { +static struct proto_ops ax25_proto_ops = { .family = PF_AX25, .release = ax25_release, @@ -1835,15 +1935,14 @@ static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { .sendpage = sock_no_sendpage, }; -#include <linux/smp_lock.h> -SOCKOPS_WRAP(ax25_proto, PF_AX25); - /* * 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, + .type = __constant_htons(ETH_P_AX25), + .dev = NULL, /* All devices */ + .func = ax25_kiss_rcv, + .data = (void *) 1 }; static struct notifier_block ax25_dev_notifier = { diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c index f96c65586c04..f4fa6dfb846e 100644 --- a/net/ax25/ax25_addr.c +++ b/net/ax25/ax25_addr.c @@ -1,24 +1,11 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -165,14 +152,14 @@ unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, a if (flags != NULL) { *flags = 0; - + if (buf[6] & AX25_CBIT) *flags = AX25_COMMAND; if (buf[13] & AX25_CBIT) *flags = AX25_RESPONSE; } - if (dama != NULL) + if (dama != NULL) *dama = ~buf[13] & AX25_DAMA_FLAG; /* Copy to, from */ @@ -243,7 +230,7 @@ int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, a if (d == NULL || d->ndigi == 0) { buf[6] |= AX25_EBIT; return 2 * AX25_ADDR_LEN; - } + } buf += AX25_ADDR_LEN; len += AX25_ADDR_LEN; @@ -277,7 +264,7 @@ int ax25_addr_size(ax25_digi *dp) 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) diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c index efeec64e03ec..e26110d6c0ca 100644 --- a/net/ax25/ax25_dev.c +++ b/net/ax25/ax25_dev.c @@ -1,21 +1,11 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> @@ -27,6 +17,7 @@ #include <linux/string.h> #include <linux/sockios.h> #include <linux/net.h> +#include <linux/spinlock.h> #include <net/ax25.h> #include <linux/inet.h> #include <linux/netdevice.h> @@ -41,27 +32,35 @@ #include <linux/init.h> ax25_dev *ax25_dev_list; +spinlock_t ax25_dev_lock = SPIN_LOCK_UNLOCKED; ax25_dev *ax25_dev_ax25dev(struct net_device *dev) { - ax25_dev *ax25_dev; + ax25_dev *ax25_dev, *res = NULL; + spin_lock_bh(&ax25_dev_lock); for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) - if (ax25_dev->dev == dev) - return ax25_dev; + if (ax25_dev->dev == dev) { + res = ax25_dev; + break; + } + spin_unlock_bh(&ax25_dev_lock); - return NULL; + return res; } ax25_dev *ax25_addr_ax25dev(ax25_address *addr) { - ax25_dev *ax25_dev; + ax25_dev *ax25_dev, *res = NULL; + spin_lock_bh(&ax25_dev_lock); 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; + if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0) { + res = ax25_dev; + } + spin_unlock_bh(&ax25_dev_lock); - return NULL; + return res; } /* @@ -71,7 +70,6 @@ ax25_dev *ax25_addr_ax25dev(ax25_address *addr) 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"); @@ -100,10 +98,10 @@ void ax25_dev_device_up(struct net_device *dev) ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL; ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT; - save_flags(flags); cli(); + spin_lock_bh(&ax25_dev_lock); ax25_dev->next = ax25_dev_list; ax25_dev_list = ax25_dev; - restore_flags(flags); + spin_unlock_bh(&ax25_dev_lock); ax25_register_sysctl(); } @@ -111,14 +109,13 @@ void ax25_dev_device_up(struct net_device *dev) 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(); + spin_lock_bh(&ax25_dev_lock); #ifdef CONFIG_AX25_DAMA_SLAVE ax25_ds_del_timer(ax25_dev); @@ -133,7 +130,7 @@ void ax25_dev_device_down(struct net_device *dev) if ((s = ax25_dev_list) == ax25_dev) { ax25_dev_list = s->next; - restore_flags(flags); + spin_unlock_bh(&ax25_dev_lock); kfree(ax25_dev); ax25_register_sysctl(); return; @@ -142,7 +139,7 @@ void ax25_dev_device_down(struct net_device *dev) while (s != NULL && s->next != NULL) { if (s->next == ax25_dev) { s->next = ax25_dev->next; - restore_flags(flags); + spin_unlock_bh(&ax25_dev_lock); kfree(ax25_dev); ax25_register_sysctl(); return; @@ -150,8 +147,8 @@ void ax25_dev_device_down(struct net_device *dev) s = s->next; } + spin_unlock_bh(&ax25_dev_lock); - restore_flags(flags); ax25_register_sysctl(); } @@ -163,22 +160,22 @@ int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) 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; + 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; - default: + case SIOCAX25DELFWD: + if (ax25_dev->forward == NULL) return -EINVAL; + ax25_dev->forward = NULL; + break; + + default: + return -EINVAL; } return 0; @@ -202,12 +199,16 @@ struct net_device *ax25_fwd_dev(struct net_device *dev) */ void __exit ax25_dev_free(void) { - ax25_dev *s, *ax25_dev = ax25_dev_list; + ax25_dev *s, *ax25_dev; + spin_lock_bh(&ax25_dev_lock); + ax25_dev = ax25_dev_list; while (ax25_dev != NULL) { s = ax25_dev; ax25_dev = ax25_dev->next; kfree(s); } + ax25_dev_list = NULL; + spin_unlock_bh(&ax25_dev_lock); } diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c index 2ae715ec3911..387e1a63bc7d 100644 --- a/net/ax25/ax25_ds_in.c +++ b/net/ax25/ax25_ds_in.c @@ -1,27 +1,12 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -53,54 +38,56 @@ 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; + 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; @@ -114,38 +101,38 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet 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); + 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_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; + } + 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; + default: + break; } return 0; @@ -161,127 +148,127 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet 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_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_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; - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; + if (ax25_validate_nr(ax25, nr)) { + if (ax25->va != nr) + ax25->n2count=0; - 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); - 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; - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } + case AX25_I: + if (!ax25_validate_nr(ax25, nr)) { + 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); + } + 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_check_iframes_acked(ax25, nr)) - ax25->n2count = 0; + if (!(ax25->condition & AX25_COND_ACK_PENDING)) { + ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); + } } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + } else { + if (ax25->condition & AX25_COND_REJECT) { 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; - } + ax25->condition |= AX25_COND_REJECT; + ax25_ds_enquiry_response(ax25); + ax25->condition &= ~AX25_COND_ACK_PENDING; } - break; + } + break; - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_ds_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_ds_establish_data_link(ax25); + ax25->state = AX25_STATE_1; + break; - default: - break; + default: + break; } return queued; @@ -297,15 +284,15 @@ int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) 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; + 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 index e3e88d7710af..d42b8f22823f 100644 --- a/net/ax25/ax25_ds_subr.c +++ b/net/ax25/ax25_ds_subr.c @@ -1,27 +1,12 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -31,6 +16,7 @@ #include <linux/timer.h> #include <linux/string.h> #include <linux/sockios.h> +#include <linux/spinlock.h> #include <linux/net.h> #include <net/ax25.h> #include <linux/inet.h> @@ -73,7 +59,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) * 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 + * 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? */ @@ -93,6 +79,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) ax25_start_t3timer(ax25); ax25_ds_set_timer(ax25->ax25_dev); + spin_lock_bh(&ax25_list_lock); for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { if (ax25o == ax25) continue; @@ -118,6 +105,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) if (ax25o->state != AX25_STATE_0) ax25_start_t3timer(ax25o); } + spin_unlock_bh(&ax25_list_lock); } void ax25_ds_establish_data_link(ax25_cb *ax25) @@ -132,8 +120,8 @@ void ax25_ds_establish_data_link(ax25_cb *ax25) /* * :::FIXME::: - * This is a kludge. Not all drivers recognize kiss commands. - * We need a driver level request to switch duplex mode, that does + * 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. */ @@ -164,19 +152,24 @@ static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char p * 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 + * 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; + int res = 0; + spin_lock_bh(&ax25_list_lock); 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; + if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) { + res = 1; + break; + } + spin_unlock_bh(&ax25_list_lock); - return 0; + return res; } void ax25_dev_dama_on(ax25_dev *ax25_dev) diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c index 02c189e4cb11..ca0309376293 100644 --- a/net/ax25/ax25_ds_timer.c +++ b/net/ax25/ax25_ds_timer.c @@ -1,23 +1,16 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> +#include <linux/spinlock.h> #include <linux/in.h> #include <linux/kernel.h> #include <linux/jiffies.h> @@ -41,7 +34,7 @@ static void ax25_ds_timeout(unsigned long); /* * Add DAMA slave timeout timer to timer list. - * Unlike the connection based timers the timeout function gets + * 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. @@ -58,7 +51,8 @@ static void ax25_ds_add_timer(ax25_dev *ax25_dev) void ax25_ds_del_timer(ax25_dev *ax25_dev) { - if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer); + if (ax25_dev) + del_timer(&ax25_dev->dama.slave_timer); } void ax25_ds_set_timer(ax25_dev *ax25_dev) @@ -80,15 +74,16 @@ 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; } - + + spin_lock_bh(&ax25_list_lock); for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) { if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) continue; @@ -96,7 +91,8 @@ static void ax25_ds_timeout(unsigned long arg) ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); ax25_disconnect(ax25, ETIMEDOUT); } - + spin_unlock_bh(&ax25_list_lock); + ax25_dev_dama_off(ax25_dev); } @@ -104,33 +100,33 @@ 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; + 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; - - 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; + } + break; } ax25_start_heartbeat(ax25); } - + /* dl1bke 960114: T3 works much like the IDLE timeout, but * gets reloaded with every frame for this * connection. @@ -168,7 +164,7 @@ void ax25_ds_idletimer_expiry(ax25_cb *ax25) } /* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC - * within the poll of any connected channel. Remember + * within the poll of any connected channel. Remember * that we are not allowed to send anything unless we * get polled by the Master. * @@ -176,48 +172,47 @@ void ax25_ds_idletimer_expiry(ax25_cb *ax25) * 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); + case AX25_STATE_1: + if (ax25->n2count == ax25->n2) { + if (ax25->modulus == AX25_MODULUS) { ax25_disconnect(ax25, ETIMEDOUT); return; } else { - ax25->n2count++; + 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); } - break; + } 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); diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c index 15ca9c45304e..403624e592ff 100644 --- a/net/ax25/ax25_iface.c +++ b/net/ax25/ax25_iface.c @@ -1,18 +1,11 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> @@ -20,6 +13,7 @@ #include <linux/in.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <linux/timer.h> #include <linux/string.h> #include <linux/sockios.h> @@ -40,22 +34,25 @@ static struct protocol_struct { unsigned int pid; int (*func)(struct sk_buff *, ax25_cb *); } *protocol_list; +static rwlock_t protocol_list_lock = RW_LOCK_UNLOCKED; static struct linkfail_struct { struct linkfail_struct *next; void (*func)(ax25_cb *, int); } *linkfail_list; +static spinlock_t linkfail_lock = SPIN_LOCK_UNLOCKED; static struct listen_struct { struct listen_struct *next; ax25_address callsign; struct net_device *dev; } *listen_list; +static spinlock_t listen_lock = SPIN_LOCK_UNLOCKED; -int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *)) +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; @@ -69,31 +66,28 @@ int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_ protocol->pid = pid; protocol->func = func; - save_flags(flags); - cli(); - + write_lock(&protocol_list_lock); protocol->next = protocol_list; protocol_list = protocol; - - restore_flags(flags); + write_unlock(&protocol_list_lock); return 1; } void ax25_protocol_release(unsigned int pid) { - struct protocol_struct *s, *protocol = protocol_list; - unsigned long flags; + struct protocol_struct *s, *protocol; - if (protocol == NULL) + write_lock(&protocol_list_lock); + protocol = protocol_list; + if (protocol == NULL) { + write_unlock(&protocol_list_lock); return; - - save_flags(flags); - cli(); + } if (protocol->pid == pid) { protocol_list = protocol->next; - restore_flags(flags); + write_unlock(&protocol_list_lock); kfree(protocol); return; } @@ -102,52 +96,45 @@ void ax25_protocol_release(unsigned int pid) if (protocol->next->pid == pid) { s = protocol->next; protocol->next = protocol->next->next; - restore_flags(flags); + write_unlock(&protocol_list_lock); kfree(s); return; } protocol = protocol->next; } - - restore_flags(flags); + write_unlock(&protocol_list_lock); } 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(); - + spin_lock_bh(&linkfail_lock); linkfail->next = linkfail_list; linkfail_list = linkfail; - - restore_flags(flags); + spin_unlock_bh(&linkfail_lock); return 1; } void ax25_linkfail_release(void (*func)(ax25_cb *, int)) { - struct linkfail_struct *s, *linkfail = linkfail_list; - unsigned long flags; + struct linkfail_struct *s, *linkfail; + spin_lock_bh(&linkfail_lock); + linkfail = linkfail_list; if (linkfail == NULL) return; - save_flags(flags); - cli(); - if (linkfail->func == func) { linkfail_list = linkfail->next; - restore_flags(flags); + spin_unlock_bh(&linkfail_lock); kfree(linkfail); return; } @@ -156,21 +143,19 @@ void ax25_linkfail_release(void (*func)(ax25_cb *, int)) if (linkfail->next->func == func) { s = linkfail->next; linkfail->next = linkfail->next->next; - restore_flags(flags); + spin_unlock_bh(&linkfail_lock); kfree(s); return; } linkfail = linkfail->next; } - - restore_flags(flags); + spin_unlock_bh(&linkfail_lock); } 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; @@ -181,31 +166,26 @@ int ax25_listen_register(ax25_address *callsign, struct net_device *dev) listen->callsign = *callsign; listen->dev = dev; - save_flags(flags); - cli(); - + spin_lock_bh(&listen_lock); listen->next = listen_list; listen_list = listen; - - restore_flags(flags); + spin_unlock_bh(&listen_lock); return 1; } void ax25_listen_release(ax25_address *callsign, struct net_device *dev) { - struct listen_struct *s, *listen = listen_list; - unsigned long flags; + struct listen_struct *s, *listen; + spin_lock_bh(&listen_lock); + listen = listen_list; if (listen == NULL) return; - save_flags(flags); - cli(); - if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { listen_list = listen->next; - restore_flags(flags); + spin_unlock_bh(&listen_lock); kfree(listen); return; } @@ -214,35 +194,41 @@ void ax25_listen_release(ax25_address *callsign, struct net_device *dev) if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) { s = listen->next; listen->next = listen->next->next; - restore_flags(flags); + spin_unlock_bh(&listen_lock); kfree(s); return; } listen = listen->next; } - - restore_flags(flags); + spin_unlock_bh(&listen_lock); } int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) { + int (*res)(struct sk_buff *, ax25_cb *) = NULL; struct protocol_struct *protocol; + read_lock(&protocol_list_lock); for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) - return protocol->func; + if (protocol->pid == pid) { + res = protocol->func; + break; + } + read_unlock(&protocol_list_lock); - return NULL; + return res; } int ax25_listen_mine(ax25_address *callsign, struct net_device *dev) { struct listen_struct *listen; + spin_lock_bh(&listen_lock); for (listen = listen_list; listen != NULL; listen = listen->next) if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) return 1; + spin_unlock_bh(&listen_lock); return 0; } @@ -251,18 +237,24 @@ void ax25_link_failed(ax25_cb *ax25, int reason) { struct linkfail_struct *linkfail; + spin_lock_bh(&linkfail_lock); for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) (linkfail->func)(ax25, reason); + spin_unlock_bh(&linkfail_lock); } int ax25_protocol_is_registered(unsigned int pid) { struct protocol_struct *protocol; + int res = 0; + read_lock(&protocol_list_lock); for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) - return 1; + if (protocol->pid == pid) { + res = 1; + break; + } + read_unlock(&protocol_list_lock); - return 0; + return res; } - diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index 2abbd886c26f..a886b03e9c2f 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -1,44 +1,14 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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) 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. + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) + * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) */ - #include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> @@ -146,7 +116,7 @@ int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) #ifdef CONFIG_INET if (pid == AX25_P_IP) { - /* working around a TCP bug to keep additional listeners + /* working around a TCP bug to keep additional listeners * happy. TCP re-uses the buffer and destroys the original * content. */ @@ -199,37 +169,33 @@ static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, i return 0; 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; + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + queued = ax25_std_frame_in(ax25, skb, type); + break; #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; + 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 } return queued; } -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, + ax25_address *dev_addr, struct packet_type *ptype) { - struct sock *make; - struct sock *sk; - int type = 0; + ax25_address src, dest, *next_digi = NULL; + int type = 0, mine = 0, dama; + struct sock *make, *sk, *raw; 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; /* * Process the AX.25/LAPB frame. @@ -274,8 +240,10 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d 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) + if ((raw = ax25_addr_match(&dest)) != NULL) { ax25_send_to_raw(raw, skb, skb->data[1]); + release_sock(raw); + } if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) { kfree_skb(skb); @@ -285,47 +253,49 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d /* 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; - - 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; + 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; + + 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 { + case AX25_P_TEXT: + /* Now find a suitable dgram socket */ + sk = ax25_get_socket(&dest, &src, SOCK_DGRAM); + if (sk != 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); } - break; + release_sock(sk); + } else { + kfree_skb(skb); + } + break; - default: - kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ - break; + default: + kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ + break; } return 0; @@ -349,9 +319,10 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d 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 + * 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); @@ -363,7 +334,8 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d /* a) received not a SABM(E) */ - if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) { + 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. @@ -383,9 +355,12 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET); 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); + 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; } @@ -486,4 +461,3 @@ int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype); } - diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index 80fe26de8181..6ea092dedab0 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -1,18 +1,11 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> @@ -88,16 +81,16 @@ int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short /* 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%2.2x\n", type); - *buff++ = 0; - break; + 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%2.2x\n", type); + *buff++ = 0; + break; } if (daddr != NULL) @@ -112,8 +105,8 @@ int ax25_rebuild_header(struct sk_buff *skb) unsigned char *bp = skb->data; struct net_device *dev; ax25_address *src, *dst; - ax25_route *route; ax25_dev *ax25_dev; + ax25_route _route, *route = &_route; dst = (ax25_address *)(bp + 1); src = (ax25_address *)(bp + 8); @@ -121,14 +114,15 @@ int ax25_rebuild_header(struct sk_buff *skb) if (arp_find(bp + 1, skb)) return 1; - route = ax25_rt_find_route(dst, NULL); + route = ax25_rt_find_route(route, dst, NULL); dev = route->dev; if (dev == NULL) dev = skb->dev; - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return 1; + if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { + goto put; + } if (bp[16] == AX25_P_IP) { if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { @@ -153,7 +147,7 @@ int ax25_rebuild_header(struct sk_buff *skb) if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) { kfree_skb(skb); - return 1; + goto put; } if (skb->sk != NULL) @@ -167,10 +161,10 @@ int ax25_rebuild_header(struct sk_buff *skb) skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ ourskb->nh.raw = ourskb->data; - ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c, + ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c, &dst_c, route->digipeat, dev); - return 1; + goto put; } } @@ -187,7 +181,7 @@ int ax25_rebuild_header(struct sk_buff *skb) if (route->digipeat != NULL) { if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { kfree_skb(skb); - return 1; + goto put; } skb = ourskb; @@ -197,6 +191,9 @@ int ax25_rebuild_header(struct sk_buff *skb) ax25_queue_xmit(skb); +put: + ax25_put_route(route); + return 1; } diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index 231c170e7a0b..e6d25e22d3ac 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -1,39 +1,13 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 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) + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) */ - #include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> @@ -44,6 +18,7 @@ #include <linux/timer.h> #include <linux/string.h> #include <linux/sockios.h> +#include <linux/spinlock.h> #include <linux/net.h> #include <net/ax25.h> #include <linux/inet.h> @@ -57,6 +32,8 @@ #include <linux/mm.h> #include <linux/interrupt.h> +static spinlock_t ax25_frag_lock = SPIN_LOCK_UNLOCKED; + ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev) { ax25_dev *ax25_dev; @@ -101,18 +78,18 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax2 } 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; + 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; + 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 } @@ -138,7 +115,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,20 +131,18 @@ 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(); - + spin_lock_bh(&ax25_frag_lock); if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { - restore_flags(flags); + spin_unlock_bh(&ax25_frag_lock); printk(KERN_CRIT "AX.25: ax25_output - out of memory\n"); return; } if (skb->sk != NULL) skb_set_owner_w(skbn, skb->sk); - - restore_flags(flags); - + + spin_unlock_bh(&ax25_frag_lock); + len = (paclen > skb->len) ? skb->len : paclen; if (ka9qfrag == 1) { @@ -202,24 +176,24 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) } switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_kick(ax25); - break; + 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; + /* + * 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. */ @@ -305,15 +279,15 @@ void ax25_kick(ax25_cb *ax25) * 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; + 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; + case AX25_PROTO_DAMA_SLAVE: + ax25_send_iframe(ax25, skbn, AX25_POLLOFF); + break; #endif } diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 254ff36fb669..21a05044cfe0 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -1,49 +1,20 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 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/ + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) + * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) + * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> +#include <linux/timer.h> #include <linux/in.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -56,6 +27,7 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/skbuff.h> +#include <linux/spinlock.h> #include <net/sock.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -65,8 +37,9 @@ #include <linux/init.h> static ax25_route *ax25_route_list; +static rwlock_t ax25_route_lock = RW_LOCK_UNLOCKED; -static ax25_route *ax25_find_route(ax25_address *, struct net_device *); +static ax25_route *ax25_get_route(ax25_address *, struct net_device *); /* * small macro to drop non-digipeated digipeaters and reverse path @@ -86,8 +59,10 @@ static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out) void ax25_rt_device_down(struct net_device *dev) { - ax25_route *s, *t, *ax25_rt = ax25_route_list; - + ax25_route *s, *t, *ax25_rt; + + write_lock(&ax25_route_lock); + ax25_rt = ax25_route_list; while (ax25_rt != NULL) { s = ax25_rt; ax25_rt = ax25_rt->next; @@ -111,129 +86,196 @@ void ax25_rt_device_down(struct net_device *dev) } } } + write_unlock(&ax25_route_lock); } -int ax25_rt_ioctl(unsigned int cmd, void *arg) +static int ax25_rt_add(struct ax25_routes_struct *route) { - unsigned long flags; - ax25_route *s, *t, *ax25_rt; - struct ax25_routes_struct route; - struct ax25_route_opt_struct rt_option; + ax25_route *ax25_rt; ax25_dev *ax25_dev; 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_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL) + return -EINVAL; + if (route->digi_count > AX25_MAX_DIGIS) + return -EINVAL; + + write_lock(&ax25_route_lock); + + ax25_rt = ax25_route_list; + while (ax25_rt != NULL) { + 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 ((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 (route->digi_count != 0) { if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree(ax25_rt); + write_unlock(&ax25_route_lock); 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->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]; + 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); - break; + return 0; + } + ax25_rt = ax25_rt->next; + } - 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; - } - } + if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) { + write_unlock(&ax25_route_lock); + return -ENOMEM; + } + + atomic_set(&ax25_rt->ref, 0); + 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) { + write_unlock(&ax25_route_lock); + 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]; + } + } + ax25_rt->next = ax25_route_list; + ax25_route_list = ax25_rt; + write_unlock(&ax25_route_lock); + + return 0; +} + +static int ax25_rt_destroy(ax25_route *ax25_rt) +{ + if (atomic_read(&ax25_rt->ref) == 0) { + if (ax25_rt->digipeat != NULL) + kfree(ax25_rt->digipeat); + kfree(ax25_rt); + } + + /* + * Uh... Route is still in use; we can't yet destroy it. Retry later. + */ + init_timer(&ax25_rt->timer); + ax25_rt->timer.data = (unsigned long) ax25_rt; + ax25_rt->timer.function = (void *) ax25_rt_destroy; + ax25_rt->timer.expires = jiffies + 5 * HZ; + + add_timer(&ax25_rt->timer); +} + +static int ax25_rt_del(struct ax25_routes_struct *route) +{ + ax25_route *s, *t, *ax25_rt; + ax25_dev *ax25_dev; + + if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL) + return -EINVAL; + + write_lock(&ax25_route_lock); + + 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; + ax25_rt_destroy(s); + } else { + for (t = ax25_route_list; t != NULL; t = t->next) { + if (t->next == s) { + t->next = s->next; + ax25_rt_destroy(s); + break; } } } - break; + } + } + write_unlock(&ax25_route_lock); - 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; - } + return 0; +} + +static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option) +{ + ax25_route *ax25_rt; + ax25_dev *ax25_dev; + int err = 0; + + if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL) + return -EINVAL; + + write_lock(&ax25_route_lock); + + ax25_rt = ax25_route_list; + while (ax25_rt != NULL) { + 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: + err = -EINVAL; + goto out; } + break; + default: + err = -EINVAL; + goto out; } - break; - - default: - return -EINVAL; + } + ax25_rt = ax25_rt->next; } - return 0; +out: + write_unlock(&ax25_route_lock); + return err; +} + +int ax25_rt_ioctl(unsigned int cmd, void *arg) +{ + struct ax25_route_opt_struct rt_option; + struct ax25_routes_struct route; + + switch (cmd) { + case SIOCADDRT: + if (copy_from_user(&route, arg, sizeof(route))) + return -EFAULT; + return ax25_rt_add(&route); + + case SIOCDELRT: + if (copy_from_user(&route, arg, sizeof(route))) + return -EFAULT; + return ax25_rt_del(&route); + + case SIOCAX25OPTRT: + if (copy_from_user(&rt_option, arg, sizeof(rt_option))) + return -EFAULT; + return ax25_rt_opt(&rt_option); + + default: + return -EINVAL; + } } int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) @@ -244,8 +286,8 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) off_t begin = 0; char *callsign; int i; - - cli(); + + read_lock(&ax25_route_lock); len += sprintf(buffer, "callsign dev mode digipeaters\n"); @@ -259,15 +301,15 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) 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; + default: + len += sprintf(buffer + len, " *"); + break; } if (ax25_rt->digipeat != NULL) @@ -286,26 +328,29 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) if (pos > offset + length) break; } - - sti(); + read_unlock(&ax25_route_lock); *start = buffer + (offset - begin); len -= (offset - begin); - if (len > length) len = length; + if (len > length) + len = length; return len; -} +} /* * Find AX.25 route + * + * Only routes with a refernce rout of zero can be destroyed. */ -static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) +static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) { ax25_route *ax25_spe_rt = NULL; ax25_route *ax25_def_rt = NULL; ax25_route *ax25_rt; + read_lock(&ax25_route_lock); /* * Bind to the physical interface we heard them on, or the default * route if none is found; @@ -324,10 +369,16 @@ static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) } } + ax25_rt = ax25_def_rt; if (ax25_spe_rt != NULL) - return ax25_spe_rt; + ax25_rt = ax25_spe_rt; - return ax25_def_rt; + if (ax25_rt != NULL) + atomic_inc(&ax25_rt->ref); + + read_unlock(&ax25_route_lock); + + return ax25_rt; } /* @@ -346,7 +397,7 @@ static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) digipeat->ndigi = k; } - + /* * Find which interface to use. @@ -355,24 +406,31 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) { ax25_route *ax25_rt; ax25_address *call; + int err; - if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL) + if ((ax25_rt = ax25_get_route(addr, NULL)) == NULL) return -EHOSTUNREACH; - if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) - return -EHOSTUNREACH; + if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) { + err = -EHOSTUNREACH; + goto put; + } if ((call = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) - return -EPERM; + if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { + err = -EPERM; + goto put; + } call = (ax25_address *)ax25->ax25_dev->dev->dev_addr; } ax25->source_addr = *call; if (ax25_rt->digipeat != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return -ENOMEM; + if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { + err = -ENOMEM; + goto put; + } memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi)); ax25_adjust_path(addr, ax25->digipeat); } @@ -380,31 +438,32 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) if (ax25->sk != NULL) ax25->sk->zapped = 0; +put: + ax25_put_route(ax25_rt); + return 0; } -/* - * dl1bke 960117: build digipeater path - * dl1bke 960301: use the default route if it exists - */ -ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev) +ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr, + struct net_device *dev) { - 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; - } + if ((ax25_rt = ax25_get_route(addr, dev))) + return ax25_rt; - return ax25_rt; + route->next = NULL; + atomic_set(&route->ref, 1); + route->callsign = *addr; + route->dev = dev; + route->digipeat = NULL; + route->ip_mode = ' '; + + return route; } -struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) +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; @@ -440,6 +499,7 @@ void __exit ax25_rt_free(void) { ax25_route *s, *ax25_rt = ax25_route_list; + write_unlock(&ax25_route_lock); while (ax25_rt != NULL) { s = ax25_rt; ax25_rt = ax25_rt->next; @@ -449,4 +509,5 @@ void __exit ax25_rt_free(void) kfree(s); } + write_unlock(&ax25_route_lock); } diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c index b68155f38a45..70470dc75ab0 100644 --- a/net/ax25/ax25_std_in.c +++ b/net/ax25/ax25_std_in.c @@ -1,42 +1,19 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) + * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) * - * 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. + * 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. */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -68,55 +45,55 @@ 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); - } + 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; + } + 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]; - } + 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; + } + break; - default: - break; + default: + break; } return 0; @@ -130,30 +107,31 @@ static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frame 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); + 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; + 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; + 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; + default: + break; } return 0; @@ -169,116 +147,116 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame 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); + 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_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; + } else { + ax25_std_nr_error_recovery(ax25); + ax25->state = AX25_STATE_1; + } + break; - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); + case AX25_I: + if (!ax25_validate_nr(ax25, nr)) { + ax25_std_nr_error_recovery(ax25); + ax25->state = AX25_STATE_1; 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; - } + } + 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; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) + } + 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); - 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_ACK_PENDING)) { + ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); + } } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + } else { + if (ax25->condition & AX25_COND_REJECT) { 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; - } + ax25->condition |= AX25_COND_REJECT; + ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); + ax25->condition &= ~AX25_COND_ACK_PENDING; } - break; + } + break; - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_std_establish_data_link(ax25); + ax25->state = AX25_STATE_1; + break; - default: - break; + default: + break; } return queued; @@ -294,145 +272,146 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame 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); + 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_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); + 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); + 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); - ax25_requeue_frames(ax25); + 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; + 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); + } } - ax25_frames_acked(ax25, nr); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + } else { + if (ax25->condition & AX25_COND_REJECT) { 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; - } + ax25->condition |= AX25_COND_REJECT; + ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); + ax25->condition &= ~AX25_COND_ACK_PENDING; } - break; + } + break; - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_std_establish_data_link(ax25); + ax25->state = AX25_STATE_1; + break; - default: - break; + default: + break; } return queued; @@ -448,18 +427,18 @@ int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) 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; + 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); diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c index c868e0507952..2b3c801ae486 100644 --- a/net/ax25/ax25_std_subr.c +++ b/net/ax25/ax25_std_subr.c @@ -1,25 +1,11 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -80,7 +66,7 @@ void ax25_std_transmit_enquiry(ax25_cb *ax25) ax25_calculate_t1(ax25); ax25_start_t1timer(ax25); } - + void ax25_std_enquiry_response(ax25_cb *ax25) { if (ax25->condition & AX25_COND_OWN_RX_BUSY) diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c index 741e08d231cc..957f5e5fec81 100644 --- a/net/ax25/ax25_std_timer.c +++ b/net/ax25/ax25_std_timer.c @@ -1,27 +1,14 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) + * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -47,30 +34,29 @@ 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; - } + 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); @@ -117,53 +103,53 @@ void ax25_std_idletimer_expiry(ax25_cb *ax25) 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); + case AX25_STATE_1: + if (ax25->n2count == ax25->n2) { + if (ax25->modulus == AX25_MODULUS) { ax25_disconnect(ax25, ETIMEDOUT); return; } else { - ax25->n2count++; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + 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); } - break; - - case AX25_STATE_3: - ax25->n2count = 1; + } 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); - 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; + } + break; } ax25_calculate_t1(ax25); diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index c49892720fab..54db70c15b4a 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -1,38 +1,14 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 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. + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) + * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -172,9 +148,9 @@ int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf) return frametype; } -/* +/* * This routine is called when the HDLC layer internally generates a - * command or response for the remote machine ( eg. RR, UA etc. ). + * 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) @@ -231,7 +207,7 @@ void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *des 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); @@ -257,18 +233,18 @@ void ax25_calculate_t1(ax25_cb *ax25) int n, t = 2; switch (ax25->backoff) { - case 0: - break; - - case 1: - t += 2 * ax25->n2count; - break; - - case 2: - for (n = 0; n < ax25->n2count; n++) - t *= 2; - if (t > 8) t = 8; - break; + case 0: + break; + + case 1: + t += 2 * ax25->n2count; + break; + + case 2: + for (n = 0; n < ax25->n2count; n++) + t *= 2; + if (t > 8) t = 8; + break; } ax25->t1 = t * ax25->rtt; diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index 8ea87b01df41..7ef07596932f 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -1,30 +1,17 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 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). + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi) + * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk) + * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) + * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) + * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) */ - #include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> @@ -152,105 +139,121 @@ unsigned long ax25_display_timer(struct timer_list *timer) static void ax25_heartbeat_expiry(unsigned long param) { - ax25_cb *ax25 = (ax25_cb *)param; int proto = AX25_PROTO_STD_SIMPLEX; + ax25_cb *ax25 = (ax25_cb *)param; + struct sock *sk = ax25->sk; if (ax25->ax25_dev) proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]; + bh_lock_sock(sk); + switch (proto) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_heartbeat_expiry(ax25); - break; + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_heartbeat_expiry(ax25); + break; #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; + case AX25_PROTO_DAMA_SLAVE: + if (ax25->ax25_dev->dama.slave) + ax25_ds_heartbeat_expiry(ax25); + else + ax25_std_heartbeat_expiry(ax25); + break; #endif } + bh_unlock_sock(sk); } static void ax25_t1timer_expiry(unsigned long param) { ax25_cb *ax25 = (ax25_cb *)param; + struct sock *sk = ax25->sk; + bh_lock_sock(sk); switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t1timer_expiry(ax25); - break; + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t1timer_expiry(ax25); + break; #ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t1timer_expiry(ax25); - break; + case AX25_PROTO_DAMA_SLAVE: + if (!ax25->ax25_dev->dama.slave) + ax25_std_t1timer_expiry(ax25); + break; #endif } + bh_unlock_sock(sk); } static void ax25_t2timer_expiry(unsigned long param) { ax25_cb *ax25 = (ax25_cb *)param; + struct sock *sk = ax25->sk; + bh_lock_sock(sk); switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t2timer_expiry(ax25); - break; + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t2timer_expiry(ax25); + break; #ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t2timer_expiry(ax25); - break; + case AX25_PROTO_DAMA_SLAVE: + if (!ax25->ax25_dev->dama.slave) + ax25_std_t2timer_expiry(ax25); + break; #endif } + bh_unlock_sock(sk); } static void ax25_t3timer_expiry(unsigned long param) { ax25_cb *ax25 = (ax25_cb *)param; + struct sock *sk = ax25->sk; + bh_lock_sock(sk); switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t3timer_expiry(ax25); - break; + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t3timer_expiry(ax25); + break; #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; + case AX25_PROTO_DAMA_SLAVE: + if (ax25->ax25_dev->dama.slave) + ax25_ds_t3timer_expiry(ax25); + else + ax25_std_t3timer_expiry(ax25); + break; #endif } + bh_unlock_sock(sk); } static void ax25_idletimer_expiry(unsigned long param) { ax25_cb *ax25 = (ax25_cb *)param; + struct sock *sk = ax25->sk; + bh_lock_sock(sk); switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_idletimer_expiry(ax25); - break; + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_idletimer_expiry(ax25); + break; #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; + case AX25_PROTO_DAMA_SLAVE: + if (ax25->ax25_dev->dama.slave) + ax25_ds_idletimer_expiry(ax25); + else + ax25_std_idletimer_expiry(ax25); + break; #endif } + bh_unlock_sock(sk); } diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c index 603d8b8cc864..c5b473dc711b 100644 --- a/net/ax25/ax25_uid.c +++ b/net/ax25/ax25_uid.c @@ -1,18 +1,11 @@ /* - * AX.25 release 037 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -23,6 +16,7 @@ #include <linux/string.h> #include <linux/sockios.h> #include <linux/net.h> +#include <linux/spinlock.h> #include <net/ax25.h> #include <linux/inet.h> #include <linux/netdevice.h> @@ -47,17 +41,23 @@ */ static ax25_uid_assoc *ax25_uid_list; +static rwlock_t ax25_uid_lock = RW_LOCK_UNLOCKED; int ax25_uid_policy = 0; ax25_address *ax25_findbyuid(uid_t uid) { ax25_uid_assoc *ax25_uid; + ax25_address *res = NULL; + read_lock(&ax25_uid_lock); for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { - if (ax25_uid->uid == uid) - return &ax25_uid->call; + if (ax25_uid->uid == uid) { + res = &ax25_uid->call; + break; + } } + read_unlock(&ax25_uid_lock); return NULL; } @@ -65,63 +65,77 @@ ax25_address *ax25_findbyuid(uid_t uid) int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) { ax25_uid_assoc *s, *ax25_uid; - unsigned long flags; + unsigned long res; 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; + case SIOCAX25GETUID: + res = -ENOENT; + read_lock(&ax25_uid_lock); + for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { + if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) { + res = ax25_uid->uid; + break; } - return -ENOENT; + } + read_unlock(&ax25_uid_lock); - case SIOCAX25ADDUID: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (ax25_findbyuid(sax->sax25_uid)) - return -EEXIST; - if (sax->sax25_uid == 0) - return -EINVAL; - if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == 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); - return 0; + return res; + + case SIOCAX25ADDUID: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (ax25_findbyuid(sax->sax25_uid)) + return -EEXIST; + if (sax->sax25_uid == 0) + return -EINVAL; + if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL) + return -ENOMEM; + + ax25_uid->uid = sax->sax25_uid; + ax25_uid->call = sax->sax25_call; + + write_lock(&ax25_uid_lock); + ax25_uid->next = ax25_uid_list; + ax25_uid_list = ax25_uid; + write_unlock(&ax25_uid_lock); + + return 0; + + case SIOCAX25DELUID: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; - case SIOCAX25DELUID: - 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; + write_lock(&ax25_uid_lock); + 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); + } + if (ax25_uid == NULL) { + write_unlock(&ax25_uid_lock); + return -ENOENT; + } + if ((s = ax25_uid_list) == ax25_uid) { + ax25_uid_list = s->next; + write_unlock(&ax25_uid_lock); + kfree(ax25_uid); + return 0; + } + while (s != NULL && s->next != NULL) { + if (s->next == ax25_uid) { + s->next = ax25_uid->next; + write_unlock(&ax25_uid_lock); 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); - return 0; - } - s = s->next; - } - restore_flags(flags); - return -ENOENT; + s = s->next; + } + write_unlock(&ax25_uid_lock); - default: - return -EINVAL; + return -ENOENT; + + default: + return -EINVAL; } return -EINVAL; /*NOTREACHED */ @@ -134,8 +148,7 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) off_t pos = 0; off_t begin = 0; - cli(); - + read_lock(&ax25_uid_lock); len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy); for (pt = ax25_uid_list; pt != NULL; pt = pt->next) { @@ -151,13 +164,13 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) if (pos > offset + length) break; } - - sti(); + read_unlock(&ax25_uid_lock); *start = buffer + (offset - begin); len -= offset - begin; - if (len > length) len = length; + if (len > length) + len = length; return len; } @@ -167,12 +180,16 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) */ void __exit ax25_uid_free(void) { - ax25_uid_assoc *s, *ax25_uid = ax25_uid_list; + ax25_uid_assoc *s, *ax25_uid; + write_lock(&ax25_uid_lock); + ax25_uid = ax25_uid_list; while (ax25_uid != NULL) { s = ax25_uid; ax25_uid = ax25_uid->next; kfree(s); } + ax25_uid_list = NULL; + write_unlock(&ax25_uid_lock); } diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c index e6016f93de43..09641dc6580a 100644 --- a/net/ax25/sysctl_net_ax25.c +++ b/net/ax25/sysctl_net_ax25.c @@ -1,13 +1,15 @@ -/* -*- linux-c -*- - * sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem. +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS] + * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) */ - #include <linux/config.h> #include <linux/mm.h> #include <linux/sysctl.h> +#include <linux/spinlock.h> #include <net/ax25.h> static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1}; @@ -105,6 +107,7 @@ void ax25_register_sysctl(void) ax25_dev *ax25_dev; int n, k; + spin_lock_bh(&ax25_dev_lock); 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); @@ -119,6 +122,7 @@ void ax25_register_sysctl(void) while (n--) kfree(ax25_table[n].child); kfree(ax25_table); + spin_unlock_bh(&ax25_dev_lock); return; } memcpy(child, ax25_param_table, sizeof(ax25_param_table)); @@ -128,7 +132,7 @@ void ax25_register_sysctl(void) ax25_table[n].mode = 0555; #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? @@ -144,6 +148,7 @@ void ax25_register_sysctl(void) n++; } + spin_unlock_bh(&ax25_dev_lock); ax25_dir_table[0].child = ax25_table; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index f1dddc7be67b..055053e507fb 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1,38 +1,13 @@ /* - * NET/ROM release 007 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * NET/ROM 001 Jonathan(G4KLX) Cloned from the AX25 code. - * NET/ROM 002 Darryl(G7LED) Fixes and address enhancement. - * Jonathan(G4KLX) Complete bind re-think. - * Alan(GW4PTS) Trivial tweaks into new format. - * NET/ROM 003 Jonathan(G4KLX) Added G8BPQ extensions. - * Added NET/ROM routing ioctl. - * Darryl(G7LED) Fix autobinding (on connect). - * Fixed nr_release(), set TCP_CLOSE, wakeup app - * context, THEN make the sock dead. - * Circuit ID check before allocating it on - * a connection. - * Alan(GW4PTS) sendmsg/recvmsg only. Fixed connect clear bug - * inherited from AX.25 - * NET/ROM 004 Jonathan(G4KLX) Converted to module. - * NET/ROM 005 Jonathan(G4KLX) Linux 2.1 - * Alan(GW4PTS) Started POSIXisms - * NET/ROM 006 Alan(GW4PTS) Brought in line with the ANK changes - * Jonathan(G4KLX) Removed hdrincl. - * NET/ROM 007 Jonathan(G4KLX) New timer architecture. - * Implemented Idle timer. - * Arnaldo C. Melo s/suser/capable/, micro cleanups + * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) */ - #include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> @@ -82,7 +57,8 @@ int sysctl_netrom_link_fails_count = NR_DEFAULT_FAILS; static unsigned short circuit = 0x101; -static struct sock *volatile nr_list; +static struct sock *nr_list; +static spinlock_t nr_list_lock; static struct proto_ops nr_proto_ops; @@ -123,27 +99,26 @@ decmod: MOD_DEC_USE_COUNT; static void nr_remove_socket(struct sock *sk) { struct sock *s; - unsigned long flags; - save_flags(flags); cli(); + spin_lock_bh(&nr_list_lock); if ((s = nr_list) == sk) { nr_list = s->next; - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); return; } while (s != NULL && s->next != NULL) { if (s->next == sk) { s->next = sk->next; - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); return; } s = s->next; } - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); } /* @@ -153,10 +128,12 @@ static void nr_kill_by_device(struct net_device *dev) { struct sock *s; + spin_lock_bh(&nr_list_lock); for (s = nr_list; s != NULL; s = s->next) { if (nr_sk(s)->device == dev) nr_disconnect(s, ENETUNREACH); } + spin_unlock_bh(&nr_list_lock); } /* @@ -180,14 +157,10 @@ static int nr_device_event(struct notifier_block *this, unsigned long event, voi */ static void nr_insert_socket(struct sock *sk) { - unsigned long flags; - - save_flags(flags); cli(); - + spin_lock_bh(&nr_list_lock); sk->next = nr_list; nr_list = sk; - - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); } /* @@ -196,21 +169,18 @@ static void nr_insert_socket(struct sock *sk) */ static struct sock *nr_find_listener(ax25_address *addr) { - unsigned long flags; struct sock *s; - save_flags(flags); - cli(); - + spin_lock_bh(&nr_list_lock); for (s = nr_list; s != NULL; s = s->next) { if (!ax25cmp(&nr_sk(s)->source_addr, addr) && s->state == TCP_LISTEN) { - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); return s; } } + spin_unlock_bh(&nr_list_lock); - restore_flags(flags); return NULL; } @@ -220,21 +190,17 @@ static struct sock *nr_find_listener(ax25_address *addr) static struct sock *nr_find_socket(unsigned char index, unsigned char id) { struct sock *s; - unsigned long flags; - - save_flags(flags); - cli(); + spin_lock_bh(&nr_list_lock); for (s = nr_list; s != NULL; s = s->next) { nr_cb *nr = nr_sk(s); if (nr->my_index == index && nr->my_id == id) { - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); return s; } } - - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); return NULL; } @@ -242,25 +208,22 @@ static struct sock *nr_find_socket(unsigned char index, unsigned char id) /* * Find a connected NET/ROM socket given their circuit IDs. */ -static struct sock *nr_find_peer(unsigned char index, unsigned char id, ax25_address *dest) +static struct sock *nr_find_peer(unsigned char index, unsigned char id, + ax25_address *dest) { struct sock *s; - unsigned long flags; - - save_flags(flags); - cli(); + spin_lock_bh(&nr_list_lock); for (s = nr_list; s != NULL; s = s->next) { nr_cb *nr = nr_sk(s); if (nr->your_index == index && nr->your_id == id && !ax25cmp(&nr->dest_addr, dest)) { - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); return s; } } - - restore_flags(flags); + spin_unlock_bh(&nr_list_lock); return NULL; } @@ -301,17 +264,16 @@ static void nr_destroy_timer(unsigned long 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. + * 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 nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer */ +void nr_destroy_socket(struct sock *sk) { struct sk_buff *skb; - unsigned long flags; - save_flags(flags); cli(); + nr_remove_socket(sk); nr_stop_heartbeat(sk); nr_stop_t1timer(sk); @@ -319,7 +281,6 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer nr_stop_t4timer(sk); nr_stop_idletimer(sk); - nr_remove_socket(sk); nr_clear_queues(sk); /* Flush the queues */ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { @@ -342,8 +303,6 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer } else { nr_free_sock(sk); } - - restore_flags(flags); } /* @@ -368,38 +327,38 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, return -EFAULT; switch (optname) { - case NETROM_T1: - if (opt < 1) - return -EINVAL; - nr->t1 = opt * HZ; - return 0; - - case NETROM_T2: - if (opt < 1) - return -EINVAL; - nr->t2 = opt * HZ; - return 0; - - case NETROM_N2: - if (opt < 1 || opt > 31) - return -EINVAL; - nr->n2 = opt; - return 0; - - case NETROM_T4: - if (opt < 1) - return -EINVAL; - nr->t4 = opt * HZ; - return 0; - - case NETROM_IDLE: - if (opt < 0) - return -EINVAL; - nr->idle = opt * 60 * HZ; - return 0; - - default: - return -ENOPROTOOPT; + case NETROM_T1: + if (opt < 1) + return -EINVAL; + nr->t1 = opt * HZ; + return 0; + + case NETROM_T2: + if (opt < 1) + return -EINVAL; + nr->t2 = opt * HZ; + return 0; + + case NETROM_N2: + if (opt < 1 || opt > 31) + return -EINVAL; + nr->n2 = opt; + return 0; + + case NETROM_T4: + if (opt < 1) + return -EINVAL; + nr->t4 = opt * HZ; + return 0; + + case NETROM_IDLE: + if (opt < 0) + return -EINVAL; + nr->idle = opt * 60 * HZ; + return 0; + + default: + return -ENOPROTOOPT; } } @@ -421,28 +380,28 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, return -EINVAL; switch (optname) { - case NETROM_T1: - val = nr->t1 / HZ; - break; + case NETROM_T1: + val = nr->t1 / HZ; + break; - case NETROM_T2: - val = nr->t2 / HZ; - break; + case NETROM_T2: + val = nr->t2 / HZ; + break; - case NETROM_N2: - val = nr->n2; - break; + case NETROM_N2: + val = nr->n2; + break; - case NETROM_T4: - val = nr->t4 / HZ; - break; + case NETROM_T4: + val = nr->t4 / HZ; + break; - case NETROM_IDLE: - val = nr->idle / (60 * HZ); - break; + case NETROM_IDLE: + val = nr->idle / (60 * HZ); + break; - default: - return -ENOPROTOOPT; + default: + return -ENOPROTOOPT; } len = min_t(unsigned int, len, sizeof(int)); @@ -567,34 +526,33 @@ static int nr_release(struct socket *sock) nr = nr_sk(sk); switch (nr->state) { - - case NR_STATE_0: - case NR_STATE_1: - case NR_STATE_2: - nr_disconnect(sk, 0); - nr_destroy_socket(sk); - break; - - case NR_STATE_3: - nr_clear_queues(sk); - nr->n2count = 0; - nr_write_internal(sk, NR_DISCREQ); - nr_start_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - nr->state = NR_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; - sk->socket = NULL; - break; - - default: - sk->socket = NULL; - break; + case NR_STATE_0: + case NR_STATE_1: + case NR_STATE_2: + nr_disconnect(sk, 0); + nr_destroy_socket(sk); + break; + + case NR_STATE_3: + nr_clear_queues(sk); + nr->n2count = 0; + nr_write_internal(sk, NR_DISCREQ); + nr_start_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); + nr->state = NR_STATE_2; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; + sk->state_change(sk); + sk->dead = 1; + sk->destroy = 1; + sk->socket = NULL; + break; + + default: + sk->socket = NULL; + break; } sock->sk = NULL; @@ -732,78 +690,98 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) return -EINPROGRESS; - cli(); /* To avoid races on the sleep */ - /* - * A Connect Ack with Choke or timeout or failed routing will go to closed. + * A Connect Ack with Choke or timeout or failed routing will go to + * closed. */ - while (sk->state == TCP_SYN_SENT) { - interruptible_sleep_on(sk->sleep); - if (signal_pending(current)) { - sti(); + if (sk->state == TCP_SYN_SENT) { + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(sk->sleep, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (sk->state != TCP_SYN_SENT) + break; + if (!signal_pending(tsk)) { + schedule(); + continue; + } return -ERESTARTSYS; } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); } if (sk->state != TCP_ESTABLISHED) { - sti(); sock->state = SS_UNCONNECTED; return sock_error(sk); /* Always set at this point */ } sock->state = SS_CONNECTED; - sti(); - return 0; } static int nr_accept(struct socket *sock, struct socket *newsock, int flags) { - struct sock *sk; - struct sock *newsk; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); struct sk_buff *skb; + struct sock *newsk; + struct sock *sk; + int err = 0; if ((sk = sock->sk) == NULL) return -EINVAL; - if (sk->type != SOCK_SEQPACKET) - return -EOPNOTSUPP; + lock_sock(sk); + if (sk->type != SOCK_SEQPACKET) { + err = -EOPNOTSUPP; + goto out; + } - if (sk->state != TCP_LISTEN) - return -EINVAL; + if (sk->state != TCP_LISTEN) { + err = -EINVAL; + goto out; + } /* * The write queue this time is holding sockets ready to use * hooked into the SABM we saved */ - do { - cli(); - if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { - if (flags & O_NONBLOCK) { - sti(); - return -EWOULDBLOCK; - } - interruptible_sleep_on(sk->sleep); - if (signal_pending(current)) { - sti(); - return -ERESTARTSYS; - } + add_wait_queue(sk->sleep, &wait); + for (;;) { + skb = skb_dequeue(&sk->receive_queue); + if (skb) + break; + + current->state = TASK_INTERRUPTIBLE; + release_sock(sk); + if (flags & O_NONBLOCK) + return -EWOULDBLOCK; + if (!signal_pending(tsk)) { + schedule(); + lock_sock(sk); + continue; } - } while (skb == NULL); + return -ERESTARTSYS; + } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); newsk = skb->sk; newsk->pair = NULL; newsk->socket = newsock; newsk->sleep = &newsock->wait; - sti(); /* Now attach up the new socket */ kfree_skb(skb); sk->ack_backlog--; newsock->sk = newsk; - return 0; +out: + return err; } static int nr_getname(struct socket *sock, struct sockaddr *uaddr, @@ -1127,54 +1105,53 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) struct sock *sk = sock->sk; switch (cmd) { - case TIOCOUTQ: { - long amount; - amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); - if (amount < 0) - amount = 0; - return put_user(amount, (int *)arg); - } + case TIOCOUTQ: { + long amount; + amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); + if (amount < 0) + amount = 0; + return put_user(amount, (int *)arg); + } - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - /* 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); - } + case TIOCINQ: { + struct sk_buff *skb; + long amount = 0L; + /* 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); + } - 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; - } - return -EINVAL; + 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; + } + return -EINVAL; - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - return -EINVAL; + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + return -EINVAL; - case SIOCADDRT: - case SIOCDELRT: - case SIOCNRDECOBS: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - return nr_rt_ioctl(cmd, (void *)arg); + case SIOCADDRT: + case SIOCDELRT: + case SIOCNRDECOBS: + if (!capable(CAP_NET_ADMIN)) return -EPERM; + return nr_rt_ioctl(cmd, (void *)arg); - default: - return dev_ioctl(cmd, (void *)arg); + default: + return dev_ioctl(cmd, (void *)arg); } - /*NOTREACHED*/ return 0; } @@ -1187,7 +1164,7 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length) off_t pos = 0; off_t begin = 0; - cli(); + spin_lock_bh(&nr_list_lock); len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q inode\n"); @@ -1240,47 +1217,45 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length) break; } - sti(); + spin_unlock_bh(&nr_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); - if (len > length) len = length; + if (len > length) + len = length; - return(len); -} + return len; +} static struct net_proto_family nr_family_ops = { - .family = PF_NETROM, - .create = nr_create, + .family = PF_NETROM, + .create = nr_create, }; -static struct proto_ops SOCKOPS_WRAPPED(nr_proto_ops) = { - .family = PF_NETROM, - - .release = nr_release, - .bind = nr_bind, - .connect = nr_connect, - .socketpair = sock_no_socketpair, - .accept = nr_accept, - .getname = nr_getname, - .poll = datagram_poll, - .ioctl = nr_ioctl, - .listen = nr_listen, - .shutdown = sock_no_shutdown, - .setsockopt = nr_setsockopt, - .getsockopt = nr_getsockopt, - .sendmsg = nr_sendmsg, - .recvmsg = nr_recvmsg, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, +static struct proto_ops nr_proto_ops = { + .family = PF_NETROM, + + .release = nr_release, + .bind = nr_bind, + .connect = nr_connect, + .socketpair = sock_no_socketpair, + .accept = nr_accept, + .getname = nr_getname, + .poll = datagram_poll, + .ioctl = nr_ioctl, + .listen = nr_listen, + .shutdown = sock_no_shutdown, + .setsockopt = nr_setsockopt, + .getsockopt = nr_getsockopt, + .sendmsg = nr_sendmsg, + .recvmsg = nr_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, }; -#include <linux/smp_lock.h> -SOCKOPS_WRAP(nr_proto, PF_NETROM); - static struct notifier_block nr_dev_notifier = { - .notifier_call =nr_device_event, + .notifier_call = nr_device_event, }; static struct net_device *dev_nr; diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index d6d5cbd641b5..67308a60d2dc 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -1,23 +1,11 @@ /* - * NET/ROM release 007 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * NET/ROM 001 Jonathan(G4KLX) Cloned from loopback.c - * NET/ROM 002 Steve Whitehouse(GW7RRM) fixed the set_mac_address - * NET/ROM 003 Jonathan(G4KLX) Put nr_rebuild_header into line with - * ax25_rebuild_header - * NET/ROM 004 Jonathan(G4KLX) Callsign registration with AX.25. - * NET/ROM 006 Hans(PE1AYX) Fixed interface to IP layer. + * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/config.h> #define __NO_VERSION__ #include <linux/module.h> @@ -115,7 +103,7 @@ static int nr_rebuild_header(struct sk_buff *skb) kfree_skb(skb); len = skbn->len; - + if (!nr_route_frame(skbn, NULL)) { kfree_skb(skbn); stats->tx_errors++; diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c index 5e28d34eeb23..a5457a1d9cfc 100644 --- a/net/netrom/nr_in.c +++ b/net/netrom/nr_in.c @@ -1,29 +1,12 @@ /* - * NET/ROM release 007 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_in.c - * NET/ROM 003 Jonathan(G4KLX) Added NET/ROM fragment reception. - * Darryl(G7LED) Added missing INFO with NAK case, optimized - * INFOACK handling, removed reconnect on error. - * NET/ROM 006 Jonathan(G4KLX) Hdrincl removal changes. - * NET/ROM 007 Jonathan(G4KLX) New timer architecture. + * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -77,7 +60,7 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) kfree_skb(skbo); } - nr->fraglen = 0; + nr->fraglen = 0; } return sock_queue_rcv_skb(sk, skbn); @@ -88,36 +71,36 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) * The handling of the timer(s) is in file nr_timer.c. * Handling of state 0 and connection release is in netrom.c. */ -static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) +static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, + int frametype) { switch (frametype) { + case NR_CONNACK: { + nr_cb *nr = nr_sk(sk); + + nr_stop_t1timer(sk); + nr_start_idletimer(sk); + nr->your_index = skb->data[17]; + nr->your_id = skb->data[18]; + nr->vs = 0; + nr->va = 0; + nr->vr = 0; + nr->vl = 0; + nr->state = NR_STATE_3; + nr->n2count = 0; + nr->window = skb->data[20]; + sk->state = TCP_ESTABLISHED; + if (!sk->dead) + sk->state_change(sk); + break; + } - case NR_CONNACK: { - nr_cb *nr = nr_sk(sk); - - nr_stop_t1timer(sk); - nr_start_idletimer(sk); - nr->your_index = skb->data[17]; - nr->your_id = skb->data[18]; - nr->vs = 0; - nr->va = 0; - nr->vr = 0; - nr->vl = 0; - nr->state = NR_STATE_3; - nr->n2count = 0; - nr->window = skb->data[20]; - sk->state = TCP_ESTABLISHED; - if (!sk->dead) - sk->state_change(sk); - break; - } - - case NR_CONNACK | NR_CHOKE_FLAG: - nr_disconnect(sk, ECONNREFUSED); - break; + case NR_CONNACK | NR_CHOKE_FLAG: + nr_disconnect(sk, ECONNREFUSED); + break; - default: - break; + default: + break; } return 0; @@ -128,23 +111,23 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype * The handling of the timer(s) is in file nr_timer.c * Handling of state 0 and connection release is in netrom.c. */ -static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype) +static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, + int frametype) { switch (frametype) { + case NR_CONNACK | NR_CHOKE_FLAG: + nr_disconnect(sk, ECONNRESET); + break; - case NR_CONNACK | NR_CHOKE_FLAG: - nr_disconnect(sk, ECONNRESET); - break; - - case NR_DISCREQ: - nr_write_internal(sk, NR_DISCACK); + case NR_DISCREQ: + nr_write_internal(sk, NR_DISCACK); - case NR_DISCACK: - nr_disconnect(sk, 0); - break; + case NR_DISCACK: + nr_disconnect(sk, 0); + break; - default: - break; + default: + break; } return 0; @@ -168,35 +151,62 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype ns = skb->data[17]; switch (frametype) { - - case NR_CONNREQ: - nr_write_internal(sk, NR_CONNACK); - break; - - case NR_DISCREQ: - nr_write_internal(sk, NR_DISCACK); - nr_disconnect(sk, 0); - break; - - case NR_CONNACK | NR_CHOKE_FLAG: - case NR_DISCACK: - nr_disconnect(sk, ECONNRESET); + case NR_CONNREQ: + nr_write_internal(sk, NR_CONNACK); + break; + + case NR_DISCREQ: + nr_write_internal(sk, NR_DISCACK); + nr_disconnect(sk, 0); + break; + + case NR_CONNACK | NR_CHOKE_FLAG: + case NR_DISCACK: + nr_disconnect(sk, ECONNRESET); + break; + + case NR_INFOACK: + case NR_INFOACK | NR_CHOKE_FLAG: + case NR_INFOACK | NR_NAK_FLAG: + case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG: + if (frametype & NR_CHOKE_FLAG) { + nrom->condition |= NR_COND_PEER_RX_BUSY; + nr_start_t4timer(sk); + } else { + nrom->condition &= ~NR_COND_PEER_RX_BUSY; + nr_stop_t4timer(sk); + } + if (!nr_validate_nr(sk, nr)) { break; - - case NR_INFOACK: - case NR_INFOACK | NR_CHOKE_FLAG: - case NR_INFOACK | NR_NAK_FLAG: - case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG: - if (frametype & NR_CHOKE_FLAG) { - nrom->condition |= NR_COND_PEER_RX_BUSY; - nr_start_t4timer(sk); + } + if (frametype & NR_NAK_FLAG) { + nr_frames_acked(sk, nr); + nr_send_nak_frame(sk); + } else { + if (nrom->condition & NR_COND_PEER_RX_BUSY) { + nr_frames_acked(sk, nr); } else { - nrom->condition &= ~NR_COND_PEER_RX_BUSY; - nr_stop_t4timer(sk); - } - if (!nr_validate_nr(sk, nr)) { - break; + nr_check_iframes_acked(sk, nr); } + } + break; + + case NR_INFO: + case NR_INFO | NR_NAK_FLAG: + case NR_INFO | NR_CHOKE_FLAG: + case NR_INFO | NR_MORE_FLAG: + case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG: + case NR_INFO | NR_CHOKE_FLAG | NR_MORE_FLAG: + case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG: + case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG: + if (frametype & NR_CHOKE_FLAG) { + nrom->condition |= NR_COND_PEER_RX_BUSY; + nr_start_t4timer(sk); + } else { + nrom->condition &= ~NR_COND_PEER_RX_BUSY; + nr_stop_t4timer(sk); + } + if (nr_validate_nr(sk, nr)) { if (frametype & NR_NAK_FLAG) { nr_frames_acked(sk, nr); nr_send_nak_frame(sk); @@ -207,76 +217,48 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype nr_check_iframes_acked(sk, nr); } } + } + queued = 1; + skb_queue_head(&nrom->reseq_queue, skb); + if (nrom->condition & NR_COND_OWN_RX_BUSY) break; - - case NR_INFO: - case NR_INFO | NR_NAK_FLAG: - case NR_INFO | NR_CHOKE_FLAG: - case NR_INFO | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG: - case NR_INFO | NR_CHOKE_FLAG | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG: - if (frametype & NR_CHOKE_FLAG) { - nrom->condition |= NR_COND_PEER_RX_BUSY; - nr_start_t4timer(sk); - } else { - nrom->condition &= ~NR_COND_PEER_RX_BUSY; - nr_stop_t4timer(sk); - } - if (nr_validate_nr(sk, nr)) { - if (frametype & NR_NAK_FLAG) { - nr_frames_acked(sk, nr); - nr_send_nak_frame(sk); - } else { - if (nrom->condition & NR_COND_PEER_RX_BUSY) { - nr_frames_acked(sk, nr); + skb_queue_head_init(&temp_queue); + do { + save_vr = nrom->vr; + while ((skbn = skb_dequeue(&nrom->reseq_queue)) != NULL) { + ns = skbn->data[17]; + if (ns == nrom->vr) { + if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) { + nrom->vr = (nrom->vr + 1) % NR_MODULUS; } else { - nr_check_iframes_acked(sk, nr); - } - } - } - queued = 1; - skb_queue_head(&nrom->reseq_queue, skb); - if (nrom->condition & NR_COND_OWN_RX_BUSY) - break; - skb_queue_head_init(&temp_queue); - do { - save_vr = nrom->vr; - while ((skbn = skb_dequeue(&nrom->reseq_queue)) != NULL) { - ns = skbn->data[17]; - if (ns == nrom->vr) { - if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) { - nrom->vr = (nrom->vr + 1) % NR_MODULUS; - } else { - nrom->condition |= NR_COND_OWN_RX_BUSY; - skb_queue_tail(&temp_queue, skbn); - } - } else if (nr_in_rx_window(sk, ns)) { + nrom->condition |= NR_COND_OWN_RX_BUSY; skb_queue_tail(&temp_queue, skbn); - } else { - kfree_skb(skbn); } - } - while ((skbn = skb_dequeue(&temp_queue)) != NULL) { - skb_queue_tail(&nrom->reseq_queue, skbn); - } - } while (save_vr != nrom->vr); - /* - * Window is full, ack it immediately. - */ - if (((nrom->vl + nrom->window) % NR_MODULUS) == nrom->vr) { - nr_enquiry_response(sk); - } else { - if (!(nrom->condition & NR_COND_ACK_PENDING)) { - nrom->condition |= NR_COND_ACK_PENDING; - nr_start_t2timer(sk); + } else if (nr_in_rx_window(sk, ns)) { + skb_queue_tail(&temp_queue, skbn); + } else { + kfree_skb(skbn); } } - break; + while ((skbn = skb_dequeue(&temp_queue)) != NULL) { + skb_queue_tail(&nrom->reseq_queue, skbn); + } + } while (save_vr != nrom->vr); + /* + * Window is full, ack it immediately. + */ + if (((nrom->vl + nrom->window) % NR_MODULUS) == nrom->vr) { + nr_enquiry_response(sk); + } else { + if (!(nrom->condition & NR_COND_ACK_PENDING)) { + nrom->condition |= NR_COND_ACK_PENDING; + nr_start_t2timer(sk); + } + } + break; - default: - break; + default: + break; } return queued; @@ -294,15 +276,15 @@ int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) frametype = skb->data[19]; switch (nr->state) { - case NR_STATE_1: - queued = nr_state1_machine(sk, skb, frametype); - break; - case NR_STATE_2: - queued = nr_state2_machine(sk, skb, frametype); - break; - case NR_STATE_3: - queued = nr_state3_machine(sk, skb, frametype); - break; + case NR_STATE_1: + queued = nr_state1_machine(sk, skb, frametype); + break; + case NR_STATE_2: + queued = nr_state2_machine(sk, skb, frametype); + break; + case NR_STATE_3: + queued = nr_state3_machine(sk, skb, frametype); + break; } nr_kick(sk); diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c index c943c09646bb..1d0f987529bc 100644 --- a/net/netrom/nr_loopback.c +++ b/net/netrom/nr_loopback.c @@ -1,20 +1,11 @@ /* - * NET/ROM release 007 - * - * 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 - * NET/ROM 007 Tomi(OH2BNS) Created this file. - * Small change in nr_loopback_queue(). + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * + * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi) */ - #include <linux/types.h> #include <linux/socket.h> #include <linux/timer.h> diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c index 10996ba5ecdd..3e71bbeba389 100644 --- a/net/netrom/nr_out.c +++ b/net/netrom/nr_out.c @@ -1,21 +1,12 @@ /* - * NET/ROM release 007 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_out.c - * NET/ROM 003 Jonathan(G4KLX) Added NET/ROM fragmentation. - * Darryl(G7LED) Fixed NAK, to give out correct reponse. - * NET/ROM 007 Jonathan(G4KLX) New timer architecture. + * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -85,7 +76,7 @@ void nr_output(struct sock *sk, struct sk_buff *skb) nr_kick(sk); } -/* +/* * 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. */ @@ -104,7 +95,7 @@ static void nr_send_iframe(struct sock *sk, struct sk_buff *skb) nr_start_idletimer(sk); - nr_transmit_buffer(sk, skb); + nr_transmit_buffer(sk, skb); } void nr_send_nak_frame(struct sock *sk) diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index ec0578b51316..fa85cae733f4 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -1,26 +1,13 @@ /* - * NET/ROM release 007 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * NET/ROM 001 Jonathan(G4KLX) First attempt. - * NET/ROM 003 Jonathan(G4KLX) Use SIOCADDRT/SIOCDELRT ioctl values - * for NET/ROM routes. - * Use '*' for a blank mnemonic in /proc/net/nr_nodes. - * Change default quality for new neighbour when same - * as node callsign. - * Alan Cox(GW4PTS) Added the firewall hooks. - * NET/ROM 006 Jonathan(G4KLX) Added the setting of digipeated neighbours. - * Tomi(OH2BNS) Routing quality and link failure changes. + * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -47,12 +34,15 @@ #include <linux/notifier.h> #include <linux/netfilter.h> #include <linux/init.h> +#include <linux/spinlock.h> #include <net/netrom.h> static unsigned int nr_neigh_no = 1; static struct nr_node *nr_node_list; +static spinlock_t nr_node_lock; static struct nr_neigh *nr_neigh_list; +static spinlock_t nr_neigh_lock; static void nr_remove_neigh(struct nr_neigh *); @@ -66,7 +56,6 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 struct nr_node *nr_node; struct nr_neigh *nr_neigh; struct nr_route nr_route; - unsigned long flags; int i, found; if (nr_dev_get(nr) != NULL) /* Can't add routes to ourself */ @@ -124,13 +113,10 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); } - save_flags(flags); - cli(); - + spin_lock_bh(&nr_neigh_lock); nr_neigh->next = nr_neigh_list; nr_neigh_list = nr_neigh; - - restore_flags(flags); + spin_unlock_bh(&nr_neigh_lock); } if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) @@ -150,13 +136,10 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_node->routes[0].obs_count = obs_count; nr_node->routes[0].neighbour = nr_neigh; - save_flags(flags); - cli(); - + spin_lock_bh(&nr_node_lock); nr_node->next = nr_node_list; nr_node_list = nr_node; - - restore_flags(flags); + spin_unlock_bh(&nr_node_lock); nr_neigh->count++; @@ -207,40 +190,49 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 /* Now re-sort the routes in quality order */ switch (nr_node->count) { - case 3: - if (nr_node->routes[1].quality > nr_node->routes[0].quality) { - switch (nr_node->which) { - case 0: nr_node->which = 1; break; - case 1: nr_node->which = 0; break; - default: break; - } - nr_route = nr_node->routes[0]; - nr_node->routes[0] = nr_node->routes[1]; - nr_node->routes[1] = nr_route; + case 3: + if (nr_node->routes[1].quality > nr_node->routes[0].quality) { + switch (nr_node->which) { + case 0: nr_node->which = 1; break; + case 1: nr_node->which = 0; break; + default: break; } - if (nr_node->routes[2].quality > nr_node->routes[1].quality) { - switch (nr_node->which) { - case 1: nr_node->which = 2; break; - case 2: nr_node->which = 1; break; - default: break; - } - nr_route = nr_node->routes[1]; - nr_node->routes[1] = nr_node->routes[2]; - nr_node->routes[2] = nr_route; + nr_route = nr_node->routes[0]; + nr_node->routes[0] = nr_node->routes[1]; + nr_node->routes[1] = nr_route; + } + if (nr_node->routes[2].quality > nr_node->routes[1].quality) { + switch (nr_node->which) { + case 1: nr_node->which = 2; + break; + + case 2: nr_node->which = 1; + break; + + default: + break; } - case 2: - if (nr_node->routes[1].quality > nr_node->routes[0].quality) { - switch (nr_node->which) { - case 0: nr_node->which = 1; break; - case 1: nr_node->which = 0; break; - default: break; - } - nr_route = nr_node->routes[0]; - nr_node->routes[0] = nr_node->routes[1]; - nr_node->routes[1] = nr_route; + nr_route = nr_node->routes[1]; + nr_node->routes[1] = nr_node->routes[2]; + nr_node->routes[2] = nr_route; + } + case 2: + if (nr_node->routes[1].quality > nr_node->routes[0].quality) { + switch (nr_node->which) { + case 0: nr_node->which = 1; + break; + + case 1: nr_node->which = 0; + break; + + default: break; } - case 1: - break; + nr_route = nr_node->routes[0]; + nr_node->routes[0] = nr_node->routes[1]; + nr_node->routes[1] = nr_route; + } + case 1: + break; } for (i = 0; i < nr_node->count; i++) { @@ -257,14 +249,11 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 static void nr_remove_node(struct nr_node *nr_node) { struct nr_node *s; - unsigned long flags; - - save_flags(flags); - cli(); + spin_lock_bh(&nr_node_lock); if ((s = nr_node_list) == nr_node) { nr_node_list = nr_node->next; - restore_flags(flags); + spin_unlock_bh(&nr_node_lock); kfree(nr_node); return; } @@ -272,7 +261,7 @@ static void nr_remove_node(struct nr_node *nr_node) while (s != NULL && s->next != NULL) { if (s->next == nr_node) { s->next = nr_node->next; - restore_flags(flags); + spin_unlock_bh(&nr_node_lock); kfree(nr_node); return; } @@ -280,20 +269,17 @@ static void nr_remove_node(struct nr_node *nr_node) s = s->next; } - restore_flags(flags); + spin_unlock_bh(&nr_node_lock); } static void nr_remove_neigh(struct nr_neigh *nr_neigh) { struct nr_neigh *s; - unsigned long flags; - - save_flags(flags); - cli(); + spin_lock_bh(&nr_neigh_lock); if ((s = nr_neigh_list) == nr_neigh) { nr_neigh_list = nr_neigh->next; - restore_flags(flags); + spin_unlock_bh(&nr_neigh_lock); if (nr_neigh->digipeat != NULL) kfree(nr_neigh->digipeat); kfree(nr_neigh); @@ -303,7 +289,7 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) while (s != NULL && s->next != NULL) { if (s->next == nr_neigh) { s->next = nr_neigh->next; - restore_flags(flags); + spin_unlock_bh(&nr_neigh_lock); if (nr_neigh->digipeat != NULL) kfree(nr_neigh->digipeat); kfree(nr_neigh); @@ -312,8 +298,7 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) s = s->next; } - - restore_flags(flags); + spin_unlock_bh(&nr_neigh_lock); } /* @@ -330,13 +315,15 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n if (ax25cmp(callsign, &nr_node->callsign) == 0) break; - if (nr_node == NULL) return -EINVAL; + 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) break; - if (nr_neigh == NULL) return -EINVAL; + if (nr_neigh == NULL) + return -EINVAL; for (i = 0; i < nr_node->count; i++) { if (nr_node->routes[i].neighbour == nr_neigh) { @@ -351,12 +338,12 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n nr_remove_node(nr_node); } else { switch (i) { - case 0: - nr_node->routes[0] = nr_node->routes[1]; - case 1: - nr_node->routes[1] = nr_node->routes[2]; - case 2: - break; + case 0: + nr_node->routes[0] = nr_node->routes[1]; + case 1: + nr_node->routes[1] = nr_node->routes[2]; + case 2: + break; } } @@ -373,7 +360,6 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, 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) { @@ -404,15 +390,12 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); } - save_flags(flags); - cli(); - + spin_lock_bh(&nr_neigh_lock); nr_neigh->next = nr_neigh_list; nr_neigh_list = nr_neigh; + spin_unlock_bh(&nr_neigh_lock); - restore_flags(flags); - - return 0; + return 0; } /* @@ -457,7 +440,6 @@ static int nr_dec_obs(void) for (i = 0; i < s->count; i++) { switch (s->routes[i].obs_count) { - case 0: /* A locked entry */ break; @@ -520,12 +502,12 @@ void nr_rt_device_down(struct net_device *dev) t->count--; switch (i) { - case 0: - t->routes[0] = t->routes[1]; - case 1: - t->routes[1] = t->routes[2]; - case 2: - break; + case 0: + t->routes[0] = t->routes[1]; + case 1: + t->routes[1] = t->routes[2]; + case 2: + break; } } } @@ -622,51 +604,50 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) struct net_device *dev; switch (cmd) { + case SIOCADDRT: + if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) + return -EFAULT; + if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) + return -EINVAL; + if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) + 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), + 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); + default: + return -EINVAL; + } - case SIOCADDRT: - if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) - return -EFAULT; - if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) - return -EINVAL; - if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) - 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), - 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); - default: - return -EINVAL; - } - - case SIOCDELRT: - if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) - return -EFAULT; - if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) - return -EINVAL; - switch (nr_route.type) { - case NETROM_NODE: - return nr_del_node(&nr_route.callsign, - &nr_route.neighbour, dev); - case NETROM_NEIGH: - return nr_del_neigh(&nr_route.callsign, - dev, nr_route.quality); - default: - return -EINVAL; - } - - case SIOCNRDECOBS: - return nr_dec_obs(); - + case SIOCDELRT: + if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) + return -EFAULT; + if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) + return -EINVAL; + switch (nr_route.type) { + case NETROM_NODE: + return nr_del_node(&nr_route.callsign, + &nr_route.neighbour, dev); + case NETROM_NEIGH: + return nr_del_neigh(&nr_route.callsign, + dev, nr_route.quality); default: return -EINVAL; + } + + case SIOCNRDECOBS: + return nr_dec_obs(); + + default: + return -EINVAL; } return 0; @@ -758,8 +739,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) off_t begin = 0; int i; - cli(); - + spin_lock_bh(&nr_node_lock); len += sprintf(buffer, "callsign mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n"); for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) { @@ -767,7 +747,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) ax2asc(&nr_node->callsign), (nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic, nr_node->which + 1, - nr_node->count); + nr_node->count); for (i = 0; i < nr_node->count; i++) { len += sprintf(buffer + len, " %3d %d %05d", @@ -788,8 +768,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) if (pos > offset + length) break; } - - sti(); + spin_unlock_bh(&nr_node_lock); *start = buffer + (offset - begin); len -= (offset - begin); @@ -797,7 +776,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) if (len > length) len = length; return len; -} +} int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) { @@ -807,8 +786,7 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) off_t begin = 0; int i; - cli(); - + spin_lock_bh(&nr_neigh_lock); len += sprintf(buffer, "addr callsign dev qual lock count failed digipeaters\n"); for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { @@ -839,7 +817,7 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) break; } - sti(); + spin_unlock_bh(&nr_neigh_lock); *start = buffer + (offset - begin); len -= (offset - begin); @@ -847,7 +825,7 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) if (len > length) len = length; return len; -} +} /* * Free all memory associated with the nodes and routes lists. diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c index f9c4fba77c04..f1d7aef4b811 100644 --- a/net/netrom/nr_subr.c +++ b/net/netrom/nr_subr.c @@ -1,20 +1,11 @@ /* - * NET/ROM release 007 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_subr.c - * NET/ROM 003 Jonathan(G4KLX) Added G8BPQ NET/ROM extensions. - * NET/ROM 007 Jonathan(G4KLX) New timer architecture. + * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -125,7 +116,7 @@ int nr_in_rx_window(struct sock *sk, unsigned short ns) return 0; } -/* +/* * This routine is called when the HDLC layer internally generates a * control frame. */ @@ -139,19 +130,19 @@ void nr_write_internal(struct sock *sk, int frametype) len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN; switch (frametype & 0x0F) { - case NR_CONNREQ: - len += 17; - break; - case NR_CONNACK: - len += (nr->bpqext) ? 2 : 1; - break; - case NR_DISCREQ: - case NR_DISCACK: - case NR_INFOACK: - break; - default: - printk(KERN_ERR "NET/ROM: nr_write_internal - invalid frame type %d\n", frametype); - return; + case NR_CONNREQ: + len += 17; + break; + case NR_CONNACK: + len += (nr->bpqext) ? 2 : 1; + break; + case NR_DISCREQ: + case NR_DISCACK: + case NR_INFOACK: + break; + default: + printk(KERN_ERR "NET/ROM: nr_write_internal - invalid frame type %d\n", frametype); + return; } if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) @@ -161,59 +152,58 @@ void nr_write_internal(struct sock *sk, int frametype) * Space for AX.25 and NET/ROM network header */ skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN); - + dptr = skb_put(skb, skb_tailroom(skb)); switch (frametype & 0x0F) { + case NR_CONNREQ: + timeout = nr->t1 / HZ; + *dptr++ = nr->my_index; + *dptr++ = nr->my_id; + *dptr++ = 0; + *dptr++ = 0; + *dptr++ = frametype; + *dptr++ = nr->window; + memcpy(dptr, &nr->user_addr, AX25_ADDR_LEN); + dptr[6] &= ~AX25_CBIT; + dptr[6] &= ~AX25_EBIT; + dptr[6] |= AX25_SSSID_SPARE; + dptr += AX25_ADDR_LEN; + memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); + dptr[6] &= ~AX25_CBIT; + dptr[6] &= ~AX25_EBIT; + dptr[6] |= AX25_SSSID_SPARE; + dptr += AX25_ADDR_LEN; + *dptr++ = timeout % 256; + *dptr++ = timeout / 256; + break; + + case NR_CONNACK: + *dptr++ = nr->your_index; + *dptr++ = nr->your_id; + *dptr++ = nr->my_index; + *dptr++ = nr->my_id; + *dptr++ = frametype; + *dptr++ = nr->window; + if (nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser; + break; + + case NR_DISCREQ: + case NR_DISCACK: + *dptr++ = nr->your_index; + *dptr++ = nr->your_id; + *dptr++ = 0; + *dptr++ = 0; + *dptr++ = frametype; + break; - case NR_CONNREQ: - timeout = nr->t1 / HZ; - *dptr++ = nr->my_index; - *dptr++ = nr->my_id; - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = frametype; - *dptr++ = nr->window; - memcpy(dptr, &nr->user_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - *dptr++ = timeout % 256; - *dptr++ = timeout / 256; - break; - - case NR_CONNACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = nr->my_index; - *dptr++ = nr->my_id; - *dptr++ = frametype; - *dptr++ = nr->window; - if (nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser; - break; - - case NR_DISCREQ: - case NR_DISCACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = frametype; - break; - - case NR_INFOACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = 0; - *dptr++ = nr->vr; - *dptr++ = frametype; - break; + case NR_INFOACK: + *dptr++ = nr->your_index; + *dptr++ = nr->your_id; + *dptr++ = 0; + *dptr++ = nr->vr; + *dptr++ = frametype; + break; } nr_transmit_buffer(sk, skb); @@ -243,7 +233,7 @@ void nr_transmit_refusal(struct sk_buff *skb, int mine) dptr[6] &= ~AX25_EBIT; dptr[6] |= AX25_SSSID_SPARE; dptr += AX25_ADDR_LEN; - + memcpy(dptr, skb->data + 0, AX25_ADDR_LEN); dptr[6] &= ~AX25_CBIT; dptr[6] |= AX25_EBIT; diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c index cdeab2e21f55..388105d711b0 100644 --- a/net/netrom/nr_timer.c +++ b/net/netrom/nr_timer.c @@ -1,20 +1,12 @@ /* - * NET/ROM release 007 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_timer.c - * NET/ROM 007 Jonathan(G4KLX) New timer architecture. - * Implemented idle timer. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -144,33 +136,34 @@ static void nr_heartbeat_expiry(unsigned long param) struct sock *sk = (struct sock *)param; nr_cb *nr = nr_sk(sk); + bh_lock_sock(sk); switch (nr->state) { - - case NR_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 (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - nr_destroy_socket(sk); - return; - } - break; - - case NR_STATE_3: - /* - * Check for the state of the receive buffer. - */ - if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && - (nr->condition & NR_COND_OWN_RX_BUSY)) { - nr->condition &= ~NR_COND_OWN_RX_BUSY; - nr->condition &= ~NR_COND_ACK_PENDING; - nr->vl = nr->vr; - nr_write_internal(sk, NR_INFOACK); - break; - } + case NR_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 (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { + nr_destroy_socket(sk); + return; + } + break; + + case NR_STATE_3: + /* + * Check for the state of the receive buffer. + */ + if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && + (nr->condition & NR_COND_OWN_RX_BUSY)) { + nr->condition &= ~NR_COND_OWN_RX_BUSY; + nr->condition &= ~NR_COND_ACK_PENDING; + nr->vl = nr->vr; + nr_write_internal(sk, NR_INFOACK); break; + } + break; } nr_start_heartbeat(sk); + bh_unlock_sock(sk); } static void nr_t2timer_expiry(unsigned long param) @@ -178,17 +171,21 @@ static void nr_t2timer_expiry(unsigned long param) struct sock *sk = (struct sock *)param; nr_cb *nr = nr_sk(sk); + bh_lock_sock(sk); if (nr->condition & NR_COND_ACK_PENDING) { nr->condition &= ~NR_COND_ACK_PENDING; nr_enquiry_response(sk); } + bh_unlock_sock(sk); } static void nr_t4timer_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + bh_lock_sock(sk); nr_sk(sk)->condition &= ~NR_COND_PEER_RX_BUSY; + bh_unlock_sock(sk); } static void nr_idletimer_expiry(unsigned long param) @@ -196,6 +193,8 @@ static void nr_idletimer_expiry(unsigned long param) struct sock *sk = (struct sock *)param; nr_cb *nr = nr_sk(sk); + bh_lock_sock(sk); + nr_clear_queues(sk); nr->n2count = 0; @@ -214,6 +213,7 @@ static void nr_idletimer_expiry(unsigned long param) sk->state_change(sk); sk->dead = 1; + bh_unlock_sock(sk); } static void nr_t1timer_expiry(unsigned long param) @@ -221,38 +221,39 @@ static void nr_t1timer_expiry(unsigned long param) struct sock *sk = (struct sock *)param; nr_cb *nr = nr_sk(sk); + bh_lock_sock(sk); switch (nr->state) { - - case NR_STATE_1: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - return; - } else { - nr->n2count++; - nr_write_internal(sk, NR_CONNREQ); - } - break; - - case NR_STATE_2: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - return; - } else { - nr->n2count++; - nr_write_internal(sk, NR_DISCREQ); - } - break; - - case NR_STATE_3: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - return; - } else { - nr->n2count++; - nr_requeue_frames(sk); - } - break; + case NR_STATE_1: + if (nr->n2count == nr->n2) { + nr_disconnect(sk, ETIMEDOUT); + return; + } else { + nr->n2count++; + nr_write_internal(sk, NR_CONNREQ); + } + break; + + case NR_STATE_2: + if (nr->n2count == nr->n2) { + nr_disconnect(sk, ETIMEDOUT); + return; + } else { + nr->n2count++; + nr_write_internal(sk, NR_DISCREQ); + } + break; + + case NR_STATE_3: + if (nr->n2count == nr->n2) { + nr_disconnect(sk, ETIMEDOUT); + return; + } else { + nr->n2count++; + nr_requeue_frames(sk); + } + break; } nr_start_t1timer(sk); + bh_unlock_sock(sk); } diff --git a/net/netrom/sysctl_net_netrom.c b/net/netrom/sysctl_net_netrom.c index bf8006236bdc..1e92edceb7bd 100644 --- a/net/netrom/sysctl_net_netrom.c +++ b/net/netrom/sysctl_net_netrom.c @@ -1,10 +1,11 @@ -/* -*- linux-c -*- - * sysctl_net_netrom.c: sysctl interface to net NET/ROM subsystem. +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/netrom directory entry (empty =) ). [MS] + * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) */ - #include <linux/mm.h> #include <linux/sysctl.h> #include <linux/init.h> diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index f9ccd78a8c0f..b672f85c9a49 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1,29 +1,14 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * ROSE 001 Jonathan(G4KLX) Cloned from af_netrom.c. - * Alan(GW4PTS) Hacked up for newer API stuff - * Terry (VK2KTJ) Added support for variable length - * address masks. - * ROSE 002 Jonathan(G4KLX) Changed hdrincl to qbitincl. - * Added random number facilities entry. - * Variable number of ROSE devices. - * ROSE 003 Jonathan(G4KLX) New timer architecture. - * Implemented idle timer. - * Added use count to neighbour. - * Tomi(OH2BNS) Fixed rose_getname(). - * Arnaldo C. Melo s/suser/capable/ + micro cleanups + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) + * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net) + * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi) */ - #include <linux/config.h> #include <linux/module.h> #include <linux/init.h> @@ -33,6 +18,7 @@ #include <linux/in.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <linux/timer.h> #include <linux/string.h> #include <linux/sockios.h> @@ -47,7 +33,7 @@ #include <asm/system.h> #include <asm/uaccess.h> #include <linux/fcntl.h> -#include <linux/termios.h> /* For TIOCINQ/OUTQ */ +#include <linux/termios.h> #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/notifier.h> @@ -71,6 +57,7 @@ int sysctl_rose_maximum_vcs = ROSE_DEFAULT_MAXVC; int sysctl_rose_window_size = ROSE_DEFAULT_WINDOW_SIZE; static struct sock *rose_list; +static spinlock_t rose_list_lock = SPIN_LOCK_UNLOCKED; static struct proto_ops rose_proto_ops; @@ -173,27 +160,24 @@ decmod: MOD_DEC_USE_COUNT; static void rose_remove_socket(struct sock *sk) { struct sock *s; - unsigned long flags; - - save_flags(flags); cli(); + spin_lock_bh(&rose_list_lock); if ((s = rose_list) == sk) { rose_list = s->next; - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); return; } while (s != NULL && s->next != NULL) { if (s->next == sk) { s->next = sk->next; - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); return; } s = s->next; } - - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); } /* @@ -204,6 +188,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) { struct sock *s; + spin_lock_bh(&rose_list_lock); for (s = rose_list; s != NULL; s = s->next) { rose_cb *rose = rose_sk(s); @@ -213,6 +198,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) rose->neighbour = NULL; } } + spin_unlock_bh(&rose_list_lock); } /* @@ -221,7 +207,8 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) static void rose_kill_by_device(struct net_device *dev) { struct sock *s; - + + spin_lock_bh(&rose_list_lock); for (s = rose_list; s != NULL; s = s->next) { rose_cb *rose = rose_sk(s); @@ -231,12 +218,14 @@ static void rose_kill_by_device(struct net_device *dev) rose->device = NULL; } } + spin_unlock_bh(&rose_list_lock); } /* * Handle device status changes. */ -static int rose_device_event(struct notifier_block *this, unsigned long event, void *ptr) +static int rose_device_event(struct notifier_block *this, unsigned long event, + void *ptr) { struct net_device *dev = (struct net_device *)ptr; @@ -244,13 +233,13 @@ static int rose_device_event(struct notifier_block *this, unsigned long event, v return NOTIFY_DONE; switch (dev->type) { - case ARPHRD_ROSE: - rose_kill_by_device(dev); - break; - case ARPHRD_AX25: - rose_link_device_down(dev); - rose_rt_device_down(dev); - break; + case ARPHRD_ROSE: + rose_kill_by_device(dev); + break; + case ARPHRD_AX25: + rose_link_device_down(dev); + rose_rt_device_down(dev); + break; } return NOTIFY_DONE; @@ -261,14 +250,11 @@ static int rose_device_event(struct notifier_block *this, unsigned long event, v */ static void rose_insert_socket(struct sock *sk) { - unsigned long flags; - - save_flags(flags); cli(); + spin_lock_bh(&rose_list_lock); sk->next = rose_list; rose_list = sk; - - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); } /* @@ -277,18 +263,16 @@ static void rose_insert_socket(struct sock *sk) */ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) { - unsigned long flags; struct sock *s; - save_flags(flags); cli(); - + spin_lock_bh(&rose_list_lock); for (s = rose_list; s != NULL; s = s->next) { rose_cb *rose = rose_sk(s); if (!rosecmp(&rose->source_addr, addr) && !ax25cmp(&rose->source_call, call) && !rose->source_ndigis && s->state == TCP_LISTEN) { - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); return s; } } @@ -299,12 +283,12 @@ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) if (!rosecmp(&rose->source_addr, addr) && !ax25cmp(&rose->source_call, &null_ax25_address) && s->state == TCP_LISTEN) { - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); return s; } } + spin_unlock_bh(&rose_list_lock); - restore_flags(flags); return NULL; } @@ -314,20 +298,17 @@ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh) { struct sock *s; - unsigned long flags; - - save_flags(flags); cli(); + spin_lock_bh(&rose_list_lock); for (s = rose_list; s != NULL; s = s->next) { rose_cb *rose = rose_sk(s); if (rose->lci == lci && rose->neighbour == neigh) { - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); return s; } } - - restore_flags(flags); + spin_unlock_bh(&rose_list_lock); return NULL; } @@ -366,23 +347,20 @@ static void rose_destroy_timer(unsigned long 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. + * 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 rose_destroy_socket(struct sock *sk) /* Not static as it's used by the timer */ +void rose_destroy_socket(struct sock *sk) { struct sk_buff *skb; - unsigned long flags; - - save_flags(flags); cli(); + rose_remove_socket(sk); rose_stop_heartbeat(sk); rose_stop_idletimer(sk); rose_stop_timer(sk); - rose_remove_socket(sk); rose_clear_queues(sk); /* Flush the queues */ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { @@ -405,8 +383,6 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time } else { rose_free_sock(sk); } - - restore_flags(flags); } /* @@ -431,46 +407,46 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, return -EFAULT; switch (optname) { - case ROSE_DEFER: - rose->defer = opt ? 1 : 0; - return 0; - - case ROSE_T1: - if (opt < 1) - return -EINVAL; - rose->t1 = opt * HZ; - return 0; - - case ROSE_T2: - if (opt < 1) - return -EINVAL; - rose->t2 = opt * HZ; - return 0; - - case ROSE_T3: - if (opt < 1) - return -EINVAL; - rose->t3 = opt * HZ; - return 0; - - case ROSE_HOLDBACK: - if (opt < 1) - return -EINVAL; - rose->hb = opt * HZ; - return 0; - - case ROSE_IDLE: - if (opt < 0) - return -EINVAL; - rose->idle = opt * 60 * HZ; - return 0; - - case ROSE_QBITINCL: - rose->qbitincl = opt ? 1 : 0; - return 0; - - default: - return -ENOPROTOOPT; + case ROSE_DEFER: + rose->defer = opt ? 1 : 0; + return 0; + + case ROSE_T1: + if (opt < 1) + return -EINVAL; + rose->t1 = opt * HZ; + return 0; + + case ROSE_T2: + if (opt < 1) + return -EINVAL; + rose->t2 = opt * HZ; + return 0; + + case ROSE_T3: + if (opt < 1) + return -EINVAL; + rose->t3 = opt * HZ; + return 0; + + case ROSE_HOLDBACK: + if (opt < 1) + return -EINVAL; + rose->hb = opt * HZ; + return 0; + + case ROSE_IDLE: + if (opt < 0) + return -EINVAL; + rose->idle = opt * 60 * HZ; + return 0; + + case ROSE_QBITINCL: + rose->qbitincl = opt ? 1 : 0; + return 0; + + default: + return -ENOPROTOOPT; } } @@ -484,44 +460,44 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_ROSE) return -ENOPROTOOPT; - + if (get_user(len, optlen)) return -EFAULT; if (len < 0) return -EINVAL; - + switch (optname) { - case ROSE_DEFER: - val = rose->defer; - break; + case ROSE_DEFER: + val = rose->defer; + break; - case ROSE_T1: - val = rose->t1 / HZ; - break; + case ROSE_T1: + val = rose->t1 / HZ; + break; - case ROSE_T2: - val = rose->t2 / HZ; - break; + case ROSE_T2: + val = rose->t2 / HZ; + break; - case ROSE_T3: - val = rose->t3 / HZ; - break; + case ROSE_T3: + val = rose->t3 / HZ; + break; - case ROSE_HOLDBACK: - val = rose->hb / HZ; - break; + case ROSE_HOLDBACK: + val = rose->hb / HZ; + break; - case ROSE_IDLE: - val = rose->idle / (60 * HZ); - break; + case ROSE_IDLE: + val = rose->idle / (60 * HZ); + break; - case ROSE_QBITINCL: - val = rose->qbitincl; - break; + case ROSE_QBITINCL: + val = rose->qbitincl; + break; - default: - return -ENOPROTOOPT; + default: + return -ENOPROTOOPT; } len = min_t(unsigned int, len, sizeof(int)); @@ -565,7 +541,7 @@ static int rose_create(struct socket *sock, int protocol) rose = rose_sk(sk); sock_init_data(sock, sk); - + skb_queue_head_init(&rose->ack_queue); #ifdef M_BIT skb_queue_head_init(&rose->frag_queue); @@ -647,39 +623,38 @@ static int rose_release(struct socket *sock) rose = rose_sk(sk); switch (rose->state) { - - case ROSE_STATE_0: - rose_disconnect(sk, 0, -1, -1); - rose_destroy_socket(sk); - break; - - case ROSE_STATE_2: - rose->neighbour->use--; - rose_disconnect(sk, 0, -1, -1); - rose_destroy_socket(sk); - break; - - case ROSE_STATE_1: - case ROSE_STATE_3: - case ROSE_STATE_4: - case ROSE_STATE_5: - rose_clear_queues(sk); - rose_stop_idletimer(sk); - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose_start_t3timer(sk); - rose->state = ROSE_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; - break; - - default: - break; + case ROSE_STATE_0: + rose_disconnect(sk, 0, -1, -1); + rose_destroy_socket(sk); + break; + + case ROSE_STATE_2: + rose->neighbour->use--; + rose_disconnect(sk, 0, -1, -1); + rose_destroy_socket(sk); + break; + + case ROSE_STATE_1: + case ROSE_STATE_3: + case ROSE_STATE_4: + case ROSE_STATE_5: + rose_clear_queues(sk); + rose_stop_idletimer(sk); + rose_write_internal(sk, ROSE_CLEAR_REQUEST); + rose_start_t3timer(sk); + rose->state = ROSE_STATE_2; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; + sk->state_change(sk); + sk->dead = 1; + sk->destroy = 1; + break; + + default: + break; } - sock->sk = NULL; + sock->sk = NULL; sk->socket = NULL; /* Not used, but we should do this. **/ return 0; @@ -842,71 +817,90 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) return -EINPROGRESS; - cli(); /* To avoid races on the sleep */ - /* - * A Connect Ack with Choke or timeout or failed routing will go to closed. + * A Connect Ack with Choke or timeout or failed routing will go to + * closed. */ - while (sk->state == TCP_SYN_SENT) { - interruptible_sleep_on(sk->sleep); - if (signal_pending(current)) { - sti(); + if (sk->state == TCP_SYN_SENT) { + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(sk->sleep, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (sk->state != TCP_SYN_SENT) + break; + if (!signal_pending(tsk)) { + schedule(); + continue; + } return -ERESTARTSYS; } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); } if (sk->state != TCP_ESTABLISHED) { - sti(); sock->state = SS_UNCONNECTED; return sock_error(sk); /* Always set at this point */ } sock->state = SS_CONNECTED; - sti(); - return 0; } static int rose_accept(struct socket *sock, struct socket *newsock, int flags) { - struct sock *sk; - struct sock *newsk; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); struct sk_buff *skb; + struct sock *newsk; + struct sock *sk; + int err = 0; if ((sk = sock->sk) == NULL) return -EINVAL; - if (sk->type != SOCK_SEQPACKET) - return -EOPNOTSUPP; + lock_sock(sk); + if (sk->type != SOCK_SEQPACKET) { + err = -EOPNOTSUPP; + goto out; + } - if (sk->state != TCP_LISTEN) - return -EINVAL; + if (sk->state != TCP_LISTEN) { + err = -EINVAL; + goto out; + } /* * The write queue this time is holding sockets ready to use * hooked into the SABM we saved */ - do { - cli(); - if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { - if (flags & O_NONBLOCK) { - sti(); - return -EWOULDBLOCK; - } - interruptible_sleep_on(sk->sleep); - if (signal_pending(current)) { - sti(); - return -ERESTARTSYS; - } + add_wait_queue(sk->sleep, &wait); + for (;;) { + skb = skb_dequeue(&sk->receive_queue); + if (skb) + break; + + current->state = TASK_INTERRUPTIBLE; + release_sock(sk); + if (flags & O_NONBLOCK) + return -EWOULDBLOCK; + if (!signal_pending(tsk)) { + schedule(); + lock_sock(sk); + continue; } - } while (skb == NULL); + return -ERESTARTSYS; + } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); newsk = skb->sk; newsk->pair = NULL; newsk->socket = newsock; newsk->sleep = &newsock->wait; - sti(); /* Now attach up the new socket */ skb->sk = NULL; @@ -914,7 +908,10 @@ static int rose_accept(struct socket *sock, struct socket *newsock, int flags) sk->ack_backlog--; newsock->sk = newsk; - return 0; +out: + release_sock(sk); + + return err; } static int rose_getname(struct socket *sock, struct sockaddr *uaddr, @@ -961,7 +958,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros * skb->data points to the rose frame start */ memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); - + len = (((skb->data[3] >> 4) & 0x0F) + 1) / 2; len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2; if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { @@ -1028,7 +1025,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros return 1; } -static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, +static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; @@ -1131,7 +1128,7 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, SOCK_DEBUG(sk, "ROSE: Built header.\n"); SOCK_DEBUG(sk, "ROSE: Transmitting buffer\n"); - + if (sk->state != TCP_ESTABLISHED) { kfree_skb(skb); return -ENOTCONN; @@ -1144,7 +1141,7 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct sk_buff *skbn; int frontlen; int lg; - + /* Save a copy of the Header */ memcpy(header, skb->data, ROSE_MIN_LEN); skb_pull(skb, ROSE_MIN_LEN); @@ -1173,10 +1170,10 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, if (skb->len > 0) skbn->data[2] |= M_BIT; - + skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */ } - + skb->free = 1; kfree_skb(skb, FREE_WRITE); } else { @@ -1192,7 +1189,7 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, } -static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, +static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; @@ -1264,96 +1261,96 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) rose_cb *rose = rose_sk(sk); switch (cmd) { - case TIOCOUTQ: { - long amount; - amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); - if (amount < 0) - amount = 0; - return put_user(amount, (unsigned int *)arg); - } + case TIOCOUTQ: { + long amount; + amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); + if (amount < 0) + amount = 0; + return put_user(amount, (unsigned int *)arg); + } - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - /* 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, (unsigned int *)arg); + case TIOCINQ: { + struct sk_buff *skb; + long amount = 0L; + /* 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, (unsigned int *)arg); + } + + 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; } + return -EINVAL; - 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; - } - return -EINVAL; + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + return -EINVAL; - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - return -EINVAL; + case SIOCADDRT: + case SIOCDELRT: + case SIOCRSCLRRT: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + return rose_rt_ioctl(cmd, (void *)arg); + + case SIOCRSGCAUSE: { + struct rose_cause_struct rose_cause; + rose_cause.cause = rose->cause; + rose_cause.diagnostic = rose->diagnostic; + return copy_to_user((void *)arg, &rose_cause, sizeof(struct rose_cause_struct)) ? -EFAULT : 0; + } - case SIOCADDRT: - case SIOCDELRT: - case SIOCRSCLRRT: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - return rose_rt_ioctl(cmd, (void *)arg); - - case SIOCRSGCAUSE: { - struct rose_cause_struct rose_cause; - rose_cause.cause = rose->cause; - rose_cause.diagnostic = rose->diagnostic; - return copy_to_user((void *)arg, &rose_cause, sizeof(struct rose_cause_struct)) ? -EFAULT : 0; - } + case SIOCRSSCAUSE: { + struct rose_cause_struct rose_cause; + if (copy_from_user(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct))) + return -EFAULT; + rose->cause = rose_cause.cause; + rose->diagnostic = rose_cause.diagnostic; + return 0; + } - case SIOCRSSCAUSE: { - struct rose_cause_struct rose_cause; - if (copy_from_user(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct))) - return -EFAULT; - rose->cause = rose_cause.cause; - rose->diagnostic = rose_cause.diagnostic; - return 0; - } + case SIOCRSSL2CALL: + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) + ax25_listen_release(&rose_callsign, NULL); + if (copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address))) + return -EFAULT; + if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) + ax25_listen_register(&rose_callsign, NULL); + return 0; - case SIOCRSSL2CALL: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - ax25_listen_release(&rose_callsign, NULL); - if (copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address))) - return -EFAULT; - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - ax25_listen_register(&rose_callsign, NULL); - return 0; - - case SIOCRSGL2CALL: - return copy_to_user((void *)arg, &rose_callsign, sizeof(ax25_address)) ? -EFAULT : 0; - - case SIOCRSACCEPT: - if (rose->state == ROSE_STATE_5) { - rose_write_internal(sk, ROSE_CALL_ACCEPTED); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->vs = 0; - rose->va = 0; - rose->vr = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - } - return 0; + case SIOCRSGL2CALL: + return copy_to_user((void *)arg, &rose_callsign, sizeof(ax25_address)) ? -EFAULT : 0; + + case SIOCRSACCEPT: + if (rose->state == ROSE_STATE_5) { + rose_write_internal(sk, ROSE_CALL_ACCEPTED); + rose_start_idletimer(sk); + rose->condition = 0x00; + rose->vs = 0; + rose->va = 0; + rose->vr = 0; + rose->vl = 0; + rose->state = ROSE_STATE_3; + } + return 0; - default: - return dev_ioctl(cmd, (void *)arg); + default: + return dev_ioctl(cmd, (void *)arg); } - /*NOTREACHED*/ return 0; } @@ -1366,7 +1363,7 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length) off_t pos = 0; off_t begin = 0; - cli(); + spin_lock_bh(&rose_list_lock); len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci neigh st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q inode\n"); @@ -1390,7 +1387,7 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length) len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %05d %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d %ld\n", rose2asc(&rose->source_addr), callsign, - devname, + devname, rose->lci & 0x0FFF, (rose->neighbour) ? rose->neighbour->number : 0, rose->state, @@ -1418,48 +1415,44 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length) if (pos > offset + length) break; } - - sti(); + spin_unlock_bh(&rose_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; - return(len); -} + return len; +} static struct net_proto_family rose_family_ops = { - .family = PF_ROSE, - .create = rose_create, + .family = PF_ROSE, + .create = rose_create, }; -static struct proto_ops SOCKOPS_WRAPPED(rose_proto_ops) = { - .family = PF_ROSE, - - .release = rose_release, - .bind = rose_bind, - .connect = rose_connect, - .socketpair = sock_no_socketpair, - .accept = rose_accept, - .getname = rose_getname, - .poll = datagram_poll, - .ioctl = rose_ioctl, - .listen = rose_listen, - .shutdown = sock_no_shutdown, - .setsockopt = rose_setsockopt, - .getsockopt = rose_getsockopt, - .sendmsg = rose_sendmsg, - .recvmsg = rose_recvmsg, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, +static struct proto_ops rose_proto_ops = { + .family = PF_ROSE, + + .release = rose_release, + .bind = rose_bind, + .connect = rose_connect, + .socketpair = sock_no_socketpair, + .accept = rose_accept, + .getname = rose_getname, + .poll = datagram_poll, + .ioctl = rose_ioctl, + .listen = rose_listen, + .shutdown = sock_no_shutdown, + .setsockopt = rose_setsockopt, + .getsockopt = rose_getsockopt, + .sendmsg = rose_sendmsg, + .recvmsg = rose_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, }; -#include <linux/smp_lock.h> -SOCKOPS_WRAP(rose_proto, PF_ROSE); - static struct notifier_block rose_dev_notifier = { - .notifier_call =rose_device_event, + .notifier_call = rose_device_event, }; static struct net_device *dev_rose; @@ -1555,5 +1548,5 @@ static void __exit rose_exit(void) kfree(dev_rose); } -module_exit(rose_exit); +module_exit(rose_exit); diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index e50bda038c3b..befeaac63937 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -1,19 +1,11 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * ROSE 001 Jonathan(G4KLX) Cloned from nr_dev.c. - * Hans(PE1AYX) Fixed interface to IP layer. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/config.h> #define __NO_VERSION__ #include <linux/module.h> @@ -29,7 +21,7 @@ #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/in.h> -#include <linux/if_ether.h> /* For the statistics structure. */ +#include <linux/if_ether.h> #include <asm/system.h> #include <asm/io.h> diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c index 4c5bbb54d534..48e09cded78e 100644 --- a/net/rose/rose_in.c +++ b/net/rose/rose_in.c @@ -1,27 +1,16 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) * - * 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 - * ROSE 001 Jonathan(G4KLX) Cloned from nr_in.c - * ROSE 002 Jonathan(G4KLX) Return cause and diagnostic codes from Clear Requests. - * ROSE 003 Jonathan(G4KLX) New timer architecture. - * Removed M bit processing. + * 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. */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -55,29 +44,28 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety rose_cb *rose = rose_sk(sk); switch (frametype) { + case ROSE_CALL_ACCEPTED: + rose_stop_timer(sk); + rose_start_idletimer(sk); + rose->condition = 0x00; + rose->vs = 0; + rose->va = 0; + rose->vr = 0; + rose->vl = 0; + rose->state = ROSE_STATE_3; + sk->state = TCP_ESTABLISHED; + if (!sk->dead) + sk->state_change(sk); + break; + + case ROSE_CLEAR_REQUEST: + rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); + rose->neighbour->use--; + break; - case ROSE_CALL_ACCEPTED: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->vs = 0; - rose->va = 0; - rose->vr = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - sk->state = TCP_ESTABLISHED; - if (!sk->dead) - sk->state_change(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); - rose->neighbour->use--; - break; - - default: - break; + default: + break; } return 0; @@ -93,20 +81,19 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety rose_cb *rose = rose_sk(sk); switch (frametype) { + case ROSE_CLEAR_REQUEST: + rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + rose->neighbour->use--; + break; - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose->neighbour->use--; - break; - - case ROSE_CLEAR_CONFIRMATION: - rose_disconnect(sk, 0, -1, -1); - rose->neighbour->use--; - break; + case ROSE_CLEAR_CONFIRMATION: + rose_disconnect(sk, 0, -1, -1); + rose->neighbour->use--; + break; - default: - break; + default: + break; } return 0; @@ -123,50 +110,68 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety int queued = 0; switch (frametype) { + case ROSE_RESET_REQUEST: + rose_stop_timer(sk); + rose_start_idletimer(sk); + rose_write_internal(sk, ROSE_RESET_CONFIRMATION); + rose->condition = 0x00; + rose->vs = 0; + rose->vr = 0; + rose->va = 0; + rose->vl = 0; + rose_requeue_frames(sk); + break; + + case ROSE_CLEAR_REQUEST: + rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + rose->neighbour->use--; + break; - case ROSE_RESET_REQUEST: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose_write_internal(sk, ROSE_RESET_CONFIRMATION); + case ROSE_RR: + case ROSE_RNR: + if (!rose_validate_nr(sk, nr)) { + rose_write_internal(sk, ROSE_RESET_REQUEST); rose->condition = 0x00; rose->vs = 0; rose->vr = 0; rose->va = 0; rose->vl = 0; - rose_requeue_frames(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose->neighbour->use--; - break; - - case ROSE_RR: - case ROSE_RNR: - if (!rose_validate_nr(sk, nr)) { - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); + rose->state = ROSE_STATE_4; + rose_start_t2timer(sk); + rose_stop_idletimer(sk); + } else { + rose_frames_acked(sk, nr); + if (frametype == ROSE_RNR) { + rose->condition |= ROSE_COND_PEER_RX_BUSY; } else { - rose_frames_acked(sk, nr); - if (frametype == ROSE_RNR) { - rose->condition |= ROSE_COND_PEER_RX_BUSY; - } else { - rose->condition &= ~ROSE_COND_PEER_RX_BUSY; - } + rose->condition &= ~ROSE_COND_PEER_RX_BUSY; } - break; + } + break; - case ROSE_DATA: /* XXX */ - rose->condition &= ~ROSE_COND_PEER_RX_BUSY; - if (!rose_validate_nr(sk, nr)) { + case ROSE_DATA: /* XXX */ + rose->condition &= ~ROSE_COND_PEER_RX_BUSY; + if (!rose_validate_nr(sk, nr)) { + rose_write_internal(sk, ROSE_RESET_REQUEST); + rose->condition = 0x00; + rose->vs = 0; + rose->vr = 0; + rose->va = 0; + rose->vl = 0; + rose->state = ROSE_STATE_4; + rose_start_t2timer(sk); + rose_stop_idletimer(sk); + break; + } + rose_frames_acked(sk, nr); + if (ns == rose->vr) { + rose_start_idletimer(sk); + if (sock_queue_rcv_skb(sk, skb) == 0) { + rose->vr = (rose->vr + 1) % ROSE_MODULUS; + queued = 1; + } else { + /* Should never happen ! */ rose_write_internal(sk, ROSE_RESET_REQUEST); rose->condition = 0x00; rose->vs = 0; @@ -178,45 +183,26 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety rose_stop_idletimer(sk); break; } - rose_frames_acked(sk, nr); - if (ns == rose->vr) { - rose_start_idletimer(sk); - if (sock_queue_rcv_skb(sk, skb) == 0) { - rose->vr = (rose->vr + 1) % ROSE_MODULUS; - queued = 1; - } else { - /* Should never happen ! */ - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); - break; - } - if (atomic_read(&sk->rmem_alloc) > (sk->rcvbuf / 2)) - rose->condition |= ROSE_COND_OWN_RX_BUSY; - } - /* - * If the window is full, ack the frame, else start the - * acknowledge hold back timer. - */ - if (((rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == rose->vr) { - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_stop_timer(sk); - rose_enquiry_response(sk); - } else { - rose->condition |= ROSE_COND_ACK_PENDING; - rose_start_hbtimer(sk); - } - break; - - default: - printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype); - break; + if (atomic_read(&sk->rmem_alloc) > (sk->rcvbuf / 2)) + rose->condition |= ROSE_COND_OWN_RX_BUSY; + } + /* + * If the window is full, ack the frame, else start the + * acknowledge hold back timer. + */ + if (((rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == rose->vr) { + rose->condition &= ~ROSE_COND_ACK_PENDING; + rose_stop_timer(sk); + rose_enquiry_response(sk); + } else { + rose->condition |= ROSE_COND_ACK_PENDING; + rose_start_hbtimer(sk); + } + break; + + default: + printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype); + break; } return queued; @@ -232,29 +218,28 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety rose_cb *rose = rose_sk(sk); switch (frametype) { + case ROSE_RESET_REQUEST: + rose_write_internal(sk, ROSE_RESET_CONFIRMATION); + case ROSE_RESET_CONFIRMATION: + rose_stop_timer(sk); + rose_start_idletimer(sk); + rose->condition = 0x00; + rose->va = 0; + rose->vr = 0; + rose->vs = 0; + rose->vl = 0; + rose->state = ROSE_STATE_3; + rose_requeue_frames(sk); + break; + + case ROSE_CLEAR_REQUEST: + rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + rose->neighbour->use--; + break; - case ROSE_RESET_REQUEST: - rose_write_internal(sk, ROSE_RESET_CONFIRMATION); - case ROSE_RESET_CONFIRMATION: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->va = 0; - rose->vr = 0; - rose->vs = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - rose_requeue_frames(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose->neighbour->use--; - break; - - default: - break; + default: + break; } return 0; @@ -288,21 +273,21 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) frametype = rose_decode(skb, &ns, &nr, &q, &d, &m); switch (rose->state) { - case ROSE_STATE_1: - queued = rose_state1_machine(sk, skb, frametype); - break; - case ROSE_STATE_2: - queued = rose_state2_machine(sk, skb, frametype); - break; - case ROSE_STATE_3: - queued = rose_state3_machine(sk, skb, frametype, ns, nr, q, d, m); - break; - case ROSE_STATE_4: - queued = rose_state4_machine(sk, skb, frametype); - break; - case ROSE_STATE_5: - queued = rose_state5_machine(sk, skb, frametype); - break; + case ROSE_STATE_1: + queued = rose_state1_machine(sk, skb, frametype); + break; + case ROSE_STATE_2: + queued = rose_state2_machine(sk, skb, frametype); + break; + case ROSE_STATE_3: + queued = rose_state3_machine(sk, skb, frametype, ns, nr, q, d, m); + break; + case ROSE_STATE_4: + queued = rose_state4_machine(sk, skb, frametype); + break; + case ROSE_STATE_5: + queued = rose_state5_machine(sk, skb, frametype); + break; } rose_kick(sk); diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 3935526edc3a..8974778d70d7 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -1,19 +1,11 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * ROSE 001 Jonathan(G4KLX) Cloned from rose_timer.c - * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -142,25 +134,25 @@ void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigne struct sk_buff *skbn; switch (frametype) { - case ROSE_RESTART_REQUEST: - rose_stop_t0timer(neigh); - neigh->restarted = 1; - neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED); - rose_transmit_restart_confirmation(neigh); - break; - - case ROSE_RESTART_CONFIRMATION: - rose_stop_t0timer(neigh); - neigh->restarted = 1; - break; - - case ROSE_DIAGNOSTIC: - printk(KERN_WARNING "ROSE: received diagnostic #%d - %02X %02X %02X\n", skb->data[3], skb->data[4], skb->data[5], skb->data[6]); - break; - - default: - printk(KERN_WARNING "ROSE: received unknown %02X with LCI 000\n", frametype); - break; + case ROSE_RESTART_REQUEST: + rose_stop_t0timer(neigh); + neigh->restarted = 1; + neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED); + rose_transmit_restart_confirmation(neigh); + break; + + case ROSE_RESTART_CONFIRMATION: + rose_stop_t0timer(neigh); + neigh->restarted = 1; + break; + + case ROSE_DIAGNOSTIC: + printk(KERN_WARNING "ROSE: received diagnostic #%d - %02X %02X %02X\n", skb->data[3], skb->data[4], skb->data[5], skb->data[6]); + break; + + default: + printk(KERN_WARNING "ROSE: received unknown %02X with LCI 000\n", frametype); + break; } if (neigh->restarted) { diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c index ad00608a3911..103b4d38f88a 100644 --- a/net/rose/rose_loopback.c +++ b/net/rose/rose_loopback.c @@ -1,19 +1,11 @@ /* - * ROSE release 003 - * - * 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 - * ROSE 003 Jonathan(G4KLX) Created this file from nr_loopback.c. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/types.h> #include <linux/socket.h> #include <linux/timer.h> diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c index 5f7f7885203a..3583963560ed 100644 --- a/net/rose/rose_out.c +++ b/net/rose/rose_out.c @@ -1,20 +1,11 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * ROSE 001 Jonathan(G4KLX) Cloned from nr_out.c - * ROSE 003 Jonathan(G4KLX) New timer architecture. - * Removed M bit processing. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -36,7 +27,7 @@ #include <linux/interrupt.h> #include <net/rose.h> -/* +/* * 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. */ @@ -52,7 +43,7 @@ static void rose_send_iframe(struct sock *sk, struct sk_buff *skb) rose_start_idletimer(sk); - rose_transmit_link(skb, rose->neighbour); + rose_transmit_link(skb, rose->neighbour); } void rose_kick(struct sock *sk) diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index e004ea29a9b7..f96ccecfcd81 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -1,24 +1,12 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * ROSE 001 Jonathan(G4KLX) Cloned from nr_route.c. - * Terry(VK2KTJ) Added support for variable length - * address masks. - * ROSE 002 Jonathan(G4KLX) Uprated through routing of packets. - * Routing loop detection. - * ROSE 003 Jonathan(G4KLX) New timer architecture. - * Added use count to neighbours. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -51,8 +39,11 @@ static unsigned int rose_neigh_no = 1; static struct rose_node *rose_node_list; +static spinlock_t rose_node_list_lock = SPIN_LOCK_UNLOCKED; static struct rose_neigh *rose_neigh_list; +static spinlock_t rose_neigh_list_lock = SPIN_LOCK_UNLOCKED; static struct rose_route *rose_route_list; +static spinlock_t rose_route_list_lock = SPIN_LOCK_UNLOCKED; struct rose_neigh *rose_loopback_neigh; @@ -62,27 +53,44 @@ static void rose_remove_neigh(struct rose_neigh *); * Add a new route to a node, and in the process add the node and the * neighbour if it is new. */ -static int rose_add_node(struct rose_route_struct *rose_route, struct net_device *dev) +static int rose_add_node(struct rose_route_struct *rose_route, + struct net_device *dev) { struct rose_node *rose_node, *rose_tmpn, *rose_tmpp; struct rose_neigh *rose_neigh; - unsigned long flags; - int i; + int i, res = 0; + + spin_lock_bh(&rose_node_list_lock); + spin_lock_bh(&rose_neigh_list_lock); - for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) - if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0)) + rose_node = rose_node_list; + while (rose_node != NULL) { + if ((rose_node->mask == rose_route->mask) && + (rosecmpm(&rose_route->address, &rose_node->address, + rose_route->mask) == 0)) break; + rose_node = rose_node->next; + } - if (rose_node != NULL && rose_node->loopback) - return -EINVAL; + if (rose_node != NULL && rose_node->loopback) { + res = -EINVAL; + goto out; + } - 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) + rose_neigh = rose_neigh_list; + while (rose_neigh != NULL) { + if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 + && rose_neigh->dev == dev) break; + rose_neigh = rose_neigh->next; + } if (rose_neigh == NULL) { - if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL) - return -ENOMEM; + rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC); + if (rose_neigh == NULL) { + res = -ENOMEM; + goto out; + } rose_neigh->callsign = rose_route->neighbour; rose_neigh->digipeat = NULL; @@ -103,22 +111,22 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device if (rose_route->ndigis != 0) { if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { kfree(rose_neigh); - return -ENOMEM; + res = -ENOMEM; + goto out; } rose_neigh->digipeat->ndigi = rose_route->ndigis; rose_neigh->digipeat->lastrepeat = -1; for (i = 0; i < rose_route->ndigis; i++) { - rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; + rose_neigh->digipeat->calls[i] = + rose_route->digipeaters[i]; rose_neigh->digipeat->repeated[i] = 0; } } - save_flags(flags); cli(); rose_neigh->next = rose_neigh_list; rose_neigh_list = rose_neigh; - restore_flags(flags); } /* @@ -142,8 +150,11 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device } /* create new node */ - if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) - return -ENOMEM; + rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC); + if (rose_node == NULL) { + res = -ENOMEM; + goto out; + } rose_node->address = rose_route->address; rose_node->mask = rose_route->mask; @@ -151,8 +162,6 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device rose_node->loopback = 0; rose_node->neighbour[0] = rose_neigh; - save_flags(flags); cli(); - if (rose_tmpn == NULL) { if (rose_tmpp == NULL) { /* Empty list */ rose_node_list = rose_node; @@ -170,12 +179,9 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device rose_node->next = rose_tmpn; } } - - restore_flags(flags); - rose_neigh->count++; - return 0; + goto out; } /* We have space, slot it in */ @@ -185,20 +191,23 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device rose_neigh->count++; } - return 0; +out: + spin_unlock_bh(&rose_neigh_list_lock); + spin_unlock_bh(&rose_node_list_lock); + + return res; } +/* + * Caller is holding rose_node_list_lock. + */ static void rose_remove_node(struct rose_node *rose_node) { struct rose_node *s; - unsigned long flags; - - save_flags(flags); - cli(); + spin_lock_bh(&rose_node_list_lock); if ((s = rose_node_list) == rose_node) { rose_node_list = rose_node->next; - restore_flags(flags); kfree(rose_node); return; } @@ -206,32 +215,31 @@ static void rose_remove_node(struct rose_node *rose_node) while (s != NULL && s->next != NULL) { if (s->next == rose_node) { s->next = rose_node->next; - restore_flags(flags); kfree(rose_node); return; } s = s->next; } - - restore_flags(flags); } +/* + * Caller is holding rose_neigh_list_lock. + */ static void rose_remove_neigh(struct rose_neigh *rose_neigh) { struct rose_neigh *s; - unsigned long flags; rose_stop_ftimer(rose_neigh); rose_stop_t0timer(rose_neigh); skb_queue_purge(&rose_neigh->queue); - save_flags(flags); cli(); + spin_lock_bh(&rose_neigh_list_lock); if ((s = rose_neigh_list) == rose_neigh) { rose_neigh_list = rose_neigh->next; - restore_flags(flags); + spin_unlock_bh(&rose_neigh_list_lock); if (rose_neigh->digipeat != NULL) kfree(rose_neigh->digipeat); kfree(rose_neigh); @@ -241,7 +249,7 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) while (s != NULL && s->next != NULL) { if (s->next == rose_neigh) { s->next = rose_neigh->next; - restore_flags(flags); + spin_unlock_bh(&rose_neigh_list_lock); if (rose_neigh->digipeat != NULL) kfree(rose_neigh->digipeat); kfree(rose_neigh); @@ -250,14 +258,15 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) s = s->next; } - - restore_flags(flags); + spin_unlock_bh(&rose_neigh_list_lock); } +/* + * Caller is holding rose_route_list_lock. + */ static void rose_remove_route(struct rose_route *rose_route) { struct rose_route *s; - unsigned long flags; if (rose_route->neigh1 != NULL) rose_route->neigh1->use--; @@ -265,11 +274,8 @@ static void rose_remove_route(struct rose_route *rose_route) if (rose_route->neigh2 != NULL) rose_route->neigh2->use--; - save_flags(flags); cli(); - if ((s = rose_route_list) == rose_route) { rose_route_list = rose_route->next; - restore_flags(flags); kfree(rose_route); return; } @@ -277,40 +283,54 @@ static void rose_remove_route(struct rose_route *rose_route) while (s != NULL && s->next != NULL) { if (s->next == rose_route) { s->next = rose_route->next; - restore_flags(flags); kfree(rose_route); return; } s = s->next; } - - restore_flags(flags); } /* * "Delete" a node. Strictly speaking remove a route to a node. The node * is only deleted if no routes are left to it. */ -static int rose_del_node(struct rose_route_struct *rose_route, struct net_device *dev) +static int rose_del_node(struct rose_route_struct *rose_route, + struct net_device *dev) { struct rose_node *rose_node; struct rose_neigh *rose_neigh; - int i; + int i, err = 0; - for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) - if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0)) - break; + spin_lock_bh(&rose_node_list_lock); + spin_lock_bh(&rose_neigh_list_lock); - if (rose_node == NULL) return -EINVAL; + rose_node = rose_node_list; + while (rose_node != NULL) { + if ((rose_node->mask == rose_route->mask) && + (rosecmpm(&rose_route->address, &rose_node->address, + rose_route->mask) == 0)) + break; + rose_node = rose_node->next; + } - if (rose_node->loopback) return -EINVAL; + if (rose_node == NULL || rose_node->loopback) { + err = -EINVAL; + goto out; + } - 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) + rose_neigh = rose_neigh_list; + while (rose_neigh != NULL) { + if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 + && rose_neigh->dev == dev) break; + rose_neigh = rose_neigh->next; + } - if (rose_neigh == NULL) return -EINVAL; + if (rose_neigh == NULL) { + err = -EINVAL; + goto out; + } for (i = 0; i < rose_node->count; i++) { if (rose_node->neighbour[i] == rose_neigh) { @@ -325,20 +345,26 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device rose_remove_node(rose_node); } else { switch (i) { - case 0: - rose_node->neighbour[0] = rose_node->neighbour[1]; - case 1: - rose_node->neighbour[1] = rose_node->neighbour[2]; - case 2: - break; + case 0: + rose_node->neighbour[0] = + rose_node->neighbour[1]; + case 1: + rose_node->neighbour[1] = + rose_node->neighbour[2]; + case 2: + break; } } - - return 0; + goto out; } } + err = -EINVAL; + +out: + spin_unlock_bh(&rose_neigh_list_lock); + spin_unlock_bh(&rose_node_list_lock); - return -EINVAL; + return err; } /* @@ -346,8 +372,6 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device */ int rose_add_loopback_neigh(void) { - unsigned long flags; - if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; @@ -367,10 +391,10 @@ int rose_add_loopback_neigh(void) init_timer(&rose_loopback_neigh->ftimer); init_timer(&rose_loopback_neigh->t0timer); - save_flags(flags); cli(); + spin_lock_bh(&rose_neigh_list_lock); rose_loopback_neigh->next = rose_neigh_list; rose_neigh_list = rose_loopback_neigh; - restore_flags(flags); + spin_unlock_bh(&rose_neigh_list_lock); return 0; } @@ -381,16 +405,26 @@ int rose_add_loopback_neigh(void) int rose_add_loopback_node(rose_address *address) { struct rose_node *rose_node; - unsigned long flags; + unsigned int err = 0; - for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) - if ((rose_node->mask == 10) && (rosecmpm(address, &rose_node->address, 10) == 0) && rose_node->loopback) + spin_lock_bh(&rose_node_list_lock); + + rose_node = rose_node_list; + while (rose_node != NULL) { + if ((rose_node->mask == 10) && + (rosecmpm(address, &rose_node->address, 10) == 0) && + rose_node->loopback) break; + rose_node = rose_node->next; + } - if (rose_node != NULL) return 0; - - if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) - return -ENOMEM; + if (rose_node != NULL) + goto out; + + if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) { + err = -ENOMEM; + goto out; + } rose_node->address = *address; rose_node->mask = 10; @@ -399,13 +433,14 @@ int rose_add_loopback_node(rose_address *address) rose_node->neighbour[0] = rose_loopback_neigh; /* Insert at the head of list. Address is always mask=10 */ - save_flags(flags); cli(); rose_node->next = rose_node_list; rose_node_list = rose_node; - restore_flags(flags); rose_loopback_neigh->count++; +out: + spin_unlock_bh(&rose_node_list_lock); + return 0; } @@ -416,15 +451,26 @@ void rose_del_loopback_node(rose_address *address) { struct rose_node *rose_node; - for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) - if ((rose_node->mask == 10) && (rosecmpm(address, &rose_node->address, 10) == 0) && rose_node->loopback) + spin_lock_bh(&rose_node_list_lock); + + rose_node = rose_node_list; + while (rose_node != NULL) { + if ((rose_node->mask == 10) && + (rosecmpm(address, &rose_node->address, 10) == 0) && + rose_node->loopback) break; + rose_node = rose_node->next; + } - if (rose_node == NULL) return; + if (rose_node == NULL) + goto out; rose_remove_node(rose_node); rose_loopback_neigh->count--; + +out: + spin_unlock_bh(&rose_node_list_lock); } /* @@ -432,52 +478,62 @@ void rose_del_loopback_node(rose_address *address) */ void rose_rt_device_down(struct net_device *dev) { - struct rose_neigh *s, *rose_neigh = rose_neigh_list; + struct rose_neigh *s, *rose_neigh; struct rose_node *t, *rose_node; int i; + spin_lock_bh(&rose_node_list_lock); + spin_lock_bh(&rose_neigh_list_lock); + rose_neigh = rose_neigh_list; while (rose_neigh != NULL) { s = rose_neigh; rose_neigh = rose_neigh->next; - if (s->dev == dev) { - rose_node = rose_node_list; - - while (rose_node != NULL) { - t = rose_node; - rose_node = rose_node->next; - - for (i = 0; i < t->count; i++) { - if (t->neighbour[i] == s) { - t->count--; - - switch (i) { - case 0: - t->neighbour[0] = t->neighbour[1]; - case 1: - t->neighbour[1] = t->neighbour[2]; - case 2: - break; - } - } - } + if (s->dev != dev) + continue; + + rose_node = rose_node_list; + + while (rose_node != NULL) { + t = rose_node; + rose_node = rose_node->next; + + for (i = 0; i < t->count; i++) { + if (t->neighbour[i] != s) + continue; + + t->count--; - if (t->count <= 0) - rose_remove_node(t); + switch (i) { + case 0: + t->neighbour[0] = t->neighbour[1]; + case 1: + t->neighbour[1] = t->neighbour[2]; + case 2: + break; + } } - rose_remove_neigh(s); + if (t->count <= 0) + rose_remove_node(t); } + + rose_remove_neigh(s); } + spin_unlock_bh(&rose_neigh_list_lock); + spin_unlock_bh(&rose_node_list_lock); } +#if 0 /* Currently unused */ /* * A device has been removed. Remove its links. */ void rose_route_device_down(struct net_device *dev) { - struct rose_route *s, *rose_route = rose_route_list; + struct rose_route *s, *rose_route; + spin_lock_bh(&rose_route_list_lock); + rose_route = rose_route_list; while (rose_route != NULL) { s = rose_route; rose_route = rose_route->next; @@ -485,7 +541,9 @@ void rose_route_device_down(struct net_device *dev) if (s->neigh1->dev == dev || s->neigh2->dev == dev) rose_remove_route(s); } + spin_unlock_bh(&rose_route_list_lock); } +#endif /* * Clear all nodes and neighbours out, except for neighbours with @@ -494,8 +552,14 @@ void rose_route_device_down(struct net_device *dev) */ static int rose_clear_routes(void) { - struct rose_neigh *s, *rose_neigh = rose_neigh_list; - struct rose_node *t, *rose_node = rose_node_list; + struct rose_neigh *s, *rose_neigh; + struct rose_node *t, *rose_node; + + spin_lock_bh(&rose_node_list_lock); + spin_lock_bh(&rose_neigh_list_lock); + + rose_neigh = rose_neigh_list; + rose_node = rose_node_list; while (rose_node != NULL) { t = rose_node; @@ -514,6 +578,9 @@ static int rose_clear_routes(void) } } + spin_unlock_bh(&rose_neigh_list_lock); + spin_unlock_bh(&rose_node_list_lock); + return 0; } @@ -603,18 +670,22 @@ struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neig /* * Find a neighbour given a ROSE address. */ -struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic) +struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, + unsigned char *diagnostic) { + struct rose_neigh *res = NULL; struct rose_node *node; int failed = 0; int i; + spin_lock_bh(&rose_node_list_lock); for (node = rose_node_list; node != NULL; node = node->next) { if (rosecmpm(addr, &node->address, node->mask) == 0) { for (i = 0; i < node->count; i++) { if (!rose_ftimer_running(node->neighbour[i])) { - return node->neighbour[i]; } - else + res = node->neighbour[i]; + goto out; + } else failed = 1; } break; @@ -629,7 +700,10 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsi *diagnostic = 0; } - return NULL; +out: + spin_unlock_bh(&rose_node_list_lock); + + return res; } /* @@ -642,38 +716,36 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) int err; switch (cmd) { - - case SIOCADDRT: - if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) - return -EFAULT; - if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) - return -EINVAL; - if (rose_dev_exists(&rose_route.address)) { /* Can't add routes to ourself */ - dev_put(dev); - return -EINVAL; - } - if (rose_route.mask > 10) /* Mask can't be more than 10 digits */ - return -EINVAL; - - err = rose_add_node(&rose_route, dev); - dev_put(dev); - return err; - - case SIOCDELRT: - if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) - return -EFAULT; - if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) - return -EINVAL; - err = rose_del_node(&rose_route, dev); + case SIOCADDRT: + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; + if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) + return -EINVAL; + if (rose_dev_exists(&rose_route.address)) { /* Can't add routes to ourself */ dev_put(dev); - return err; - + return -EINVAL; + } + if (rose_route.mask > 10) /* Mask can't be more than 10 digits */ + return -EINVAL; - case SIOCRSCLRRT: - return rose_clear_routes(); + err = rose_add_node(&rose_route, dev); + dev_put(dev); + return err; - default: + case SIOCDELRT: + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; + if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; + err = rose_del_node(&rose_route, dev); + dev_put(dev); + return err; + + case SIOCRSCLRRT: + return rose_clear_routes(); + + default: + return -EINVAL; } return 0; @@ -690,6 +762,8 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) skb_queue_purge(&rose_neigh->queue); + spin_lock_bh(&rose_route_list_lock); + rose_route = rose_route_list; while (rose_route != NULL) { @@ -716,6 +790,7 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) rose_route = rose_route->next; } + spin_unlock_bh(&rose_route_list_lock); } /* @@ -727,16 +802,21 @@ void rose_link_failed(ax25_cb *ax25, int reason) { struct rose_neigh *rose_neigh; - for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) + spin_lock_bh(&rose_neigh_list_lock); + rose_neigh = rose_neigh_list; + while (rose_neigh != NULL) { if (rose_neigh->ax25 == ax25) break; + rose_neigh = rose_neigh->next; + } - if (rose_neigh == NULL) return; - - rose_neigh->ax25 = NULL; + if (rose_neigh != NULL) { + rose_neigh->ax25 = NULL; - rose_del_route_by_neigh(rose_neigh); - rose_kill_by_neigh(rose_neigh); + rose_del_route_by_neigh(rose_neigh); + rose_kill_by_neigh(rose_neigh); + } + spin_unlock_bh(&rose_neigh_list_lock); } /* @@ -769,12 +849,11 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) unsigned int lci, new_lci; unsigned char cause, diagnostic; struct net_device *dev; - unsigned long flags; - int len; + int len, res = 0; #if 0 if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) - return 0; + return res; #endif frametype = skb->data[2]; @@ -782,13 +861,22 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) src_addr = (rose_address *)(skb->data + 9); 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) + spin_lock_bh(&rose_node_list_lock); + spin_lock_bh(&rose_neigh_list_lock); + spin_lock_bh(&rose_route_list_lock); + + rose_neigh = rose_neigh_list; + while (rose_neigh != NULL) { + if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && + ax25->ax25_dev->dev == rose_neigh->dev) break; + rose_neigh = rose_neigh->next; + } if (rose_neigh == NULL) { - printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr)); - return 0; + printk("rose_route : unknown neighbour or device %s\n", + ax2asc(&ax25->dest_addr)); + goto out; } /* @@ -802,7 +890,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) */ if (lci == 0) { rose_link_rx_restart(skb, rose_neigh, frametype); - return 0; + goto out; } /* @@ -828,7 +916,8 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) } else { skb->h.raw = skb->data; - return rose_process_rx_frame(sk, skb); + res = rose_process_rx_frame(sk, skb); + goto out; } } @@ -837,21 +926,23 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) */ if (frametype == ROSE_CALL_REQUEST) if ((dev = rose_dev_get(dest_addr)) != NULL) { - int err = rose_rx_call_request(skb, dev, rose_neigh, lci); + res = rose_rx_call_request(skb, dev, rose_neigh, lci); dev_put(dev); - return err; + goto out; } if (!sysctl_rose_routing_control) { rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); - return 0; + goto out; } /* * Route it to the next in line if we have an entry for it. */ - for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) { - if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) { + rose_route = rose_route_list; + while (rose_route != NULL) { + if (rose_route->lci1 == lci && + rose_route->neigh1 == rose_neigh) { if (frametype == ROSE_CALL_REQUEST) { /* F6FBB - Remove an existing unused route */ rose_remove_route(rose_route); @@ -863,14 +954,16 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) rose_transmit_link(skb, rose_route->neigh2); if (frametype == ROSE_CLEAR_CONFIRMATION) rose_remove_route(rose_route); - return 1; + res = 1; + goto out; } else { if (frametype == ROSE_CLEAR_CONFIRMATION) rose_remove_route(rose_route); - return 0; + goto out; } } - if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) { + if (rose_route->lci2 == lci && + rose_route->neigh2 == rose_neigh) { if (frametype == ROSE_CALL_REQUEST) { /* F6FBB - Remove an existing unused route */ rose_remove_route(rose_route); @@ -882,13 +975,15 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) rose_transmit_link(skb, rose_route->neigh1); if (frametype == ROSE_CLEAR_CONFIRMATION) rose_remove_route(rose_route); - return 1; + res = 1; + goto out; } else { if (frametype == ROSE_CLEAR_CONFIRMATION) rose_remove_route(rose_route); - return 0; + goto out; } } + rose_route = rose_route->next; } /* @@ -903,38 +998,40 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2; memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); - + if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); - return 0; + goto out; } /* * Check for routing loops. */ - for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) { + rose_route = rose_route_list; + while (rose_route != NULL) { if (rose_route->rand == facilities.rand && rosecmp(src_addr, &rose_route->src_addr) == 0 && ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 && ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); - return 0; + goto out; } + rose_route = rose_route->next; } if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) { rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); - return 0; + goto out; } if ((new_lci = rose_new_lci(new_neigh)) == 0) { rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); - return 0; + goto out; } if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) { rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); - return 0; + goto out; } rose_route->lci1 = lci; @@ -950,18 +1047,22 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) rose_route->neigh1->use++; rose_route->neigh2->use++; - save_flags(flags); cli(); rose_route->next = rose_route_list; rose_route_list = rose_route; - restore_flags(flags); skb->data[0] &= 0xF0; skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; rose_transmit_link(skb, rose_route->neigh2); + res = 1; + +out: + spin_unlock_bh(&rose_route_list_lock); + spin_unlock_bh(&rose_neigh_list_lock); + spin_unlock_bh(&rose_node_list_lock); - return 1; + return res; } int rose_nodes_get_info(char *buffer, char **start, off_t offset, int length) @@ -972,7 +1073,7 @@ int rose_nodes_get_info(char *buffer, char **start, off_t offset, int length) off_t begin = 0; int i; - cli(); + spin_lock_bh(&rose_neigh_list_lock); len += sprintf(buffer, "address mask n neigh neigh neigh\n"); @@ -1004,16 +1105,16 @@ int rose_nodes_get_info(char *buffer, char **start, off_t offset, int length) if (pos > offset + length) break; } - - sti(); + spin_unlock_bh(&rose_neigh_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); - if (len > length) len = length; + if (len > length) + len = length; return len; -} +} int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) { @@ -1023,7 +1124,7 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) off_t begin = 0; int i; - cli(); + spin_lock_bh(&rose_neigh_list_lock); len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n"); @@ -1059,15 +1160,16 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) /* } */ } - sti(); + spin_unlock_bh(&rose_neigh_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); - if (len > length) len = length; + if (len > length) + len = length; return len; -} +} int rose_routes_get_info(char *buffer, char **start, off_t offset, int length) { @@ -1076,7 +1178,7 @@ int rose_routes_get_info(char *buffer, char **start, off_t offset, int length) off_t pos = 0; off_t begin = 0; - cli(); + spin_lock_bh(&rose_route_list_lock); len += sprintf(buffer, "lci address callsign neigh <-> lci address callsign neigh\n"); @@ -1112,15 +1214,16 @@ int rose_routes_get_info(char *buffer, char **start, off_t offset, int length) break; } - sti(); + spin_unlock_bh(&rose_route_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); - if (len > length) len = length; + if (len > length) + len = length; return len; -} +} /* * Release all memory associated with ROSE routing structures. diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 08b21f2fa39e..2653d4899499 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -1,20 +1,11 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * ROSE 001 Jonathan(G4KLX) Cloned from nr_subr.c - * ROSE 002 Jonathan(G4KLX) Centralised disconnect processing. - * ROSE 003 Jonathan(G4KLX) Added use count to neighbours. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -103,7 +94,7 @@ int rose_validate_nr(struct sock *sk, unsigned short nr) return nr == rose->vs; } -/* +/* * This routine is called when the packet layer internally generates a * control frame. */ @@ -119,16 +110,16 @@ void rose_write_internal(struct sock *sk, int frametype) len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1; switch (frametype) { - case ROSE_CALL_REQUEST: - len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN; - faclen = rose_create_facilities(buffer, rose); - len += faclen; - break; - case ROSE_CALL_ACCEPTED: - case ROSE_CLEAR_REQUEST: - case ROSE_RESET_REQUEST: - len += 2; - break; + case ROSE_CALL_REQUEST: + len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN; + faclen = rose_create_facilities(buffer, rose); + len += faclen; + break; + case ROSE_CALL_ACCEPTED: + case ROSE_CLEAR_REQUEST: + case ROSE_RESET_REQUEST: + len += 2; + break; } if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) @@ -138,70 +129,69 @@ 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); - + dptr = skb_put(skb, skb_tailroom(skb)); lci1 = (rose->lci >> 8) & 0x0F; lci2 = (rose->lci >> 0) & 0xFF; switch (frametype) { - - case ROSE_CALL_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = 0xAA; - memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN); - dptr += ROSE_ADDR_LEN; - memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); - dptr += ROSE_ADDR_LEN; - memcpy(dptr, buffer, faclen); - dptr += faclen; - break; - - case ROSE_CALL_ACCEPTED: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = 0x00; /* Address length */ - *dptr++ = 0; /* Facilities length */ - break; - - case ROSE_CLEAR_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = rose->cause; - *dptr++ = rose->diagnostic; - break; - - case ROSE_RESET_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = ROSE_DTE_ORIGINATED; - *dptr++ = 0; - break; - - case ROSE_RR: - case ROSE_RNR: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr = frametype; - *dptr++ |= (rose->vr << 5) & 0xE0; - break; - - case ROSE_CLEAR_CONFIRMATION: - case ROSE_RESET_CONFIRMATION: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - break; - - default: - printk(KERN_ERR "ROSE: rose_write_internal - invalid frametype %02X\n", frametype); - kfree_skb(skb); - return; + case ROSE_CALL_REQUEST: + *dptr++ = ROSE_GFI | lci1; + *dptr++ = lci2; + *dptr++ = frametype; + *dptr++ = 0xAA; + memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN); + dptr += ROSE_ADDR_LEN; + memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); + dptr += ROSE_ADDR_LEN; + memcpy(dptr, buffer, faclen); + dptr += faclen; + break; + + case ROSE_CALL_ACCEPTED: + *dptr++ = ROSE_GFI | lci1; + *dptr++ = lci2; + *dptr++ = frametype; + *dptr++ = 0x00; /* Address length */ + *dptr++ = 0; /* Facilities length */ + break; + + case ROSE_CLEAR_REQUEST: + *dptr++ = ROSE_GFI | lci1; + *dptr++ = lci2; + *dptr++ = frametype; + *dptr++ = rose->cause; + *dptr++ = rose->diagnostic; + break; + + case ROSE_RESET_REQUEST: + *dptr++ = ROSE_GFI | lci1; + *dptr++ = lci2; + *dptr++ = frametype; + *dptr++ = ROSE_DTE_ORIGINATED; + *dptr++ = 0; + break; + + case ROSE_RR: + case ROSE_RNR: + *dptr++ = ROSE_GFI | lci1; + *dptr++ = lci2; + *dptr = frametype; + *dptr++ |= (rose->vr << 5) & 0xE0; + break; + + case ROSE_CLEAR_CONFIRMATION: + case ROSE_RESET_CONFIRMATION: + *dptr++ = ROSE_GFI | lci1; + *dptr++ = lci2; + *dptr++ = frametype; + break; + + default: + printk(KERN_ERR "ROSE: rose_write_internal - invalid frametype %02X\n", frametype); + kfree_skb(skb); + return; } rose_transmit_link(skb, rose->neighbour); @@ -216,15 +206,15 @@ int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) *ns = *nr = *q = *d = *m = 0; switch (frame[2]) { - case ROSE_CALL_REQUEST: - case ROSE_CALL_ACCEPTED: - case ROSE_CLEAR_REQUEST: - case ROSE_CLEAR_CONFIRMATION: - case ROSE_RESET_REQUEST: - case ROSE_RESET_CONFIRMATION: - return frame[2]; - default: - break; + case ROSE_CALL_REQUEST: + case ROSE_CALL_ACCEPTED: + case ROSE_CLEAR_REQUEST: + case ROSE_CLEAR_CONFIRMATION: + case ROSE_RESET_REQUEST: + case ROSE_RESET_CONFIRMATION: + return frame[2]; + default: + break; } if ((frame[2] & 0x1F) == ROSE_RR || @@ -253,61 +243,61 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct * do { switch (*p & 0xC0) { - case 0x00: - p += 2; - n += 2; - len -= 2; - break; + case 0x00: + p += 2; + n += 2; + len -= 2; + break; - case 0x40: - if (*p == FAC_NATIONAL_RAND) - facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF); - p += 3; - n += 3; - len -= 3; - break; + case 0x40: + if (*p == FAC_NATIONAL_RAND) + facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF); + p += 3; + n += 3; + len -= 3; + break; - case 0x80: - p += 4; - n += 4; - len -= 4; - break; + case 0x80: + p += 4; + n += 4; + len -= 4; + break; - case 0xC0: - l = p[1]; - if (*p == FAC_NATIONAL_DEST_DIGI) { - if (!fac_national_digis_received) { - memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN); - facilities->source_ndigis = 1; - } + case 0xC0: + l = p[1]; + if (*p == FAC_NATIONAL_DEST_DIGI) { + if (!fac_national_digis_received) { + memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN); + facilities->source_ndigis = 1; } - else if (*p == FAC_NATIONAL_SRC_DIGI) { - if (!fac_national_digis_received) { - memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); - facilities->dest_ndigis = 1; - } - } - else if (*p == FAC_NATIONAL_FAIL_CALL) { - memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); - } - else if (*p == FAC_NATIONAL_FAIL_ADD) { - memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); + } + else if (*p == FAC_NATIONAL_SRC_DIGI) { + if (!fac_national_digis_received) { + memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); + facilities->dest_ndigis = 1; } - else if (*p == FAC_NATIONAL_DIGIS) { - fac_national_digis_received = 1; - facilities->source_ndigis = 0; - facilities->dest_ndigis = 0; - for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { - if (pt[6] & AX25_HBIT) - memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); - else - memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); - } + } + else if (*p == FAC_NATIONAL_FAIL_CALL) { + memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); + } + else if (*p == FAC_NATIONAL_FAIL_ADD) { + memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); + } + else if (*p == FAC_NATIONAL_DIGIS) { + fac_national_digis_received = 1; + facilities->source_ndigis = 0; + facilities->dest_ndigis = 0; + for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { + if (pt[6] & AX25_HBIT) + memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); + else + memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); } - p += l + 2; - n += l + 2; - len -= l + 2; - break; + } + p += l + 2; + n += l + 2; + len -= l + 2; + break; } } while (*p != 0x00 && len > 0); @@ -321,49 +311,50 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac do { switch (*p & 0xC0) { - case 0x00: - p += 2; - n += 2; - len -= 2; - break; + case 0x00: + p += 2; + n += 2; + len -= 2; + break; - case 0x40: - p += 3; - n += 3; - len -= 3; - break; + case 0x40: + p += 3; + n += 3; + len -= 3; + break; - case 0x80: - p += 4; - n += 4; - len -= 4; - break; + case 0x80: + p += 4; + n += 4; + len -= 4; + break; - case 0xC0: - l = p[1]; - if (*p == FAC_CCITT_DEST_NSAP) { - memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN); - memcpy(callsign, p + 12, l - 10); - callsign[l - 10] = '\0'; - facilities->source_call = *asc2ax(callsign); - } - if (*p == FAC_CCITT_SRC_NSAP) { - memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN); - memcpy(callsign, p + 12, l - 10); - callsign[l - 10] = '\0'; - facilities->dest_call = *asc2ax(callsign); - } - p += l + 2; - n += l + 2; - len -= l + 2; - break; + case 0xC0: + l = p[1]; + if (*p == FAC_CCITT_DEST_NSAP) { + memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN); + memcpy(callsign, p + 12, l - 10); + callsign[l - 10] = '\0'; + facilities->source_call = *asc2ax(callsign); + } + if (*p == FAC_CCITT_SRC_NSAP) { + memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN); + memcpy(callsign, p + 12, l - 10); + callsign[l - 10] = '\0'; + facilities->dest_call = *asc2ax(callsign); + } + p += l + 2; + n += l + 2; + len -= l + 2; + break; } } while (*p != 0x00 && len > 0); return n; } -int rose_parse_facilities(unsigned char *p, struct rose_facilities_struct *facilities) +int rose_parse_facilities(unsigned char *p, + struct rose_facilities_struct *facilities) { int facilities_len, len; @@ -378,26 +369,26 @@ int rose_parse_facilities(unsigned char *p, struct rose_facilities_struct *facil p++; switch (*p) { - case FAC_NATIONAL: /* National */ - len = rose_parse_national(p + 1, facilities, facilities_len - 1); - facilities_len -= len + 1; - p += len + 1; - break; + case FAC_NATIONAL: /* National */ + len = rose_parse_national(p + 1, facilities, facilities_len - 1); + facilities_len -= len + 1; + p += len + 1; + break; - case FAC_CCITT: /* CCITT */ - len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); - facilities_len -= len + 1; - p += len + 1; - break; + case FAC_CCITT: /* CCITT */ + len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); + facilities_len -= len + 1; + p += len + 1; + break; - default: - printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); - facilities_len--; - p++; - break; + default: + printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); + facilities_len--; + p++; + break; } - } - else break; /* Error in facilities format */ + } else + break; /* Error in facilities format */ } return 1; diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c index 4f66adbaf8a7..5af130c74541 100644 --- a/net/rose/rose_timer.c +++ b/net/rose/rose_timer.c @@ -1,20 +1,12 @@ /* - * ROSE release 003 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This 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 - * ROSE 001 Jonathan(G4KLX) Cloned from nr_timer.c - * ROSE 003 Jonathan(G4KLX) New timer architecture. - * Implemented idle timer. + * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) + * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) */ - #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -139,34 +131,35 @@ static void rose_heartbeat_expiry(unsigned long param) struct sock *sk = (struct sock *)param; rose_cb *rose = rose_sk(sk); + bh_lock_sock(sk); switch (rose->state) { - - case ROSE_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 (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - rose_destroy_socket(sk); - return; - } - break; - - case ROSE_STATE_3: - /* - * Check for the state of the receive buffer. - */ - if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && - (rose->condition & ROSE_COND_OWN_RX_BUSY)) { - rose->condition &= ~ROSE_COND_OWN_RX_BUSY; - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose->vl = rose->vr; - rose_write_internal(sk, ROSE_RR); - rose_stop_timer(sk); /* HB */ - break; - } + case ROSE_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 (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { + rose_destroy_socket(sk); + return; + } + break; + + case ROSE_STATE_3: + /* + * Check for the state of the receive buffer. + */ + if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) && + (rose->condition & ROSE_COND_OWN_RX_BUSY)) { + rose->condition &= ~ROSE_COND_OWN_RX_BUSY; + rose->condition &= ~ROSE_COND_ACK_PENDING; + rose->vl = rose->vr; + rose_write_internal(sk, ROSE_RR); + rose_stop_timer(sk); /* HB */ break; + } + break; } rose_start_heartbeat(sk); + bh_unlock_sock(sk); } static void rose_timer_expiry(unsigned long param) @@ -174,33 +167,35 @@ static void rose_timer_expiry(unsigned long param) struct sock *sk = (struct sock *)param; rose_cb *rose = rose_sk(sk); + bh_lock_sock(sk); switch (rose->state) { - - case ROSE_STATE_1: /* T1 */ - case ROSE_STATE_4: /* T2 */ - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose->state = ROSE_STATE_2; - rose_start_t3timer(sk); - break; - - case ROSE_STATE_2: /* T3 */ - rose->neighbour->use--; - rose_disconnect(sk, ETIMEDOUT, -1, -1); - break; - - case ROSE_STATE_3: /* HB */ - if (rose->condition & ROSE_COND_ACK_PENDING) { - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_enquiry_response(sk); - } - break; + case ROSE_STATE_1: /* T1 */ + case ROSE_STATE_4: /* T2 */ + rose_write_internal(sk, ROSE_CLEAR_REQUEST); + rose->state = ROSE_STATE_2; + rose_start_t3timer(sk); + break; + + case ROSE_STATE_2: /* T3 */ + rose->neighbour->use--; + rose_disconnect(sk, ETIMEDOUT, -1, -1); + break; + + case ROSE_STATE_3: /* HB */ + if (rose->condition & ROSE_COND_ACK_PENDING) { + rose->condition &= ~ROSE_COND_ACK_PENDING; + rose_enquiry_response(sk); + } + break; } + bh_unlock_sock(sk); } static void rose_idletimer_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; + bh_lock_sock(sk); rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_REQUEST); @@ -210,10 +205,11 @@ static void rose_idletimer_expiry(unsigned long param) sk->state = TCP_CLOSE; sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; + sk->shutdown |= SEND_SHUTDOWN; if (!sk->dead) sk->state_change(sk); sk->dead = 1; + bh_unlock_sock(sk); } diff --git a/net/rose/sysctl_net_rose.c b/net/rose/sysctl_net_rose.c index 8b5c89f83be2..7c309c10d49c 100644 --- a/net/rose/sysctl_net_rose.c +++ b/net/rose/sysctl_net_rose.c @@ -1,10 +1,11 @@ -/* -*- linux-c -*- - * sysctl_net_rose.c: sysctl interface to net ROSE subsystem. +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/rose directory entry (empty =) ). [MS] + * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) */ - #include <linux/mm.h> #include <linux/sysctl.h> #include <linux/init.h> |
