Commit b331768e authored by David S. Miller's avatar David S. Miller

Merge nuts.ninka.net:/home/davem/src/BK/ham-2.5

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents e65ca57c c2edf826
...@@ -240,8 +240,8 @@ L: linux-net@vger.kernel.org ...@@ -240,8 +240,8 @@ L: linux-net@vger.kernel.org
S: Maintained S: Maintained
AX.25 NETWORK LAYER AX.25 NETWORK LAYER
P: Matthias Welwarsky P: Ralf Baechle
M: dg2fef@afthd.tu-darmstadt.de M: ralf@linux-mips.org
L: linux-hams@vger.kernel.org L: linux-hams@vger.kernel.org
S: Maintained S: Maintained
...@@ -1121,8 +1121,8 @@ L: netfilter-devel@lists.netfilter.org ...@@ -1121,8 +1121,8 @@ L: netfilter-devel@lists.netfilter.org
S: Supported S: Supported
NETROM NETWORK LAYER NETROM NETWORK LAYER
P: Tomi Manninen P: Ralf Baechle
M: Tomi.Manninen@hut.fi M: ralf@linux-mips.org
L: linux-hams@vger.kernel.org L: linux-hams@vger.kernel.org
S: Maintained S: Maintained
...@@ -1371,8 +1371,8 @@ W: http://www.namesys.com ...@@ -1371,8 +1371,8 @@ W: http://www.namesys.com
S: Supported S: Supported
ROSE NETWORK LAYER ROSE NETWORK LAYER
P: Jean-Paul Roubelat P: Ralf Baechle
M: jpr@f6fbb.org M: ralf@linux-mips.org
L: linux-hams@vger.kernel.org L: linux-hams@vger.kernel.org
S: Maintained S: Maintained
......
...@@ -3,11 +3,14 @@ ...@@ -3,11 +3,14 @@
* *
* Alan Cox (GW4PTS) 10/11/93 * Alan Cox (GW4PTS) 10/11/93
*/ */
#ifndef _AX25_H #ifndef _AX25_H
#define _AX25_H #define _AX25_H
#include <linux/config.h> #include <linux/config.h>
#include <linux/ax25.h> #include <linux/ax25.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <asm/atomic.h>
#define AX25_T1CLAMPLO 1 #define AX25_T1CLAMPLO 1
#define AX25_T1CLAMPHI (30 * HZ) #define AX25_T1CLAMPHI (30 * HZ)
...@@ -66,6 +69,8 @@ ...@@ -66,6 +69,8 @@
#define AX25_UA 0x63 /* Unnumbered acknowledge */ #define AX25_UA 0x63 /* Unnumbered acknowledge */
#define AX25_FRMR 0x87 /* Frame reject */ #define AX25_FRMR 0x87 /* Frame reject */
#define AX25_UI 0x03 /* Unnumbered information */ #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_PF 0x10 /* Poll/final bit for standard AX.25 */
#define AX25_EPF 0x01 /* Poll/final bit for extended AX.25 */ #define AX25_EPF 0x01 /* Poll/final bit for extended AX.25 */
...@@ -147,10 +152,12 @@ typedef struct { ...@@ -147,10 +152,12 @@ typedef struct {
typedef struct ax25_route { typedef struct ax25_route {
struct ax25_route *next; struct ax25_route *next;
atomic_t ref;
ax25_address callsign; ax25_address callsign;
struct net_device *dev; struct net_device *dev;
ax25_digi *digipeat; ax25_digi *digipeat;
char ip_mode; char ip_mode;
struct timer_list timer;
} ax25_route; } ax25_route;
typedef struct { typedef struct {
...@@ -197,11 +204,12 @@ typedef struct ax25_cb { ...@@ -197,11 +204,12 @@ typedef struct ax25_cb {
#define ax25_sk(__sk) ((ax25_cb *)(__sk)->protinfo) #define ax25_sk(__sk) ((ax25_cb *)(__sk)->protinfo)
/* af_ax25.c */ /* 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_free_cb(ax25_cb *);
extern void ax25_insert_socket(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_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 ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct net_device *);
extern struct sock *ax25_addr_match(ax25_address *); extern struct sock *ax25_addr_match(ax25_address *);
extern void ax25_send_to_raw(struct sock *, struct sk_buff *, int); 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 *); ...@@ -224,6 +232,7 @@ extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
/* ax25_dev.c */ /* ax25_dev.c */
extern ax25_dev *ax25_dev_list; 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_dev_ax25dev(struct net_device *);
extern ax25_dev *ax25_addr_ax25dev(ax25_address *); extern ax25_dev *ax25_addr_ax25dev(ax25_address *);
extern void ax25_dev_device_up(struct net_device *); extern void ax25_dev_device_up(struct net_device *);
...@@ -286,10 +295,16 @@ extern void ax25_rt_device_down(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_ioctl(unsigned int, void *);
extern int ax25_rt_get_info(char *, char **, off_t, int); extern int ax25_rt_get_info(char *, char **, off_t, int);
extern int ax25_rt_autobind(ax25_cb *, ax25_address *); 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 struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *);
extern void ax25_rt_free(void); 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 */ /* ax25_std_in.c */
extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int);
......
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.
/* /*
* 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 * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* This module: * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk)
* This module is free software; you can redistribute it and/or * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org)
* modify it under the terms of the GNU General Public License * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* as published by the Free Software Foundation; either version * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
* 2 of the License, or (at your option) any later version. * Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl)
* * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
* 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.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -114,6 +23,7 @@ ...@@ -114,6 +23,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/smp_lock.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/net.h> #include <linux/net.h>
#include <net/ax25.h> #include <net/ax25.h>
...@@ -134,13 +44,15 @@ ...@@ -134,13 +44,15 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h>
#include <net/tcp.h> #include <net/tcp.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/arp.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; static struct proto_ops ax25_proto_ops;
...@@ -171,27 +83,24 @@ static void ax25_free_sock(struct sock *sk) ...@@ -171,27 +83,24 @@ static void ax25_free_sock(struct sock *sk)
static void ax25_remove_socket(ax25_cb *ax25) static void ax25_remove_socket(ax25_cb *ax25)
{ {
ax25_cb *s; ax25_cb *s;
unsigned long flags;
save_flags(flags); cli();
spin_lock_bh(&ax25_list_lock);
if ((s = ax25_list) == ax25) { if ((s = ax25_list) == ax25) {
ax25_list = s->next; ax25_list = s->next;
restore_flags(flags); spin_unlock_bh(&ax25_list_lock);
return; return;
} }
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == ax25) { if (s->next == ax25) {
s->next = ax25->next; s->next = ax25->next;
restore_flags(flags); spin_unlock_bh(&ax25_list_lock);
return; return;
} }
s = s->next; s = s->next;
} }
spin_unlock_bh(&ax25_list_lock);
restore_flags(flags);
} }
/* /*
...@@ -205,18 +114,21 @@ static void ax25_kill_by_device(struct net_device *dev) ...@@ -205,18 +114,21 @@ static void ax25_kill_by_device(struct net_device *dev)
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return; return;
spin_lock_bh(&ax25_list_lock);
for (s = ax25_list; s != NULL; s = s->next) { for (s = ax25_list; s != NULL; s = s->next) {
if (s->ax25_dev == ax25_dev) { if (s->ax25_dev == ax25_dev) {
s->ax25_dev = NULL; s->ax25_dev = NULL;
ax25_disconnect(s, ENETUNREACH); ax25_disconnect(s, ENETUNREACH);
} }
} }
spin_unlock_bh(&ax25_list_lock);
} }
/* /*
* Handle device status changes. * 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; struct net_device *dev = (struct net_device *)ptr;
...@@ -245,64 +157,59 @@ static int ax25_device_event(struct notifier_block *this,unsigned long event, vo ...@@ -245,64 +157,59 @@ static int ax25_device_event(struct notifier_block *this,unsigned long event, vo
*/ */
void ax25_insert_socket(ax25_cb *ax25) void ax25_insert_socket(ax25_cb *ax25)
{ {
unsigned long flags; spin_lock_bh(&ax25_list_lock);
save_flags(flags);
cli();
ax25->next = ax25_list; ax25->next = ax25_list;
ax25_list = ax25; ax25_list = ax25;
spin_unlock_bh(&ax25_list_lock);
restore_flags(flags);
} }
/* /*
* Find a socket that wants to accept the SABM we have just * Find a socket that wants to accept the SABM we have just
* received. * 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; ax25_cb *s;
save_flags(flags); spin_lock_bh(&ax25_list_lock);
cli();
for (s = ax25_list; s != NULL; s = s->next) { for (s = ax25_list; s != NULL; s = s->next) {
if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
continue; continue;
if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) { 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 device is null we match any device */
if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) { if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
restore_flags(flags); spin_unlock_bh(&ax25_list_lock);
return s->sk; return s->sk;
} }
} }
} }
spin_unlock_bh(&ax25_list_lock);
restore_flags(flags);
return NULL; return NULL;
} }
/* /*
* Find an AX.25 socket given both ends. * 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; 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) { 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) { 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); sk = s->sk;
return s->sk; lock_sock(sk);
break;
} }
} }
restore_flags(flags); out:
spin_unlock_bh(&ax25_list_lock);
return NULL; return NULL;
} }
...@@ -311,14 +218,12 @@ struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, in ...@@ -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 * 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. * 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; 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) { for (s = ax25_list; s != NULL; s = s->next) {
if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
continue; continue;
...@@ -334,12 +239,12 @@ ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi ...@@ -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) if (s->digipeat != NULL && s->digipeat->ndigi != 0)
continue; continue;
} }
restore_flags(flags); spin_unlock_bh(&ax25_list_lock);
return s; return s;
} }
} }
spin_unlock_bh(&ax25_list_lock);
restore_flags(flags);
return NULL; return NULL;
} }
...@@ -349,22 +254,21 @@ ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi ...@@ -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) struct sock *ax25_addr_match(ax25_address *addr)
{ {
unsigned long flags; struct sock *sk = NULL;
ax25_cb *s; ax25_cb *s;
save_flags(flags); spin_lock_bh(&ax25_list_lock);
cli();
for (s = ax25_list; s != NULL; s = s->next) { for (s = ax25_list; s != NULL; s = s->next) {
if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == SOCK_RAW) { if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 &&
restore_flags(flags); s->sk->type == SOCK_RAW) {
return s->sk; sk = s->sk;
lock_sock(sk);
break;
} }
} }
spin_unlock_bh(&ax25_list_lock);
restore_flags(flags); return sk;
return NULL;
} }
void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) 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) ...@@ -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 * This is called from user mode and the timers. Thus it protects itself
* interrupt users but doesn't worry about being called during work. * against interrupt users but doesn't worry about being called during
* Once it is removed from the queue no interrupt or bottom half will * work. Once it is removed from the queue no interrupt or bottom half
* touch it and we are (fairly 8-) ) safe. * 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; struct sk_buff *skb;
unsigned long flags;
save_flags(flags); cli(); ax25_remove_socket(ax25);
ax25_stop_heartbeat(ax25); ax25_stop_heartbeat(ax25);
ax25_stop_t1timer(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 ...@@ -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_t3timer(ax25);
ax25_stop_idletimer(ax25); ax25_stop_idletimer(ax25);
ax25_remove_socket(ax25);
ax25_clear_queues(ax25); /* Flush the queues */ ax25_clear_queues(ax25); /* Flush the queues */
if (ax25->sk != NULL) { if (ax25->sk != NULL) {
while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != 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); 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); ax25_start_heartbeat(sax25);
sax25->state = AX25_STATE_0; 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 ...@@ -450,8 +355,6 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer
} else { } else {
ax25_free_cb(ax25); ax25_free_cb(ax25);
} }
restore_flags(flags);
} }
/* /*
...@@ -631,13 +534,14 @@ ax25_cb *ax25_create_cb(void) ...@@ -631,13 +534,14 @@ ax25_cb *ax25_create_cb(void)
* AX25 socket object * 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; struct sock *sk = sock->sk;
ax25_cb *ax25 = ax25_sk(sk); ax25_cb *ax25;
struct net_device *dev; struct net_device *dev;
char devname[IFNAMSIZ]; char devname[IFNAMSIZ];
int opt; int opt, res = 0;
if (level != SOL_AX25) if (level != SOL_AX25)
return -ENOPROTOOPT; return -ENOPROTOOPT;
...@@ -648,98 +552,131 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op ...@@ -648,98 +552,131 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
if (get_user(opt, (int *)optval)) if (get_user(opt, (int *)optval))
return -EFAULT; return -EFAULT;
lock_sock(sk);
ax25 = ax25_sk(sk);
switch (optname) { switch (optname) {
case AX25_WINDOW: case AX25_WINDOW:
if (ax25->modulus == AX25_MODULUS) { if (ax25->modulus == AX25_MODULUS) {
if (opt < 1 || opt > 7) if (opt < 1 || opt > 7) {
return -EINVAL; res = -EINVAL;
break;
}
} else { } else {
if (opt < 1 || opt > 63) if (opt < 1 || opt > 63) {
return -EINVAL; res = -EINVAL;
break;
}
} }
ax25->window = opt; ax25->window = opt;
return 0; break;
case AX25_T1: case AX25_T1:
if (opt < 1) if (opt < 1) {
return -EINVAL; res = -EINVAL;
break;
}
ax25->rtt = (opt * HZ) / 2; ax25->rtt = (opt * HZ) / 2;
ax25->t1 = opt * HZ; ax25->t1 = opt * HZ;
return 0; break;
case AX25_T2: case AX25_T2:
if (opt < 1) if (opt < 1) {
return -EINVAL; res = -EINVAL;
break;
}
ax25->t2 = opt * HZ; ax25->t2 = opt * HZ;
return 0; break;
case AX25_N2: case AX25_N2:
if (opt < 1 || opt > 31) if (opt < 1 || opt > 31) {
return -EINVAL; res = -EINVAL;
break;
}
ax25->n2 = opt; ax25->n2 = opt;
return 0; break;
case AX25_T3: case AX25_T3:
if (opt < 1) if (opt < 1) {
return -EINVAL; res = -EINVAL;
break;
}
ax25->t3 = opt * HZ; ax25->t3 = opt * HZ;
return 0; break;
case AX25_IDLE: case AX25_IDLE:
if (opt < 0) if (opt < 0) {
return -EINVAL; res = -EINVAL;
break;
}
ax25->idle = opt * 60 * HZ; ax25->idle = opt * 60 * HZ;
return 0; break;
case AX25_BACKOFF: case AX25_BACKOFF:
if (opt < 0 || opt > 2) if (opt < 0 || opt > 2) {
return -EINVAL; res = -EINVAL;
break;
}
ax25->backoff = opt; ax25->backoff = opt;
return 0; break;
case AX25_EXTSEQ: case AX25_EXTSEQ:
ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
return 0; break;
case AX25_PIDINCL: case AX25_PIDINCL:
ax25->pidincl = opt ? 1 : 0; ax25->pidincl = opt ? 1 : 0;
return 0; break;
case AX25_IAMDIGI: case AX25_IAMDIGI:
ax25->iamdigi = opt ? 1 : 0; ax25->iamdigi = opt ? 1 : 0;
return 0; break;
case AX25_PACLEN: case AX25_PACLEN:
if (opt < 16 || opt > 65535) if (opt < 16 || opt > 65535) {
return -EINVAL; res = -EINVAL;
break;
}
ax25->paclen = opt; ax25->paclen = opt;
return 0; break;
case SO_BINDTODEVICE: case SO_BINDTODEVICE:
if (optlen > IFNAMSIZ) optlen=IFNAMSIZ; if (optlen > IFNAMSIZ)
if (copy_from_user(devname, optval, optlen)) optlen=IFNAMSIZ;
return -EFAULT; if (copy_from_user(devname, optval, optlen)) {
res = -EFAULT;
break;
}
dev = dev_get_by_name(devname); dev = dev_get_by_name(devname);
if (dev == NULL) return -ENODEV; if (dev == NULL) {
res = -ENODEV;
break;
}
if (sk->type == SOCK_SEQPACKET && if (sk->type == SOCK_SEQPACKET &&
(sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) {
return -EADDRNOTAVAIL; res = -EADDRNOTAVAIL;
break;
}
ax25->ax25_dev = ax25_dev_ax25dev(dev); ax25->ax25_dev = ax25_dev_ax25dev(dev);
ax25_fillin_cb(ax25, ax25->ax25_dev); ax25_fillin_cb(ax25, ax25->ax25_dev);
return 0; break;
default: default:
return -ENOPROTOOPT; 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; struct sock *sk = sock->sk;
ax25_cb *ax25 = ax25_sk(sk); ax25_cb *ax25;
struct ax25_dev *ax25_dev; struct ax25_dev *ax25_dev;
char devname[IFNAMSIZ]; char devname[IFNAMSIZ];
void *valptr; void *valptr;
...@@ -758,6 +695,9 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op ...@@ -758,6 +695,9 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
valptr = (void *) &val; valptr = (void *) &val;
length = min_t(unsigned int, maxlen, sizeof(int)); length = min_t(unsigned int, maxlen, sizeof(int));
lock_sock(sk);
ax25 = ax25_sk(sk);
switch (optname) { switch (optname) {
case AX25_WINDOW: case AX25_WINDOW:
val = ax25->window; val = ax25->window;
...@@ -819,8 +759,10 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op ...@@ -819,8 +759,10 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break; break;
default: default:
release_sock(sk);
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
release_sock(sk);
if (put_user(length, optlen)) if (put_user(length, optlen))
return -EFAULT; return -EFAULT;
...@@ -831,14 +773,20 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op ...@@ -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) static int ax25_listen(struct socket *sock, int backlog)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int res = 0;
lock_sock(sk);
if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) { if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) {
sk->max_ack_backlog = backlog; sk->max_ack_backlog = backlog;
sk->state = TCP_LISTEN; 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) int ax25_create(struct socket *sock, int protocol)
...@@ -851,6 +799,7 @@ int ax25_create(struct socket *sock, int protocol) ...@@ -851,6 +799,7 @@ int ax25_create(struct socket *sock, int protocol)
if (protocol == 0 || protocol == PF_AX25) if (protocol == 0 || protocol == PF_AX25)
protocol = AX25_P_TEXT; protocol = AX25_P_TEXT;
break; break;
case SOCK_SEQPACKET: case SOCK_SEQPACKET:
switch (protocol) { switch (protocol) {
case 0: case 0:
...@@ -883,6 +832,7 @@ int ax25_create(struct socket *sock, int protocol) ...@@ -883,6 +832,7 @@ int ax25_create(struct socket *sock, int protocol)
break; break;
} }
break; break;
case SOCK_RAW: case SOCK_RAW:
break; break;
default: default:
...@@ -985,8 +935,10 @@ static int ax25_release(struct socket *sock) ...@@ -985,8 +935,10 @@ static int ax25_release(struct socket *sock)
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
ax25_cb *ax25; ax25_cb *ax25;
if (sk == NULL) return 0; if (sk == NULL)
return 0;
lock_sock(sk);
ax25 = ax25_sk(sk); ax25 = ax25_sk(sk);
if (sk->type == SOCK_SEQPACKET) { if (sk->type == SOCK_SEQPACKET) {
...@@ -1007,6 +959,7 @@ static int ax25_release(struct socket *sock) ...@@ -1007,6 +959,7 @@ static int ax25_release(struct socket *sock)
case AX25_STATE_4: case AX25_STATE_4:
ax25_clear_queues(ax25); ax25_clear_queues(ax25);
ax25->n2count = 0; ax25->n2count = 0;
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX: case AX25_PROTO_STD_DUPLEX:
...@@ -1048,6 +1001,7 @@ static int ax25_release(struct socket *sock) ...@@ -1048,6 +1001,7 @@ static int ax25_release(struct socket *sock)
sock->sk = NULL; sock->sk = NULL;
sk->socket = NULL; /* Not used, but we should do this */ sk->socket = NULL; /* Not used, but we should do this */
release_sock(sk);
return 0; return 0;
} }
...@@ -1061,20 +1015,19 @@ static int ax25_release(struct socket *sock) ...@@ -1061,20 +1015,19 @@ static int ax25_release(struct socket *sock)
static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
ax25_cb *ax25 = ax25_sk(sk);
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
ax25_address *call;
ax25_dev *ax25_dev = NULL; ax25_dev *ax25_dev = NULL;
ax25_address *call;
if (sk->zapped == 0) ax25_cb *ax25;
return -EINVAL; int err = 0;
if (addr_len != sizeof(struct sockaddr_ax25) && if (addr_len != sizeof(struct sockaddr_ax25) &&
addr_len != sizeof(struct full_sockaddr_ax25)) { addr_len != sizeof(struct full_sockaddr_ax25)) {
/* support for old structure may go away some time */ /* support for old structure may go away some time */
if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || 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; return -EINVAL;
}
printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n", printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n",
current->comm); current->comm);
...@@ -1084,8 +1037,17 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -1084,8 +1037,17 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return -EINVAL; return -EINVAL;
call = ax25_findbyuid(current->euid); 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; return -EACCES;
}
lock_sock(sk);
ax25 = ax25_sk(sk);
if (sk->zapped == 0) {
err = -EINVAL;
goto out;
}
if (call == NULL) if (call == NULL)
ax25->source_addr = addr->fsa_ax25.sax25_call; 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) ...@@ -1095,17 +1057,20 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
/* /*
* User already set interface with SO_BINDTODEVICE * User already set interface with SO_BINDTODEVICE
*/ */
if (ax25->ax25_dev != NULL) if (ax25->ax25_dev != NULL)
goto done; goto done;
if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 && if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
(ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) {
return -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
goto out;
}
} else { } else {
if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) {
return -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
goto out;
}
} }
if (ax25_dev != NULL) if (ax25_dev != NULL)
...@@ -1114,41 +1079,24 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -1114,41 +1079,24 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
done: done:
ax25_insert_socket(ax25); ax25_insert_socket(ax25);
sk->zapped = 0; sk->zapped = 0;
out:
release_sock(sk);
return 0; return 0;
} }
/* /*
* FIXME: nonblock behaviour looks like it may have a bug. * FIXME: nonblock behaviour looks like it may have a bug.
*/ */
static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
ax25_cb *ax25 = ax25_sk(sk); ax25_cb *ax25 = ax25_sk(sk);
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
ax25_digi *digi = NULL; ax25_digi *digi = NULL;
int ct = 0, err; int ct = 0, err = 0;
/* 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;
/* /*
* some sanity checks. code further down depends on this * 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 ...@@ -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)) { else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
/* support for old structure may go away some time */ /* support for old structure may go away some time */
if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || 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; return -EINVAL;
}
printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n", printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n",
current->comm); current->comm);
...@@ -1172,6 +1121,34 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ...@@ -1172,6 +1121,34 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
if (fsa->fsa_ax25.sax25_family != AF_AX25) if (fsa->fsa_ax25.sax25_family != AF_AX25)
return -EINVAL; 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) { if (ax25->digipeat != NULL) {
kfree(ax25->digipeat); kfree(ax25->digipeat);
ax25->digipeat = NULL; ax25->digipeat = NULL;
...@@ -1180,13 +1157,18 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ...@@ -1180,13 +1157,18 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
/* /*
* Handle digi-peaters to be used. * 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 ? */ /* Valid number of digipeaters ? */
if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) {
return -EINVAL; err = -EINVAL;
goto out;
}
if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
return -ENOBUFS; err = -ENOBUFS;
goto out;
}
digi->ndigi = fsa->fsa_ax25.sax25_ndigis; digi->ndigi = fsa->fsa_ax25.sax25_ndigis;
digi->lastrepeat = -1; digi->lastrepeat = -1;
...@@ -1214,19 +1196,24 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ...@@ -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", printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n",
current->comm); current->comm);
if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0) if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0)
return err; goto out;
ax25_fillin_cb(ax25, ax25->ax25_dev); ax25_fillin_cb(ax25, ax25->ax25_dev);
ax25_insert_socket(ax25); ax25_insert_socket(ax25);
} else { } else {
if (ax25->ax25_dev == NULL) if (ax25->ax25_dev == NULL) {
return -EHOSTUNREACH; err = -EHOSTUNREACH;
goto out;
}
} }
if (sk->type == SOCK_SEQPACKET && if (sk->type == SOCK_SEQPACKET &&
ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi,
ax25->ax25_dev->dev)) { ax25->ax25_dev->dev)) {
if (digi != NULL) kfree(digi); if (digi != NULL)
return -EADDRINUSE; /* Already such a connection */ kfree(digi);
err = -EADDRINUSE; /* Already such a connection */
goto out;
} }
ax25->dest_addr = fsa->fsa_ax25.sax25_call; 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 ...@@ -1236,7 +1223,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
if (sk->type != SOCK_SEQPACKET) { if (sk->type != SOCK_SEQPACKET) {
sock->state = SS_CONNECTED; sock->state = SS_CONNECTED;
sk->state = TCP_ESTABLISHED; sk->state = TCP_ESTABLISHED;
return 0; goto out;
} }
/* Move to connecting socket, ax.25 lapb WAIT_UA.. */ /* Move to connecting socket, ax.25 lapb WAIT_UA.. */
...@@ -1252,8 +1239,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ...@@ -1252,8 +1239,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
#ifdef CONFIG_AX25_DAMA_SLAVE #ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE: case AX25_PROTO_DAMA_SLAVE:
ax25->modulus = AX25_MODULUS; ax25->modulus = AX25_MODULUS;
ax25->window = ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
ax25->ax25_dev->values[AX25_VALUES_WINDOW];
if (ax25->ax25_dev->dama.slave) if (ax25->ax25_dev->dama.slave)
ax25_ds_establish_data_link(ax25); ax25_ds_establish_data_link(ax25);
else else
...@@ -1267,30 +1253,43 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ...@@ -1267,30 +1253,43 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
ax25_start_heartbeat(ax25); ax25_start_heartbeat(ax25);
/* Now the loop */ /* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) {
return -EINPROGRESS; 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 */ add_wait_queue(sk->sleep, &wait);
while (sk->state == TCP_SYN_SENT) { for (;;) {
interruptible_sleep_on(sk->sleep); if (sk->state != TCP_SYN_SENT)
if (signal_pending(current)) { break;
sti(); set_current_state(TASK_INTERRUPTIBLE);
release_sock(sk);
if (!signal_pending(tsk)) {
schedule();
lock_sock(sk);
continue;
}
return -ERESTARTSYS; return -ERESTARTSYS;
} }
current->state = TASK_RUNNING;
remove_wait_queue(sk->sleep, &wait);
} }
if (sk->state != TCP_ESTABLISHED) { if (sk->state != TCP_ESTABLISHED) {
/* Not in ABM, not in WAIT_UA -> failed */ /* Not in ABM, not in WAIT_UA -> failed */
sti();
sock->state = SS_UNCONNECTED; 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; sock->state = SS_CONNECTED;
sti(); out:
release_sock(sk);
return 0; return 0;
} }
...@@ -1298,9 +1297,12 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ...@@ -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) static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
{ {
struct sock *sk; struct task_struct *tsk = current;
struct sock *newsk; DECLARE_WAITQUEUE(wait, tsk);
struct sk_buff *skb; struct sk_buff *skb;
struct sock *newsk;
struct sock *sk;
int err = 0;
if (sock->state != SS_UNCONNECTED) if (sock->state != SS_UNCONNECTED)
return -EINVAL; return -EINVAL;
...@@ -1308,26 +1310,40 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) ...@@ -1308,26 +1310,40 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
if ((sk = sock->sk) == NULL) if ((sk = sock->sk) == NULL)
return -EINVAL; return -EINVAL;
if (sk->type != SOCK_SEQPACKET) lock_sock(sk);
return -EOPNOTSUPP; if (sk->type != SOCK_SEQPACKET) {
err = -EOPNOTSUPP;
goto out;
}
if (sk->state != TCP_LISTEN) if (sk->state != TCP_LISTEN) {
return -EINVAL; err = -EINVAL;
goto out;
}
/* /*
* The read queue this time is holding sockets ready to use * The read queue this time is holding sockets ready to use
* hooked into the SABM we saved * hooked into the SABM we saved
*/ */
do { add_wait_queue(sk->sleep, &wait);
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { for (;;) {
skb = skb_dequeue(&sk->receive_queue);
if (skb)
break;
current->state = TASK_INTERRUPTIBLE;
release_sock(sk);
if (flags & O_NONBLOCK) if (flags & O_NONBLOCK)
return -EWOULDBLOCK; return -EWOULDBLOCK;
if (!signal_pending(tsk)) {
interruptible_sleep_on(sk->sleep); schedule();
if (signal_pending(current)) lock_sock(sk);
continue;
}
return -ERESTARTSYS; return -ERESTARTSYS;
} }
} while (skb == NULL); current->state = TASK_RUNNING;
remove_wait_queue(sk->sleep, &wait);
newsk = skb->sk; newsk = skb->sk;
newsk->pair = NULL; newsk->pair = NULL;
...@@ -1340,19 +1356,29 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) ...@@ -1340,19 +1356,29 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
newsock->sk = newsk; newsock->sk = newsk;
newsock->state = SS_CONNECTED; 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 full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
struct sock *sk = sock->sk;
unsigned char ndigi, i; unsigned char ndigi, i;
ax25_cb *ax25;
int err = 0;
lock_sock(sk);
ax25 = ax25_sk(sk);
if (peer != 0) { if (peer != 0) {
if (sk->state != TCP_ESTABLISHED) if (sk->state != TCP_ESTABLISHED) {
return -ENOTCONN; err = -ENOTCONN;
goto out;
}
fsa->fsa_ax25.sax25_family = AF_AX25; fsa->fsa_ax25.sax25_family = AF_AX25;
fsa->fsa_ax25.sax25_call = ax25->dest_addr; fsa->fsa_ax25.sax25_call = ax25->dest_addr;
...@@ -1377,41 +1403,53 @@ static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_ ...@@ -1377,41 +1403,53 @@ static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_
} }
} }
*uaddr_len = sizeof (struct full_sockaddr_ax25); *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; struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
int err; struct sock *sk = sock->sk;
struct sockaddr_ax25 sax; struct sockaddr_ax25 sax;
struct sk_buff *skb; struct sk_buff *skb;
ax25_digi dtmp, *dp;
unsigned char *asmptr; unsigned char *asmptr;
int size; ax25_cb *ax25;
ax25_digi *dp; int lv, size, err, addr_len = msg->msg_namelen;
ax25_digi dtmp;
int lv;
int addr_len = msg->msg_namelen;
if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) {
return -EINVAL; return -EINVAL;
}
lock_sock(sk);
ax25 = ax25_sk(sk);
if (sk->zapped) if (sk->zapped) {
return -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
goto out;
}
if (sk->shutdown & SEND_SHUTDOWN) { if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 0); send_sig(SIGPIPE, current, 0);
return -EPIPE; err = -EPIPE;
goto out;
} }
if (ax25->ax25_dev == NULL) if (ax25->ax25_dev == NULL) {
return -ENETUNREACH; err = -ENETUNREACH;
goto out;
}
if (usax != NULL) { if (usax != NULL) {
if (usax->sax25_family != AF_AX25) if (usax->sax25_family != AF_AX25) {
return -EINVAL; err = -EINVAL;
goto out;
}
if (addr_len == sizeof(struct sockaddr_ax25)) { if (addr_len == sizeof(struct sockaddr_ax25)) {
printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n", 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 ...@@ -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)) { else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
/* support for old structure may go away some time */ /* support for old structure may go away some time */
if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || 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; err = -EINVAL;
goto out;
}
printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n", printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n",
current->comm); current->comm);
...@@ -1432,8 +1472,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct ...@@ -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; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax;
/* Valid number of digipeaters ? */ /* Valid number of digipeaters ? */
if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) {
return -EINVAL; err = -EINVAL;
goto out;
}
dtmp.ndigi = usax->sax25_ndigis; dtmp.ndigi = usax->sax25_ndigis;
...@@ -1447,8 +1489,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct ...@@ -1447,8 +1489,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
} }
sax = *usax; sax = *usax;
if (sk->type == SOCK_SEQPACKET && ax25cmp(&ax25->dest_addr, &sax.sax25_call) != 0) if (sk->type == SOCK_SEQPACKET && ax25cmp(&ax25->dest_addr, &sax.sax25_call) != 0) {
return -EISCONN; err = -EISCONN;
goto out;
}
if (usax->sax25_ndigis == 0) if (usax->sax25_ndigis == 0)
dp = NULL; dp = NULL;
else else
...@@ -1459,8 +1503,10 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct ...@@ -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 * it has become closed (not started closed) and is VC
* we ought to SIGPIPE, EPIPE * we ought to SIGPIPE, EPIPE
*/ */
if (sk->state != TCP_ESTABLISHED) if (sk->state != TCP_ESTABLISHED) {
return -ENOTCONN; err = -ENOTCONN;
goto out;
}
sax.sax25_family = AF_AX25; sax.sax25_family = AF_AX25;
sax.sax25_call = ax25->dest_addr; sax.sax25_call = ax25->dest_addr;
dp = ax25->digipeat; dp = ax25->digipeat;
...@@ -1474,8 +1520,9 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct ...@@ -1474,8 +1520,9 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
/* Assume the worst case */ /* Assume the worst case */
size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN; 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) skb = sock_alloc_send_skb(sk, size, msg->msg_flags&MSG_DONTWAIT, &err);
return err; if (skb == NULL)
goto out;
skb_reserve(skb, size - len); skb_reserve(skb, size - len);
...@@ -1497,14 +1544,17 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct ...@@ -1497,14 +1544,17 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
/* Connected mode sockets go via the LAPB machine */ /* Connected mode sockets go via the LAPB machine */
if (sk->state != TCP_ESTABLISHED) { if (sk->state != TCP_ESTABLISHED) {
kfree_skb(skb); kfree_skb(skb);
return -ENOTCONN; err = -ENOTCONN;
goto out;
} }
/* Shove it onto the queue and kick */ /* Shove it onto the queue and kick */
ax25_output(ax25, ax25->paclen, skb); ax25_output(ax25, ax25->paclen, skb);
return len; err = len;
} else { goto out;
}
asmptr = skb_push(skb, 1 + ax25_addr_size(dp)); asmptr = skb_push(skb, 1 + ax25_addr_size(dp));
SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp); SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp);
...@@ -1530,27 +1580,37 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct ...@@ -1530,27 +1580,37 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
ax25_queue_xmit(skb); ax25_queue_xmit(skb);
return len; 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; struct sock *sk = sock->sk;
int copied;
struct sk_buff *skb; struct sk_buff *skb;
int er; int copied;
int err = 0;
lock_sock(sk);
/* /*
* This works for seqpacket too. The receiver has ordered the * This works for seqpacket too. The receiver has ordered the
* queue for us! We do one quick check first though * queue for us! We do one quick check first though
*/ */
if (sk->type == SOCK_SEQPACKET && sk->state != TCP_ESTABLISHED) if (sk->type == SOCK_SEQPACKET && sk->state != TCP_ESTABLISHED) {
return -ENOTCONN; err = -ENOTCONN;
goto out;
}
/* Now we can treat all alike */ /* Now we can treat all alike */
if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
return er; flags & MSG_DONTWAIT, &err);
if (skb == NULL)
goto out;
if (!ax25_sk(sk)->pidincl) if (!ax25_sk(sk)->pidincl)
skb_pull(skb, 1); /* Remove PID */ skb_pull(skb, 1); /* Remove PID */
...@@ -1590,8 +1650,12 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f ...@@ -1590,8 +1650,12 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f
} }
skb_free_datagram(sk, skb); skb_free_datagram(sk, skb);
err = copied;
return copied; out:
release_sock(sk);
return err;
} }
static int ax25_shutdown(struct socket *sk, int how) static int ax25_shutdown(struct socket *sk, int how)
...@@ -1603,14 +1667,17 @@ static int ax25_shutdown(struct socket *sk, int how) ...@@ -1603,14 +1667,17 @@ static int ax25_shutdown(struct socket *sk, int how)
static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int res = 0;
lock_sock(sk);
switch (cmd) { switch (cmd) {
case TIOCOUTQ: { case TIOCOUTQ: {
long amount; long amount;
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0) if (amount < 0)
amount = 0; amount = 0;
return put_user(amount, (int *)arg); res = put_user(amount, (int *)arg);
break;
} }
case TIOCINQ: { case TIOCINQ: {
...@@ -1619,49 +1686,70 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -1619,49 +1686,70 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
/* These two are safe on a single CPU system as only user tasks fiddle here */ /* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->receive_queue)) != NULL) if ((skb = skb_peek(&sk->receive_queue)) != NULL)
amount = skb->len; amount = skb->len;
return put_user(amount, (int *)arg); res = put_user(amount, (int *)arg);
break;
} }
case SIOCGSTAMP: case SIOCGSTAMP:
if (sk != NULL) { if (sk != NULL) {
if (sk->stamp.tv_sec == 0) if (sk->stamp.tv_sec == 0) {
return -ENOENT; res = -ENOENT;
return copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0; break;
} }
return -EINVAL; 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 SIOCAX25ADDUID: /* Add a uid to the uid/call map table */
case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */
case SIOCAX25GETUID: { case SIOCAX25GETUID: {
struct sockaddr_ax25 sax25; struct sockaddr_ax25 sax25;
if (copy_from_user(&sax25, (void *)arg, sizeof(sax25))) if (copy_from_user(&sax25, (void *)arg, sizeof(sax25))) {
return -EFAULT; res = -EFAULT;
return ax25_uid_ioctl(cmd, &sax25); break;
}
res = ax25_uid_ioctl(cmd, &sax25);
break;
} }
case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ case SIOCAX25NOUID: { /* Set the default policy (default/bar) */
long amount; long amount;
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN)) {
return -EPERM; res = -EPERM;
if (get_user(amount, (long *)arg)) break;
return -EFAULT; }
if (amount > AX25_NOUID_BLOCK) if (get_user(amount, (long *)arg)) {
return -EINVAL; res = -EFAULT;
break;
}
if (amount > AX25_NOUID_BLOCK) {
res = -EINVAL;
break;
}
ax25_uid_policy = amount; ax25_uid_policy = amount;
return 0; res = 0;
break;
} }
case SIOCADDRT: case SIOCADDRT:
case SIOCDELRT: case SIOCDELRT:
case SIOCAX25OPTRT: case SIOCAX25OPTRT:
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN)) {
return -EPERM; res = -EPERM;
return ax25_rt_ioctl(cmd, (void *)arg); break;
}
res = ax25_rt_ioctl(cmd, (void *)arg);
break;
case SIOCAX25CTLCON: case SIOCAX25CTLCON:
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN)) {
return -EPERM; res = -EPERM;
return ax25_ctl_ioctl(cmd, (void *)arg); break;
}
res = ax25_ctl_ioctl(cmd, (void *)arg);
break;
case SIOCAX25GETINFO: case SIOCAX25GETINFO:
case SIOCAX25GETINFOOLD: { case SIOCAX25GETINFOOLD: {
...@@ -1697,23 +1785,33 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -1697,23 +1785,33 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
warned=1; warned=1;
} }
if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) {
return -EFAULT; res = -EFAULT;
break;
}
} else { } else {
if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct))) if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct))) {
return -EINVAL; res = -EINVAL;
break;
} }
return 0; }
res = 0;
break;
} }
case SIOCAX25ADDFWD: case SIOCAX25ADDFWD:
case SIOCAX25DELFWD: { case SIOCAX25DELFWD: {
struct ax25_fwd_struct ax25_fwd; struct ax25_fwd_struct ax25_fwd;
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN)) {
return -EPERM; res = -EPERM;
if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) break;
return -EFAULT; }
return ax25_fwd_ioctl(cmd, &ax25_fwd); if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) {
res = -EFAULT;
break;
}
res = ax25_fwd_ioctl(cmd, &ax25_fwd);
break;
} }
case SIOCGIFADDR: case SIOCGIFADDR:
...@@ -1726,14 +1824,16 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -1726,14 +1824,16 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSIFNETMASK: case SIOCSIFNETMASK:
case SIOCGIFMETRIC: case SIOCGIFMETRIC:
case SIOCSIFMETRIC: case SIOCSIFMETRIC:
return -EINVAL; res = -EINVAL;
break;
default: default:
return dev_ioctl(cmd, (void *)arg); res = dev_ioctl(cmd, (void *)arg);
break;
} }
release_sock(sk);
/*NOTREACHED*/ return res;
return 0;
} }
static int ax25_get_info(char *buffer, char **start, off_t offset, int length) static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
...@@ -1744,7 +1844,7 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1744,7 +1844,7 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
off_t pos = 0; off_t pos = 0;
off_t begin = 0; off_t begin = 0;
cli(); spin_lock_bh(&ax25_list_lock);
/* /*
* New format: * New format:
...@@ -1799,7 +1899,7 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1799,7 +1899,7 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
break; break;
} }
sti(); spin_unlock_bh(&ax25_list_lock);
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
...@@ -1814,7 +1914,7 @@ static struct net_proto_family ax25_family_ops = { ...@@ -1814,7 +1914,7 @@ static struct net_proto_family ax25_family_ops = {
.create = ax25_create, .create = ax25_create,
}; };
static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { static struct proto_ops ax25_proto_ops = {
.family = PF_AX25, .family = PF_AX25,
.release = ax25_release, .release = ax25_release,
...@@ -1835,15 +1935,14 @@ static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { ...@@ -1835,15 +1935,14 @@ static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = {
.sendpage = sock_no_sendpage, .sendpage = sock_no_sendpage,
}; };
#include <linux/smp_lock.h>
SOCKOPS_WRAP(ax25_proto, PF_AX25);
/* /*
* Called by socket.c on kernel start up * Called by socket.c on kernel start up
*/ */
static struct packet_type ax25_packet_type = { static struct packet_type ax25_packet_type = {
.type = __constant_htons(ETH_P_AX25), .type = __constant_htons(ETH_P_AX25),
.dev = NULL, /* All devices */
.func = ax25_kiss_rcv, .func = ax25_kiss_rcv,
.data = (void *) 1
}; };
static struct notifier_block ax25_dev_notifier = { static struct notifier_block ax25_dev_notifier = {
......
/* /*
* 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) 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
* AX.25 036 Jonathan(G4KLX) Split from ax25_subr.c.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
......
/* /*
* 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) 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.
*
* Other kernels modules in this kit are generally BSD derived. See the copyright headers.
*
*
* History
* AX.25 036 Jonathan(G4KLX) Split from ax25_route.c.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -27,6 +17,7 @@ ...@@ -27,6 +17,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/net.h> #include <linux/net.h>
#include <linux/spinlock.h>
#include <net/ax25.h> #include <net/ax25.h>
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
...@@ -41,27 +32,35 @@ ...@@ -41,27 +32,35 @@
#include <linux/init.h> #include <linux/init.h>
ax25_dev *ax25_dev_list; 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_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) for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
if (ax25_dev->dev == dev) if (ax25_dev->dev == dev) {
return ax25_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_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) 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) if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0) {
return ax25_dev; 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) ...@@ -71,7 +70,6 @@ ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
void ax25_dev_device_up(struct net_device *dev) void ax25_dev_device_up(struct net_device *dev)
{ {
ax25_dev *ax25_dev; ax25_dev *ax25_dev;
unsigned long flags;
if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) { if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n"); 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) ...@@ -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_PROTOCOL] = AX25_DEF_PROTOCOL;
ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT; 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->next = ax25_dev_list;
ax25_dev_list = ax25_dev; ax25_dev_list = ax25_dev;
restore_flags(flags); spin_unlock_bh(&ax25_dev_lock);
ax25_register_sysctl(); ax25_register_sysctl();
} }
...@@ -111,14 +109,13 @@ void ax25_dev_device_up(struct net_device *dev) ...@@ -111,14 +109,13 @@ void ax25_dev_device_up(struct net_device *dev)
void ax25_dev_device_down(struct net_device *dev) void ax25_dev_device_down(struct net_device *dev)
{ {
ax25_dev *s, *ax25_dev; ax25_dev *s, *ax25_dev;
unsigned long flags;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return; return;
ax25_unregister_sysctl(); ax25_unregister_sysctl();
save_flags(flags); cli(); spin_lock_bh(&ax25_dev_lock);
#ifdef CONFIG_AX25_DAMA_SLAVE #ifdef CONFIG_AX25_DAMA_SLAVE
ax25_ds_del_timer(ax25_dev); ax25_ds_del_timer(ax25_dev);
...@@ -133,7 +130,7 @@ void ax25_dev_device_down(struct net_device *dev) ...@@ -133,7 +130,7 @@ void ax25_dev_device_down(struct net_device *dev)
if ((s = ax25_dev_list) == ax25_dev) { if ((s = ax25_dev_list) == ax25_dev) {
ax25_dev_list = s->next; ax25_dev_list = s->next;
restore_flags(flags); spin_unlock_bh(&ax25_dev_lock);
kfree(ax25_dev); kfree(ax25_dev);
ax25_register_sysctl(); ax25_register_sysctl();
return; return;
...@@ -142,7 +139,7 @@ void ax25_dev_device_down(struct net_device *dev) ...@@ -142,7 +139,7 @@ void ax25_dev_device_down(struct net_device *dev)
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == ax25_dev) { if (s->next == ax25_dev) {
s->next = ax25_dev->next; s->next = ax25_dev->next;
restore_flags(flags); spin_unlock_bh(&ax25_dev_lock);
kfree(ax25_dev); kfree(ax25_dev);
ax25_register_sysctl(); ax25_register_sysctl();
return; return;
...@@ -150,8 +147,8 @@ void ax25_dev_device_down(struct net_device *dev) ...@@ -150,8 +147,8 @@ void ax25_dev_device_down(struct net_device *dev)
s = s->next; s = s->next;
} }
spin_unlock_bh(&ax25_dev_lock);
restore_flags(flags);
ax25_register_sysctl(); ax25_register_sysctl();
} }
...@@ -202,12 +199,16 @@ struct net_device *ax25_fwd_dev(struct net_device *dev) ...@@ -202,12 +199,16 @@ struct net_device *ax25_fwd_dev(struct net_device *dev)
*/ */
void __exit ax25_dev_free(void) 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) { while (ax25_dev != NULL) {
s = ax25_dev; s = ax25_dev;
ax25_dev = ax25_dev->next; ax25_dev = ax25_dev->next;
kfree(s); kfree(s);
} }
ax25_dev_list = NULL;
spin_unlock_bh(&ax25_dev_lock);
} }
/* /*
* 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) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.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 036 Jonathan(G4KLX) Cloned from ax25_in.c
* Joerg(DL1BKE) Fixed it.
* AX.25 037 Jonathan(G4KLX) New timer architecture.
* Joerg(DL1BKE) ax25->n2count never got reset
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -95,11 +80,13 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet ...@@ -95,11 +80,13 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet
break; break;
case AX25_DM: case AX25_DM:
if (pf) ax25_disconnect(ax25, ECONNREFUSED); if (pf)
ax25_disconnect(ax25, ECONNREFUSED);
break; break;
default: default:
if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); if (pf)
ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
break; break;
} }
......
/* /*
* 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) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.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 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c.
* Joerg(DL1BKE) Changed ax25_ds_enquiry_response(),
* fixed ax25_dama_on() and ax25_dama_off().
* AX.25 037 Jonathan(G4KLX) New timer architecture.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -31,6 +16,7 @@ ...@@ -31,6 +16,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/spinlock.h>
#include <linux/net.h> #include <linux/net.h>
#include <net/ax25.h> #include <net/ax25.h>
#include <linux/inet.h> #include <linux/inet.h>
...@@ -93,6 +79,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) ...@@ -93,6 +79,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25)
ax25_start_t3timer(ax25); ax25_start_t3timer(ax25);
ax25_ds_set_timer(ax25->ax25_dev); ax25_ds_set_timer(ax25->ax25_dev);
spin_lock_bh(&ax25_list_lock);
for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
if (ax25o == ax25) if (ax25o == ax25)
continue; continue;
...@@ -118,6 +105,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) ...@@ -118,6 +105,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25)
if (ax25o->state != AX25_STATE_0) if (ax25o->state != AX25_STATE_0)
ax25_start_t3timer(ax25o); ax25_start_t3timer(ax25o);
} }
spin_unlock_bh(&ax25_list_lock);
} }
void ax25_ds_establish_data_link(ax25_cb *ax25) void ax25_ds_establish_data_link(ax25_cb *ax25)
...@@ -171,12 +159,17 @@ static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char p ...@@ -171,12 +159,17 @@ static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char p
static int ax25_check_dama_slave(ax25_dev *ax25_dev) static int ax25_check_dama_slave(ax25_dev *ax25_dev)
{ {
ax25_cb *ax25; ax25_cb *ax25;
int res = 0;
spin_lock_bh(&ax25_list_lock);
for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next) 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) if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) {
return 1; res = 1;
break;
}
spin_unlock_bh(&ax25_list_lock);
return 0; return res;
} }
void ax25_dev_dama_on(ax25_dev *ax25_dev) void ax25_dev_dama_on(ax25_dev *ax25_dev)
......
/* /*
* 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) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.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.
*
* History
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c.
* Joerg(DL1BKE) Added DAMA Slave Timeout timer
* AX.25 037 Jonathan(G4KLX) New timer architecture.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/spinlock.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
...@@ -58,7 +51,8 @@ static void ax25_ds_add_timer(ax25_dev *ax25_dev) ...@@ -58,7 +51,8 @@ static void ax25_ds_add_timer(ax25_dev *ax25_dev)
void ax25_ds_del_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) void ax25_ds_set_timer(ax25_dev *ax25_dev)
...@@ -89,6 +83,7 @@ static void ax25_ds_timeout(unsigned long arg) ...@@ -89,6 +83,7 @@ static void ax25_ds_timeout(unsigned long arg)
return; return;
} }
spin_lock_bh(&ax25_list_lock);
for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) { for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) {
if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE))
continue; continue;
...@@ -96,6 +91,7 @@ static void ax25_ds_timeout(unsigned long arg) ...@@ -96,6 +91,7 @@ static void ax25_ds_timeout(unsigned long arg)
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25_disconnect(ax25, ETIMEDOUT); ax25_disconnect(ax25, ETIMEDOUT);
} }
spin_unlock_bh(&ax25_list_lock);
ax25_dev_dama_off(ax25_dev); ax25_dev_dama_off(ax25_dev);
} }
...@@ -178,7 +174,6 @@ void ax25_ds_idletimer_expiry(ax25_cb *ax25) ...@@ -178,7 +174,6 @@ void ax25_ds_idletimer_expiry(ax25_cb *ax25)
void ax25_ds_t1_timeout(ax25_cb *ax25) void ax25_ds_t1_timeout(ax25_cb *ax25)
{ {
switch (ax25->state) { switch (ax25->state) {
case AX25_STATE_1: case AX25_STATE_1:
if (ax25->n2count == ax25->n2) { if (ax25->n2count == ax25->n2) {
if (ax25->modulus == AX25_MODULUS) { if (ax25->modulus == AX25_MODULUS) {
......
/* /*
* 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) 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.
*
* History
* AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -20,6 +13,7 @@ ...@@ -20,6 +13,7 @@
#include <linux/in.h> #include <linux/in.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sockios.h> #include <linux/sockios.h>
...@@ -40,22 +34,25 @@ static struct protocol_struct { ...@@ -40,22 +34,25 @@ static struct protocol_struct {
unsigned int pid; unsigned int pid;
int (*func)(struct sk_buff *, ax25_cb *); int (*func)(struct sk_buff *, ax25_cb *);
} *protocol_list; } *protocol_list;
static rwlock_t protocol_list_lock = RW_LOCK_UNLOCKED;
static struct linkfail_struct { static struct linkfail_struct {
struct linkfail_struct *next; struct linkfail_struct *next;
void (*func)(ax25_cb *, int); void (*func)(ax25_cb *, int);
} *linkfail_list; } *linkfail_list;
static spinlock_t linkfail_lock = SPIN_LOCK_UNLOCKED;
static struct listen_struct { static struct listen_struct {
struct listen_struct *next; struct listen_struct *next;
ax25_address callsign; ax25_address callsign;
struct net_device *dev; struct net_device *dev;
} *listen_list; } *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; struct protocol_struct *protocol;
unsigned long flags;
if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT) if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
return 0; return 0;
...@@ -69,31 +66,28 @@ int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_ ...@@ -69,31 +66,28 @@ int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_
protocol->pid = pid; protocol->pid = pid;
protocol->func = func; protocol->func = func;
save_flags(flags); write_lock(&protocol_list_lock);
cli();
protocol->next = protocol_list; protocol->next = protocol_list;
protocol_list = protocol; protocol_list = protocol;
write_unlock(&protocol_list_lock);
restore_flags(flags);
return 1; return 1;
} }
void ax25_protocol_release(unsigned int pid) void ax25_protocol_release(unsigned int pid)
{ {
struct protocol_struct *s, *protocol = protocol_list; struct protocol_struct *s, *protocol;
unsigned long flags;
if (protocol == NULL) write_lock(&protocol_list_lock);
protocol = protocol_list;
if (protocol == NULL) {
write_unlock(&protocol_list_lock);
return; return;
}
save_flags(flags);
cli();
if (protocol->pid == pid) { if (protocol->pid == pid) {
protocol_list = protocol->next; protocol_list = protocol->next;
restore_flags(flags); write_unlock(&protocol_list_lock);
kfree(protocol); kfree(protocol);
return; return;
} }
...@@ -102,52 +96,45 @@ void ax25_protocol_release(unsigned int pid) ...@@ -102,52 +96,45 @@ void ax25_protocol_release(unsigned int pid)
if (protocol->next->pid == pid) { if (protocol->next->pid == pid) {
s = protocol->next; s = protocol->next;
protocol->next = protocol->next->next; protocol->next = protocol->next->next;
restore_flags(flags); write_unlock(&protocol_list_lock);
kfree(s); kfree(s);
return; return;
} }
protocol = protocol->next; protocol = protocol->next;
} }
write_unlock(&protocol_list_lock);
restore_flags(flags);
} }
int ax25_linkfail_register(void (*func)(ax25_cb *, int)) int ax25_linkfail_register(void (*func)(ax25_cb *, int))
{ {
struct linkfail_struct *linkfail; struct linkfail_struct *linkfail;
unsigned long flags;
if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL) if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
return 0; return 0;
linkfail->func = func; linkfail->func = func;
save_flags(flags); spin_lock_bh(&linkfail_lock);
cli();
linkfail->next = linkfail_list; linkfail->next = linkfail_list;
linkfail_list = linkfail; linkfail_list = linkfail;
spin_unlock_bh(&linkfail_lock);
restore_flags(flags);
return 1; return 1;
} }
void ax25_linkfail_release(void (*func)(ax25_cb *, int)) void ax25_linkfail_release(void (*func)(ax25_cb *, int))
{ {
struct linkfail_struct *s, *linkfail = linkfail_list; struct linkfail_struct *s, *linkfail;
unsigned long flags;
spin_lock_bh(&linkfail_lock);
linkfail = linkfail_list;
if (linkfail == NULL) if (linkfail == NULL)
return; return;
save_flags(flags);
cli();
if (linkfail->func == func) { if (linkfail->func == func) {
linkfail_list = linkfail->next; linkfail_list = linkfail->next;
restore_flags(flags); spin_unlock_bh(&linkfail_lock);
kfree(linkfail); kfree(linkfail);
return; return;
} }
...@@ -156,21 +143,19 @@ void ax25_linkfail_release(void (*func)(ax25_cb *, int)) ...@@ -156,21 +143,19 @@ void ax25_linkfail_release(void (*func)(ax25_cb *, int))
if (linkfail->next->func == func) { if (linkfail->next->func == func) {
s = linkfail->next; s = linkfail->next;
linkfail->next = linkfail->next->next; linkfail->next = linkfail->next->next;
restore_flags(flags); spin_unlock_bh(&linkfail_lock);
kfree(s); kfree(s);
return; return;
} }
linkfail = linkfail->next; linkfail = linkfail->next;
} }
spin_unlock_bh(&linkfail_lock);
restore_flags(flags);
} }
int ax25_listen_register(ax25_address *callsign, struct net_device *dev) int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
{ {
struct listen_struct *listen; struct listen_struct *listen;
unsigned long flags;
if (ax25_listen_mine(callsign, dev)) if (ax25_listen_mine(callsign, dev))
return 0; return 0;
...@@ -181,31 +166,26 @@ int ax25_listen_register(ax25_address *callsign, struct net_device *dev) ...@@ -181,31 +166,26 @@ int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
listen->callsign = *callsign; listen->callsign = *callsign;
listen->dev = dev; listen->dev = dev;
save_flags(flags); spin_lock_bh(&listen_lock);
cli();
listen->next = listen_list; listen->next = listen_list;
listen_list = listen; listen_list = listen;
spin_unlock_bh(&listen_lock);
restore_flags(flags);
return 1; return 1;
} }
void ax25_listen_release(ax25_address *callsign, struct net_device *dev) void ax25_listen_release(ax25_address *callsign, struct net_device *dev)
{ {
struct listen_struct *s, *listen = listen_list; struct listen_struct *s, *listen;
unsigned long flags;
spin_lock_bh(&listen_lock);
listen = listen_list;
if (listen == NULL) if (listen == NULL)
return; return;
save_flags(flags);
cli();
if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
listen_list = listen->next; listen_list = listen->next;
restore_flags(flags); spin_unlock_bh(&listen_lock);
kfree(listen); kfree(listen);
return; return;
} }
...@@ -214,35 +194,41 @@ void ax25_listen_release(ax25_address *callsign, struct net_device *dev) ...@@ -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) { if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
s = listen->next; s = listen->next;
listen->next = listen->next->next; listen->next = listen->next->next;
restore_flags(flags); spin_unlock_bh(&listen_lock);
kfree(s); kfree(s);
return; return;
} }
listen = listen->next; listen = listen->next;
} }
spin_unlock_bh(&listen_lock);
restore_flags(flags);
} }
int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
{ {
int (*res)(struct sk_buff *, ax25_cb *) = NULL;
struct protocol_struct *protocol; struct protocol_struct *protocol;
read_lock(&protocol_list_lock);
for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
if (protocol->pid == pid) if (protocol->pid == pid) {
return protocol->func; res = protocol->func;
break;
}
read_unlock(&protocol_list_lock);
return NULL; return res;
} }
int ax25_listen_mine(ax25_address *callsign, struct net_device *dev) int ax25_listen_mine(ax25_address *callsign, struct net_device *dev)
{ {
struct listen_struct *listen; struct listen_struct *listen;
spin_lock_bh(&listen_lock);
for (listen = listen_list; listen != NULL; listen = listen->next) for (listen = listen_list; listen != NULL; listen = listen->next)
if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
return 1; return 1;
spin_unlock_bh(&listen_lock);
return 0; return 0;
} }
...@@ -251,18 +237,24 @@ void ax25_link_failed(ax25_cb *ax25, int reason) ...@@ -251,18 +237,24 @@ void ax25_link_failed(ax25_cb *ax25, int reason)
{ {
struct linkfail_struct *linkfail; struct linkfail_struct *linkfail;
spin_lock_bh(&linkfail_lock);
for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
(linkfail->func)(ax25, reason); (linkfail->func)(ax25, reason);
spin_unlock_bh(&linkfail_lock);
} }
int ax25_protocol_is_registered(unsigned int pid) int ax25_protocol_is_registered(unsigned int pid)
{ {
struct protocol_struct *protocol; struct protocol_struct *protocol;
int res = 0;
read_lock(&protocol_list_lock);
for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
if (protocol->pid == pid) if (protocol->pid == pid) {
return 1; res = 1;
break;
}
read_unlock(&protocol_list_lock);
return 0; return res;
} }
/* /*
* 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)
* This module: * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* This module is free software; you can redistribute it and/or * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
* 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.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -217,19 +187,15 @@ static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, i ...@@ -217,19 +187,15 @@ static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, i
return queued; 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; ax25_address src, dest, *next_digi = NULL;
struct sock *sk; int type = 0, mine = 0, dama;
int type = 0; struct sock *make, *sk, *raw;
ax25_digi dp, reverse_dp; ax25_digi dp, reverse_dp;
ax25_cb *ax25; ax25_cb *ax25;
ax25_address src, dest;
ax25_address *next_digi = NULL;
ax25_dev *ax25_dev; ax25_dev *ax25_dev;
struct sock *raw;
int mine = 0;
int dama;
/* /*
* Process the AX.25/LAPB frame. * 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 ...@@ -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) { if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
skb->h.raw = skb->data + 2; /* skip control and pid */ 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]); ax25_send_to_raw(raw, skb, skb->data[1]);
release_sock(raw);
}
if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) { if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
kfree_skb(skb); kfree_skb(skb);
...@@ -307,7 +275,8 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d ...@@ -307,7 +275,8 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d
#endif #endif
case AX25_P_TEXT: case AX25_P_TEXT:
/* Now find a suitable dgram socket */ /* Now find a suitable dgram socket */
if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) { sk = ax25_get_socket(&dest, &src, SOCK_DGRAM);
if (sk != NULL) {
if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) { if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
kfree_skb(skb); kfree_skb(skb);
} else { } else {
...@@ -318,6 +287,7 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d ...@@ -318,6 +287,7 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d
if (sock_queue_rcv_skb(sk, skb) != 0) if (sock_queue_rcv_skb(sk, skb) != 0)
kfree_skb(skb); kfree_skb(skb);
} }
release_sock(sk);
} else { } else {
kfree_skb(skb); kfree_skb(skb);
} }
...@@ -349,9 +319,10 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d ...@@ -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) { 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 * Process the frame. If it is queued up internally it
* free it immediately. This routine itself wakes the user context layers so we * returns one otherwise we free it immediately. This
* do no further work * routine itself wakes the user context layers so we do
* no further work
*/ */
if (ax25_process_rx_frame(ax25, skb, type, dama) == 0) if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
kfree_skb(skb); kfree_skb(skb);
...@@ -363,7 +334,8 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d ...@@ -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) */ /* 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 * Never reply to a DM. Also ignore any connects for
* addresses that are not our interfaces and not a socket. * 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 ...@@ -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); sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
if (sk != NULL) { if (sk != NULL) {
if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, ax25_dev)) == NULL) { if (sk->ack_backlog == sk->max_ack_backlog ||
if (mine) ax25_return_dm(dev, &src, &dest, &dp); (make = ax25_make_new(sk, ax25_dev)) == NULL) {
if (mine)
ax25_return_dm(dev, &src, &dest, &dp);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
...@@ -486,4 +461,3 @@ int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -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); return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
} }
/* /*
* 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) 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.
*
* History
* AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -112,8 +105,8 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -112,8 +105,8 @@ int ax25_rebuild_header(struct sk_buff *skb)
unsigned char *bp = skb->data; unsigned char *bp = skb->data;
struct net_device *dev; struct net_device *dev;
ax25_address *src, *dst; ax25_address *src, *dst;
ax25_route *route;
ax25_dev *ax25_dev; ax25_dev *ax25_dev;
ax25_route _route, *route = &_route;
dst = (ax25_address *)(bp + 1); dst = (ax25_address *)(bp + 1);
src = (ax25_address *)(bp + 8); src = (ax25_address *)(bp + 8);
...@@ -121,14 +114,15 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -121,14 +114,15 @@ int ax25_rebuild_header(struct sk_buff *skb)
if (arp_find(bp + 1, skb)) if (arp_find(bp + 1, skb))
return 1; return 1;
route = ax25_rt_find_route(dst, NULL); route = ax25_rt_find_route(route, dst, NULL);
dev = route->dev; dev = route->dev;
if (dev == NULL) if (dev == NULL)
dev = skb->dev; dev = skb->dev;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
return 1; goto put;
}
if (bp[16] == AX25_P_IP) { if (bp[16] == AX25_P_IP) {
if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { 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) ...@@ -153,7 +147,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) { if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
kfree_skb(skb); kfree_skb(skb);
return 1; goto put;
} }
if (skb->sk != NULL) if (skb->sk != NULL)
...@@ -170,7 +164,7 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -170,7 +164,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
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); &dst_c, route->digipeat, dev);
return 1; goto put;
} }
} }
...@@ -187,7 +181,7 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -187,7 +181,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
if (route->digipeat != NULL) { if (route->digipeat != NULL) {
if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
kfree_skb(skb); kfree_skb(skb);
return 1; goto put;
} }
skb = ourskb; skb = ourskb;
...@@ -197,6 +191,9 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -197,6 +191,9 @@ int ax25_rebuild_header(struct sk_buff *skb)
ax25_queue_xmit(skb); ax25_queue_xmit(skb);
put:
ax25_put_route(route);
return 1; return 1;
} }
......
/* /*
* 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)
* This module: * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* 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)
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -44,6 +18,7 @@ ...@@ -44,6 +18,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/spinlock.h>
#include <linux/net.h> #include <linux/net.h>
#include <net/ax25.h> #include <net/ax25.h>
#include <linux/inet.h> #include <linux/inet.h>
...@@ -57,6 +32,8 @@ ...@@ -57,6 +32,8 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/interrupt.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_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; ax25_dev *ax25_dev;
...@@ -138,7 +115,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) ...@@ -138,7 +115,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
struct sk_buff *skbn; struct sk_buff *skbn;
unsigned char *p; unsigned char *p;
int frontlen, len, fragno, ka9qfrag, first = 1; int frontlen, len, fragno, ka9qfrag, first = 1;
long flags;
if ((skb->len - 1) > paclen) { if ((skb->len - 1) > paclen) {
if (*skb->data == AX25_P_TEXT) { if (*skb->data == AX25_P_TEXT) {
...@@ -155,11 +131,9 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) ...@@ -155,11 +131,9 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
frontlen = skb_headroom(skb); /* Address space + CTRL */ frontlen = skb_headroom(skb); /* Address space + CTRL */
while (skb->len > 0) { while (skb->len > 0) {
save_flags(flags); spin_lock_bh(&ax25_frag_lock);
cli();
if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { 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"); printk(KERN_CRIT "AX.25: ax25_output - out of memory\n");
return; return;
} }
...@@ -167,7 +141,7 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) ...@@ -167,7 +141,7 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
if (skb->sk != NULL) if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk); skb_set_owner_w(skbn, skb->sk);
restore_flags(flags); spin_unlock_bh(&ax25_frag_lock);
len = (paclen > skb->len) ? skb->len : paclen; len = (paclen > skb->len) ? skb->len : paclen;
......
/* /*
* 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)
* This module: * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org)
* This module is free software; you can redistribute it and/or * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* modify it under the terms of the GNU General Public License * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
* as published by the Free Software Foundation; either version * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
* 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/
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/timer.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -56,6 +27,7 @@ ...@@ -56,6 +27,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <net/sock.h> #include <net/sock.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -65,8 +37,9 @@ ...@@ -65,8 +37,9 @@
#include <linux/init.h> #include <linux/init.h>
static ax25_route *ax25_route_list; 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 * 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) ...@@ -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) 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) { while (ax25_rt != NULL) {
s = ax25_rt; s = ax25_rt;
ax25_rt = ax25_rt->next; ax25_rt = ax25_rt->next;
...@@ -111,129 +86,196 @@ void ax25_rt_device_down(struct net_device *dev) ...@@ -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 *ax25_rt;
ax25_route *s, *t, *ax25_rt;
struct ax25_routes_struct route;
struct ax25_route_opt_struct rt_option;
ax25_dev *ax25_dev; ax25_dev *ax25_dev;
int i; int i;
switch (cmd) { if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL)
case SIOCADDRT:
if (copy_from_user(&route, arg, sizeof(route)))
return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
return -EINVAL; return -EINVAL;
if (route.digi_count > AX25_MAX_DIGIS) if (route->digi_count > AX25_MAX_DIGIS)
return -EINVAL; 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) { 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) { if (ax25_rt->digipeat != NULL) {
kfree(ax25_rt->digipeat); kfree(ax25_rt->digipeat);
ax25_rt->digipeat = NULL; ax25_rt->digipeat = NULL;
} }
if (route.digi_count != 0) { if (route->digi_count != 0) {
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
write_unlock(&ax25_route_lock);
return -ENOMEM; return -ENOMEM;
}
ax25_rt->digipeat->lastrepeat = -1; ax25_rt->digipeat->lastrepeat = -1;
ax25_rt->digipeat->ndigi = route.digi_count; ax25_rt->digipeat->ndigi = route->digi_count;
for (i = 0; i < route.digi_count; i++) { for (i = 0; i < route->digi_count; i++) {
ax25_rt->digipeat->repeated[i] = 0; ax25_rt->digipeat->repeated[i] = 0;
ax25_rt->digipeat->calls[i] = route.digi_addr[i]; ax25_rt->digipeat->calls[i] = route->digi_addr[i];
} }
} }
return 0; return 0;
} }
ax25_rt = ax25_rt->next;
} }
if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL)
if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) {
write_unlock(&ax25_route_lock);
return -ENOMEM; return -ENOMEM;
ax25_rt->callsign = route.dest_addr; }
atomic_set(&ax25_rt->ref, 0);
ax25_rt->callsign = route->dest_addr;
ax25_rt->dev = ax25_dev->dev; ax25_rt->dev = ax25_dev->dev;
ax25_rt->digipeat = NULL; ax25_rt->digipeat = NULL;
ax25_rt->ip_mode = ' '; 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) { if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
write_unlock(&ax25_route_lock);
kfree(ax25_rt); kfree(ax25_rt);
return -ENOMEM; return -ENOMEM;
} }
ax25_rt->digipeat->lastrepeat = -1; ax25_rt->digipeat->lastrepeat = -1;
ax25_rt->digipeat->ndigi = route.digi_count; ax25_rt->digipeat->ndigi = route->digi_count;
for (i = 0; i < route.digi_count; i++) { for (i = 0; i < route->digi_count; i++) {
ax25_rt->digipeat->repeated[i] = 0; 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_rt->next = ax25_route_list;
ax25_route_list = ax25_rt; ax25_route_list = ax25_rt;
restore_flags(flags); write_unlock(&ax25_route_lock);
break;
case SIOCDELRT: return 0;
if (copy_from_user(&route, arg, sizeof(route))) }
return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) 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; return -EINVAL;
write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list; ax25_rt = ax25_route_list;
while (ax25_rt != NULL) { while (ax25_rt != NULL) {
s = ax25_rt; s = ax25_rt;
ax25_rt = ax25_rt->next; ax25_rt = ax25_rt->next;
if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) { if (s->dev == ax25_dev->dev &&
ax25cmp(&route->dest_addr, &s->callsign) == 0) {
if (ax25_route_list == s) { if (ax25_route_list == s) {
ax25_route_list = s->next; ax25_route_list = s->next;
if (s->digipeat != NULL) ax25_rt_destroy(s);
kfree(s->digipeat);
kfree(s);
} else { } else {
for (t = ax25_route_list; t != NULL; t = t->next) { for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) { if (t->next == s) {
t->next = s->next; t->next = s->next;
if (s->digipeat != NULL) ax25_rt_destroy(s);
kfree(s->digipeat);
kfree(s);
break; break;
} }
} }
} }
} }
} }
break; write_unlock(&ax25_route_lock);
case SIOCAX25OPTRT: return 0;
if (copy_from_user(&rt_option, arg, sizeof(rt_option))) }
return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL) 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; 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) { write_lock(&ax25_route_lock);
switch (rt_option.cmd) {
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: case AX25_SET_RT_IPMODE:
switch (rt_option.arg) { switch (rt_option->arg) {
case ' ': case ' ':
case 'D': case 'D':
case 'V': case 'V':
ax25_rt->ip_mode = rt_option.arg; ax25_rt->ip_mode = rt_option->arg;
break; break;
default: default:
return -EINVAL; err = -EINVAL;
goto out;
} }
break; break;
default: default:
return -EINVAL; err = -EINVAL;
goto out;
} }
} }
ax25_rt = ax25_rt->next;
} }
break;
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: default:
return -EINVAL; return -EINVAL;
} }
return 0;
} }
int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
...@@ -245,7 +287,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -245,7 +287,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
char *callsign; char *callsign;
int i; int i;
cli(); read_lock(&ax25_route_lock);
len += sprintf(buffer, "callsign dev mode digipeaters\n"); len += sprintf(buffer, "callsign dev mode digipeaters\n");
...@@ -286,26 +328,29 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -286,26 +328,29 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
if (pos > offset + length) if (pos > offset + length)
break; break;
} }
read_unlock(&ax25_route_lock);
sti();
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
if (len > length) len = length; if (len > length)
len = length;
return len; return len;
} }
/* /*
* Find AX.25 route * 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_spe_rt = NULL;
ax25_route *ax25_def_rt = NULL; ax25_route *ax25_def_rt = NULL;
ax25_route *ax25_rt; ax25_route *ax25_rt;
read_lock(&ax25_route_lock);
/* /*
* Bind to the physical interface we heard them on, or the default * Bind to the physical interface we heard them on, or the default
* route if none is found; * route if none is found;
...@@ -324,10 +369,16 @@ static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) ...@@ -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) if (ax25_spe_rt != NULL)
return ax25_spe_rt; ax25_rt = ax25_spe_rt;
if (ax25_rt != NULL)
atomic_inc(&ax25_rt->ref);
return ax25_def_rt; read_unlock(&ax25_route_lock);
return ax25_rt;
} }
/* /*
...@@ -355,24 +406,31 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) ...@@ -355,24 +406,31 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
{ {
ax25_route *ax25_rt; ax25_route *ax25_rt;
ax25_address *call; 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; return -EHOSTUNREACH;
if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) {
return -EHOSTUNREACH; err = -EHOSTUNREACH;
goto put;
}
if ((call = ax25_findbyuid(current->euid)) == NULL) { if ((call = ax25_findbyuid(current->euid)) == NULL) {
if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) {
return -EPERM; err = -EPERM;
goto put;
}
call = (ax25_address *)ax25->ax25_dev->dev->dev_addr; call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
} }
ax25->source_addr = *call; ax25->source_addr = *call;
if (ax25_rt->digipeat != NULL) { if (ax25_rt->digipeat != NULL) {
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
return -ENOMEM; err = -ENOMEM;
goto put;
}
memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi)); memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
ax25_adjust_path(addr, ax25->digipeat); ax25_adjust_path(addr, ax25->digipeat);
} }
...@@ -380,31 +438,32 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) ...@@ -380,31 +438,32 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
if (ax25->sk != NULL) if (ax25->sk != NULL)
ax25->sk->zapped = 0; ax25->sk->zapped = 0;
put:
ax25_put_route(ax25_rt);
return 0; return 0;
} }
/* ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr,
* dl1bke 960117: build digipeater path struct net_device *dev)
* dl1bke 960301: use the default route if it exists
*/
ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev)
{ {
static ax25_route route;
ax25_route *ax25_rt; ax25_route *ax25_rt;
if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) { if ((ax25_rt = ax25_get_route(addr, dev)))
route.next = NULL;
route.callsign = *addr;
route.dev = dev;
route.digipeat = NULL;
route.ip_mode = ' ';
return &route;
}
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; struct sk_buff *skbn;
unsigned char *bp; unsigned char *bp;
...@@ -440,6 +499,7 @@ void __exit ax25_rt_free(void) ...@@ -440,6 +499,7 @@ void __exit ax25_rt_free(void)
{ {
ax25_route *s, *ax25_rt = ax25_route_list; ax25_route *s, *ax25_rt = ax25_route_list;
write_unlock(&ax25_route_lock);
while (ax25_rt != NULL) { while (ax25_rt != NULL) {
s = ax25_rt; s = ax25_rt;
ax25_rt = ax25_rt->next; ax25_rt = ax25_rt->next;
...@@ -449,4 +509,5 @@ void __exit ax25_rt_free(void) ...@@ -449,4 +509,5 @@ void __exit ax25_rt_free(void)
kfree(s); kfree(s);
} }
write_unlock(&ax25_route_lock);
} }
/* /*
* 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: * Most of this code is based on the SDL diagrams published in the 7th ARRL
* This module is free software; you can redistribute it and/or * Computer Networking Conference papers. The diagrams have mistakes in them,
* modify it under the terms of the GNU General Public License * but are mostly correct. Before you modify the code could you read the SDL
* as published by the Free Software Foundation; either version * diagrams as the code is not obvious and probably very easy to break.
* 2 of the License, or (at your option) any later version.
*
* Most of this code is based on the SDL diagrams published in the 7th
* ARRL Computer Networking Conference papers. The diagrams have mistakes
* in them, but are mostly correct. Before you modify the code could you
* read the SDL diagrams as the code is not obvious and probably very
* easy to break;
*
* History
* AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
* AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from
* the sock structure.
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
* Jonathan(G4KLX) Added IP mode registration.
* AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception.
* Upgraded state machine for SABME.
* Added arbitrary protocol id support.
* AX.25 031 Joerg(DL1BKE) Added DAMA support
* HaJo(DD8NE) Added Idle Disc Timer T5
* Joerg(DL1BKE) Renamed it to "IDLE" with a slightly
* different behaviour. Fixed defrag
* routine (I hope)
* AX.25 032 Darryl(G7LED) AX.25 segmentation fixed.
* AX.25 033 Jonathan(G4KLX) Remove auto-router.
* Modularisation changes.
* AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c.
* AX.25 037 Jonathan(G4KLX) New timer architecture.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -142,7 +119,8 @@ static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frame ...@@ -142,7 +119,8 @@ static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frame
case AX25_DM: case AX25_DM:
case AX25_UA: case AX25_UA:
if (pf) ax25_disconnect(ax25, 0); if (pf)
ax25_disconnect(ax25, 0);
break; break;
case AX25_I: case AX25_I:
...@@ -397,7 +375,8 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame ...@@ -397,7 +375,8 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame
} }
ax25_frames_acked(ax25, nr); ax25_frames_acked(ax25, nr);
if (ax25->condition & AX25_COND_OWN_RX_BUSY) { if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
if (pf) ax25_std_enquiry_response(ax25); if (pf)
ax25_std_enquiry_response(ax25);
break; break;
} }
if (ns == ax25->vr) { if (ns == ax25->vr) {
......
/* /*
* 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) 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
* AX.25 036 Jonathan(G4KLX) Split from ax25_out.c.
* AX.25 037 Jonathan(G4KLX) New timer architecture.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
......
/* /*
* 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)
* This module: * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* This module is free software; you can redistribute it and/or * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* History
* AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
* AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
* sock structure.
* AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
* AX.25 031 Joerg(DL1BKE) Added DAMA support
* AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
* AX.25 033 Jonathan(G4KLX) Modularisation functions.
* AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
* AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
* AX.25 037 Jonathan(G4KLX) New timer architecture.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -47,7 +34,6 @@ ...@@ -47,7 +34,6 @@
void ax25_std_heartbeat_expiry(ax25_cb *ax25) void ax25_std_heartbeat_expiry(ax25_cb *ax25)
{ {
switch (ax25->state) { switch (ax25->state) {
case AX25_STATE_0: case AX25_STATE_0:
/* Magic here: If we listen() and a new link dies before it /* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */ is accepted() it isn't 'dead' so doesn't get removed. */
......
/* /*
* 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)
* This module: * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* This module is free software; you can redistribute it and/or * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
......
/* /*
* 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)
* This module: * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi)
* This module is free software; you can redistribute it and/or * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk)
* modify it under the terms of the GNU General Public License * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* as published by the Free Software Foundation; either version * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
* 2 of the License, or (at your option) any later version. * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org)
*
* 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).
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -152,12 +139,15 @@ unsigned long ax25_display_timer(struct timer_list *timer) ...@@ -152,12 +139,15 @@ unsigned long ax25_display_timer(struct timer_list *timer)
static void ax25_heartbeat_expiry(unsigned long param) static void ax25_heartbeat_expiry(unsigned long param)
{ {
ax25_cb *ax25 = (ax25_cb *)param;
int proto = AX25_PROTO_STD_SIMPLEX; int proto = AX25_PROTO_STD_SIMPLEX;
ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
if (ax25->ax25_dev) if (ax25->ax25_dev)
proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]; proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL];
bh_lock_sock(sk);
switch (proto) { switch (proto) {
case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX: case AX25_PROTO_STD_DUPLEX:
...@@ -173,12 +163,15 @@ static void ax25_heartbeat_expiry(unsigned long param) ...@@ -173,12 +163,15 @@ static void ax25_heartbeat_expiry(unsigned long param)
break; break;
#endif #endif
} }
bh_unlock_sock(sk);
} }
static void ax25_t1timer_expiry(unsigned long param) static void ax25_t1timer_expiry(unsigned long param)
{ {
ax25_cb *ax25 = (ax25_cb *)param; ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX: case AX25_PROTO_STD_DUPLEX:
...@@ -192,12 +185,15 @@ static void ax25_t1timer_expiry(unsigned long param) ...@@ -192,12 +185,15 @@ static void ax25_t1timer_expiry(unsigned long param)
break; break;
#endif #endif
} }
bh_unlock_sock(sk);
} }
static void ax25_t2timer_expiry(unsigned long param) static void ax25_t2timer_expiry(unsigned long param)
{ {
ax25_cb *ax25 = (ax25_cb *)param; ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX: case AX25_PROTO_STD_DUPLEX:
...@@ -211,12 +207,15 @@ static void ax25_t2timer_expiry(unsigned long param) ...@@ -211,12 +207,15 @@ static void ax25_t2timer_expiry(unsigned long param)
break; break;
#endif #endif
} }
bh_unlock_sock(sk);
} }
static void ax25_t3timer_expiry(unsigned long param) static void ax25_t3timer_expiry(unsigned long param)
{ {
ax25_cb *ax25 = (ax25_cb *)param; ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX: case AX25_PROTO_STD_DUPLEX:
...@@ -232,12 +231,15 @@ static void ax25_t3timer_expiry(unsigned long param) ...@@ -232,12 +231,15 @@ static void ax25_t3timer_expiry(unsigned long param)
break; break;
#endif #endif
} }
bh_unlock_sock(sk);
} }
static void ax25_idletimer_expiry(unsigned long param) static void ax25_idletimer_expiry(unsigned long param)
{ {
ax25_cb *ax25 = (ax25_cb *)param; ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX: case AX25_PROTO_STD_DUPLEX:
...@@ -253,4 +255,5 @@ static void ax25_idletimer_expiry(unsigned long param) ...@@ -253,4 +255,5 @@ static void ax25_idletimer_expiry(unsigned long param)
break; break;
#endif #endif
} }
bh_unlock_sock(sk);
} }
/* /*
* 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) 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.
*
* History
* AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -23,6 +16,7 @@ ...@@ -23,6 +16,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/net.h> #include <linux/net.h>
#include <linux/spinlock.h>
#include <net/ax25.h> #include <net/ax25.h>
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
...@@ -47,17 +41,23 @@ ...@@ -47,17 +41,23 @@
*/ */
static ax25_uid_assoc *ax25_uid_list; static ax25_uid_assoc *ax25_uid_list;
static rwlock_t ax25_uid_lock = RW_LOCK_UNLOCKED;
int ax25_uid_policy = 0; int ax25_uid_policy = 0;
ax25_address *ax25_findbyuid(uid_t uid) ax25_address *ax25_findbyuid(uid_t uid)
{ {
ax25_uid_assoc *ax25_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) { for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
if (ax25_uid->uid == uid) if (ax25_uid->uid == uid) {
return &ax25_uid->call; res = &ax25_uid->call;
break;
}
} }
read_unlock(&ax25_uid_lock);
return NULL; return NULL;
} }
...@@ -65,15 +65,21 @@ ax25_address *ax25_findbyuid(uid_t uid) ...@@ -65,15 +65,21 @@ ax25_address *ax25_findbyuid(uid_t uid)
int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
{ {
ax25_uid_assoc *s, *ax25_uid; ax25_uid_assoc *s, *ax25_uid;
unsigned long flags; unsigned long res;
switch (cmd) { switch (cmd) {
case SIOCAX25GETUID: case SIOCAX25GETUID:
res = -ENOENT;
read_lock(&ax25_uid_lock);
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
return ax25_uid->uid; res = ax25_uid->uid;
break;
} }
return -ENOENT; }
read_unlock(&ax25_uid_lock);
return res;
case SIOCAX25ADDUID: case SIOCAX25ADDUID:
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
...@@ -84,40 +90,48 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) ...@@ -84,40 +90,48 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
return -EINVAL; return -EINVAL;
if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL) if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL)
return -ENOMEM; return -ENOMEM;
ax25_uid->uid = sax->sax25_uid; ax25_uid->uid = sax->sax25_uid;
ax25_uid->call = sax->sax25_call; ax25_uid->call = sax->sax25_call;
save_flags(flags); cli();
write_lock(&ax25_uid_lock);
ax25_uid->next = ax25_uid_list; ax25_uid->next = ax25_uid_list;
ax25_uid_list = ax25_uid; ax25_uid_list = ax25_uid;
restore_flags(flags); write_unlock(&ax25_uid_lock);
return 0; return 0;
case SIOCAX25DELUID: case SIOCAX25DELUID:
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
write_lock(&ax25_uid_lock);
for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
break; break;
} }
if (ax25_uid == NULL) }
if (ax25_uid == NULL) {
write_unlock(&ax25_uid_lock);
return -ENOENT; return -ENOENT;
save_flags(flags); cli(); }
if ((s = ax25_uid_list) == ax25_uid) { if ((s = ax25_uid_list) == ax25_uid) {
ax25_uid_list = s->next; ax25_uid_list = s->next;
restore_flags(flags); write_unlock(&ax25_uid_lock);
kfree(ax25_uid); kfree(ax25_uid);
return 0; return 0;
} }
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == ax25_uid) { if (s->next == ax25_uid) {
s->next = ax25_uid->next; s->next = ax25_uid->next;
restore_flags(flags); write_unlock(&ax25_uid_lock);
kfree(ax25_uid); kfree(ax25_uid);
return 0; return 0;
} }
s = s->next; s = s->next;
} }
restore_flags(flags); write_unlock(&ax25_uid_lock);
return -ENOENT; return -ENOENT;
default: default:
...@@ -134,8 +148,7 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -134,8 +148,7 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
off_t pos = 0; off_t pos = 0;
off_t begin = 0; off_t begin = 0;
cli(); read_lock(&ax25_uid_lock);
len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy); len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
for (pt = ax25_uid_list; pt != NULL; pt = pt->next) { 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) ...@@ -151,13 +164,13 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
if (pos > offset + length) if (pos > offset + length)
break; break;
} }
read_unlock(&ax25_uid_lock);
sti();
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= offset - begin; len -= offset - begin;
if (len > length) len = length; if (len > length)
len = length;
return len; return len;
} }
...@@ -167,12 +180,16 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -167,12 +180,16 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
*/ */
void __exit ax25_uid_free(void) 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) { while (ax25_uid != NULL) {
s = ax25_uid; s = ax25_uid;
ax25_uid = ax25_uid->next; ax25_uid = ax25_uid->next;
kfree(s); kfree(s);
} }
ax25_uid_list = NULL;
write_unlock(&ax25_uid_lock);
} }
/* -*- 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. * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com)
* Added /proc/sys/net/ax25 directory entry (empty =) ). [MS]
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/spinlock.h>
#include <net/ax25.h> #include <net/ax25.h>
static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1}; static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1};
...@@ -105,6 +107,7 @@ void ax25_register_sysctl(void) ...@@ -105,6 +107,7 @@ void ax25_register_sysctl(void)
ax25_dev *ax25_dev; ax25_dev *ax25_dev;
int n, k; 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) 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); ax25_table_size += sizeof(ctl_table);
...@@ -119,6 +122,7 @@ void ax25_register_sysctl(void) ...@@ -119,6 +122,7 @@ void ax25_register_sysctl(void)
while (n--) while (n--)
kfree(ax25_table[n].child); kfree(ax25_table[n].child);
kfree(ax25_table); kfree(ax25_table);
spin_unlock_bh(&ax25_dev_lock);
return; return;
} }
memcpy(child, ax25_param_table, sizeof(ax25_param_table)); memcpy(child, ax25_param_table, sizeof(ax25_param_table));
...@@ -144,6 +148,7 @@ void ax25_register_sysctl(void) ...@@ -144,6 +148,7 @@ void ax25_register_sysctl(void)
n++; n++;
} }
spin_unlock_bh(&ax25_dev_lock);
ax25_dir_table[0].child = ax25_table; ax25_dir_table[0].child = ax25_table;
......
/* /*
* 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 * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* This module: * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk)
* 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
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -82,7 +57,8 @@ int sysctl_netrom_link_fails_count = NR_DEFAULT_FAILS; ...@@ -82,7 +57,8 @@ int sysctl_netrom_link_fails_count = NR_DEFAULT_FAILS;
static unsigned short circuit = 0x101; 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; static struct proto_ops nr_proto_ops;
...@@ -123,27 +99,26 @@ decmod: MOD_DEC_USE_COUNT; ...@@ -123,27 +99,26 @@ decmod: MOD_DEC_USE_COUNT;
static void nr_remove_socket(struct sock *sk) static void nr_remove_socket(struct sock *sk)
{ {
struct sock *s; struct sock *s;
unsigned long flags;
save_flags(flags); cli(); spin_lock_bh(&nr_list_lock);
if ((s = nr_list) == sk) { if ((s = nr_list) == sk) {
nr_list = s->next; nr_list = s->next;
restore_flags(flags); spin_unlock_bh(&nr_list_lock);
return; return;
} }
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == sk) { if (s->next == sk) {
s->next = sk->next; s->next = sk->next;
restore_flags(flags); spin_unlock_bh(&nr_list_lock);
return; return;
} }
s = s->next; 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) ...@@ -153,10 +128,12 @@ static void nr_kill_by_device(struct net_device *dev)
{ {
struct sock *s; struct sock *s;
spin_lock_bh(&nr_list_lock);
for (s = nr_list; s != NULL; s = s->next) { for (s = nr_list; s != NULL; s = s->next) {
if (nr_sk(s)->device == dev) if (nr_sk(s)->device == dev)
nr_disconnect(s, ENETUNREACH); 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 ...@@ -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) static void nr_insert_socket(struct sock *sk)
{ {
unsigned long flags; spin_lock_bh(&nr_list_lock);
save_flags(flags); cli();
sk->next = nr_list; sk->next = nr_list;
nr_list = sk; nr_list = sk;
spin_unlock_bh(&nr_list_lock);
restore_flags(flags);
} }
/* /*
...@@ -196,21 +169,18 @@ static void nr_insert_socket(struct sock *sk) ...@@ -196,21 +169,18 @@ static void nr_insert_socket(struct sock *sk)
*/ */
static struct sock *nr_find_listener(ax25_address *addr) static struct sock *nr_find_listener(ax25_address *addr)
{ {
unsigned long flags;
struct sock *s; struct sock *s;
save_flags(flags); spin_lock_bh(&nr_list_lock);
cli();
for (s = nr_list; s != NULL; s = s->next) { for (s = nr_list; s != NULL; s = s->next) {
if (!ax25cmp(&nr_sk(s)->source_addr, addr) && if (!ax25cmp(&nr_sk(s)->source_addr, addr) &&
s->state == TCP_LISTEN) { s->state == TCP_LISTEN) {
restore_flags(flags); spin_unlock_bh(&nr_list_lock);
return s; return s;
} }
} }
spin_unlock_bh(&nr_list_lock);
restore_flags(flags);
return NULL; return NULL;
} }
...@@ -220,21 +190,17 @@ static struct sock *nr_find_listener(ax25_address *addr) ...@@ -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) static struct sock *nr_find_socket(unsigned char index, unsigned char id)
{ {
struct sock *s; 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) { for (s = nr_list; s != NULL; s = s->next) {
nr_cb *nr = nr_sk(s); nr_cb *nr = nr_sk(s);
if (nr->my_index == index && nr->my_id == id) { if (nr->my_index == index && nr->my_id == id) {
restore_flags(flags); spin_unlock_bh(&nr_list_lock);
return s; return s;
} }
} }
spin_unlock_bh(&nr_list_lock);
restore_flags(flags);
return NULL; return NULL;
} }
...@@ -242,25 +208,22 @@ static struct sock *nr_find_socket(unsigned char index, unsigned char id) ...@@ -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. * 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; 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) { for (s = nr_list; s != NULL; s = s->next) {
nr_cb *nr = nr_sk(s); nr_cb *nr = nr_sk(s);
if (nr->your_index == index && nr->your_id == id && if (nr->your_index == index && nr->your_id == id &&
!ax25cmp(&nr->dest_addr, dest)) { !ax25cmp(&nr->dest_addr, dest)) {
restore_flags(flags); spin_unlock_bh(&nr_list_lock);
return s; return s;
} }
} }
spin_unlock_bh(&nr_list_lock);
restore_flags(flags);
return NULL; return NULL;
} }
...@@ -301,17 +264,16 @@ static void nr_destroy_timer(unsigned long data) ...@@ -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 * This is called from user mode and the timers. Thus it protects itself
* interrupt users but doesn't worry about being called during work. * against interrupt users but doesn't worry about being called during
* Once it is removed from the queue no interrupt or bottom half will * work. Once it is removed from the queue no interrupt or bottom half
* touch it and we are (fairly 8-) ) safe. * 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; struct sk_buff *skb;
unsigned long flags;
save_flags(flags); cli(); nr_remove_socket(sk);
nr_stop_heartbeat(sk); nr_stop_heartbeat(sk);
nr_stop_t1timer(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 ...@@ -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_t4timer(sk);
nr_stop_idletimer(sk); nr_stop_idletimer(sk);
nr_remove_socket(sk);
nr_clear_queues(sk); /* Flush the queues */ nr_clear_queues(sk); /* Flush the queues */
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { 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 ...@@ -342,8 +303,6 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer
} else { } else {
nr_free_sock(sk); nr_free_sock(sk);
} }
restore_flags(flags);
} }
/* /*
...@@ -567,7 +526,6 @@ static int nr_release(struct socket *sock) ...@@ -567,7 +526,6 @@ static int nr_release(struct socket *sock)
nr = nr_sk(sk); nr = nr_sk(sk);
switch (nr->state) { switch (nr->state) {
case NR_STATE_0: case NR_STATE_0:
case NR_STATE_1: case NR_STATE_1:
case NR_STATE_2: case NR_STATE_2:
...@@ -732,78 +690,98 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, ...@@ -732,78 +690,98 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
return -EINPROGRESS; 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) { if (sk->state == TCP_SYN_SENT) {
interruptible_sleep_on(sk->sleep); struct task_struct *tsk = current;
if (signal_pending(current)) { DECLARE_WAITQUEUE(wait, tsk);
sti();
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; return -ERESTARTSYS;
} }
current->state = TASK_RUNNING;
remove_wait_queue(sk->sleep, &wait);
} }
if (sk->state != TCP_ESTABLISHED) { if (sk->state != TCP_ESTABLISHED) {
sti();
sock->state = SS_UNCONNECTED; sock->state = SS_UNCONNECTED;
return sock_error(sk); /* Always set at this point */ return sock_error(sk); /* Always set at this point */
} }
sock->state = SS_CONNECTED; sock->state = SS_CONNECTED;
sti();
return 0; return 0;
} }
static int nr_accept(struct socket *sock, struct socket *newsock, int flags) static int nr_accept(struct socket *sock, struct socket *newsock, int flags)
{ {
struct sock *sk; struct task_struct *tsk = current;
struct sock *newsk; DECLARE_WAITQUEUE(wait, tsk);
struct sk_buff *skb; struct sk_buff *skb;
struct sock *newsk;
struct sock *sk;
int err = 0;
if ((sk = sock->sk) == NULL) if ((sk = sock->sk) == NULL)
return -EINVAL; return -EINVAL;
if (sk->type != SOCK_SEQPACKET) lock_sock(sk);
return -EOPNOTSUPP; if (sk->type != SOCK_SEQPACKET) {
err = -EOPNOTSUPP;
goto out;
}
if (sk->state != TCP_LISTEN) if (sk->state != TCP_LISTEN) {
return -EINVAL; err = -EINVAL;
goto out;
}
/* /*
* The write queue this time is holding sockets ready to use * The write queue this time is holding sockets ready to use
* hooked into the SABM we saved * hooked into the SABM we saved
*/ */
do { add_wait_queue(sk->sleep, &wait);
cli(); for (;;) {
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { skb = skb_dequeue(&sk->receive_queue);
if (flags & O_NONBLOCK) { if (skb)
sti(); break;
current->state = TASK_INTERRUPTIBLE;
release_sock(sk);
if (flags & O_NONBLOCK)
return -EWOULDBLOCK; return -EWOULDBLOCK;
if (!signal_pending(tsk)) {
schedule();
lock_sock(sk);
continue;
} }
interruptible_sleep_on(sk->sleep);
if (signal_pending(current)) {
sti();
return -ERESTARTSYS; return -ERESTARTSYS;
} }
} current->state = TASK_RUNNING;
} while (skb == NULL); remove_wait_queue(sk->sleep, &wait);
newsk = skb->sk; newsk = skb->sk;
newsk->pair = NULL; newsk->pair = NULL;
newsk->socket = newsock; newsk->socket = newsock;
newsk->sleep = &newsock->wait; newsk->sleep = &newsock->wait;
sti();
/* Now attach up the new socket */ /* Now attach up the new socket */
kfree_skb(skb); kfree_skb(skb);
sk->ack_backlog--; sk->ack_backlog--;
newsock->sk = newsk; newsock->sk = newsk;
return 0; out:
return err;
} }
static int nr_getname(struct socket *sock, struct sockaddr *uaddr, static int nr_getname(struct socket *sock, struct sockaddr *uaddr,
...@@ -1174,7 +1152,6 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -1174,7 +1152,6 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return dev_ioctl(cmd, (void *)arg); return dev_ioctl(cmd, (void *)arg);
} }
/*NOTREACHED*/
return 0; return 0;
} }
...@@ -1187,7 +1164,7 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1187,7 +1164,7 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length)
off_t pos = 0; off_t pos = 0;
off_t begin = 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"); 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,14 +1217,15 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1240,14 +1217,15 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length)
break; break;
} }
sti(); spin_unlock_bh(&nr_list_lock);
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (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 = { static struct net_proto_family nr_family_ops = {
...@@ -1255,7 +1233,7 @@ static struct net_proto_family nr_family_ops = { ...@@ -1255,7 +1233,7 @@ static struct net_proto_family nr_family_ops = {
.create = nr_create, .create = nr_create,
}; };
static struct proto_ops SOCKOPS_WRAPPED(nr_proto_ops) = { static struct proto_ops nr_proto_ops = {
.family = PF_NETROM, .family = PF_NETROM,
.release = nr_release, .release = nr_release,
...@@ -1276,11 +1254,8 @@ static struct proto_ops SOCKOPS_WRAPPED(nr_proto_ops) = { ...@@ -1276,11 +1254,8 @@ static struct proto_ops SOCKOPS_WRAPPED(nr_proto_ops) = {
.sendpage = sock_no_sendpage, .sendpage = sock_no_sendpage,
}; };
#include <linux/smp_lock.h>
SOCKOPS_WRAP(nr_proto, PF_NETROM);
static struct notifier_block nr_dev_notifier = { static struct notifier_block nr_dev_notifier = {
.notifier_call =nr_device_event, .notifier_call = nr_device_event,
}; };
static struct net_device *dev_nr; static struct net_device *dev_nr;
......
/* /*
* 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 * Copyright 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.
*
* 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.
*/ */
#include <linux/config.h> #include <linux/config.h>
#define __NO_VERSION__ #define __NO_VERSION__
#include <linux/module.h> #include <linux/module.h>
......
/* /*
* 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 * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright Darryl Miles G7LED (dlm@g7led.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
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -88,10 +71,10 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) ...@@ -88,10 +71,10 @@ 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. * The handling of the timer(s) is in file nr_timer.c.
* Handling of state 0 and connection release is in netrom.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) { switch (frametype) {
case NR_CONNACK: { case NR_CONNACK: {
nr_cb *nr = nr_sk(sk); nr_cb *nr = nr_sk(sk);
...@@ -128,10 +111,10 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype ...@@ -128,10 +111,10 @@ 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 * The handling of the timer(s) is in file nr_timer.c
* Handling of state 0 and connection release is in netrom.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) { switch (frametype) {
case NR_CONNACK | NR_CHOKE_FLAG: case NR_CONNACK | NR_CHOKE_FLAG:
nr_disconnect(sk, ECONNRESET); nr_disconnect(sk, ECONNRESET);
break; break;
...@@ -168,7 +151,6 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype ...@@ -168,7 +151,6 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
ns = skb->data[17]; ns = skb->data[17];
switch (frametype) { switch (frametype) {
case NR_CONNREQ: case NR_CONNREQ:
nr_write_internal(sk, NR_CONNACK); nr_write_internal(sk, NR_CONNACK);
break; break;
......
/* /*
* 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
* This code REQUIRES 2.1.15 or higher/ NET3.038 * the Free Software Foundation; either version 2 of the License, or
* * (at your option) any later version.
* 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().
* *
* Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi)
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/timer.h> #include <linux/timer.h>
......
/* /*
* 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 * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright Darryl Miles G7LED (dlm@g7led.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.
*
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
......
/* /*
* 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 * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* This module: * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi)
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -47,12 +34,15 @@ ...@@ -47,12 +34,15 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h>
#include <net/netrom.h> #include <net/netrom.h>
static unsigned int nr_neigh_no = 1; static unsigned int nr_neigh_no = 1;
static struct nr_node *nr_node_list; static struct nr_node *nr_node_list;
static spinlock_t nr_node_lock;
static struct nr_neigh *nr_neigh_list; static struct nr_neigh *nr_neigh_list;
static spinlock_t nr_neigh_lock;
static void nr_remove_neigh(struct nr_neigh *); 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 ...@@ -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_node *nr_node;
struct nr_neigh *nr_neigh; struct nr_neigh *nr_neigh;
struct nr_route nr_route; struct nr_route nr_route;
unsigned long flags;
int i, found; int i, found;
if (nr_dev_get(nr) != NULL) /* Can't add routes to ourself */ 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 ...@@ -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)); memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
} }
save_flags(flags); spin_lock_bh(&nr_neigh_lock);
cli();
nr_neigh->next = nr_neigh_list; nr_neigh->next = nr_neigh_list;
nr_neigh_list = nr_neigh; nr_neigh_list = nr_neigh;
spin_unlock_bh(&nr_neigh_lock);
restore_flags(flags);
} }
if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) 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 ...@@ -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].obs_count = obs_count;
nr_node->routes[0].neighbour = nr_neigh; nr_node->routes[0].neighbour = nr_neigh;
save_flags(flags); spin_lock_bh(&nr_node_lock);
cli();
nr_node->next = nr_node_list; nr_node->next = nr_node_list;
nr_node_list = nr_node; nr_node_list = nr_node;
spin_unlock_bh(&nr_node_lock);
restore_flags(flags);
nr_neigh->count++; nr_neigh->count++;
...@@ -220,9 +203,14 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 ...@@ -220,9 +203,14 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
} }
if (nr_node->routes[2].quality > nr_node->routes[1].quality) { if (nr_node->routes[2].quality > nr_node->routes[1].quality) {
switch (nr_node->which) { switch (nr_node->which) {
case 1: nr_node->which = 2; break; case 1: nr_node->which = 2;
case 2: nr_node->which = 1; break; break;
default: break;
case 2: nr_node->which = 1;
break;
default:
break;
} }
nr_route = nr_node->routes[1]; nr_route = nr_node->routes[1];
nr_node->routes[1] = nr_node->routes[2]; nr_node->routes[1] = nr_node->routes[2];
...@@ -231,8 +219,12 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 ...@@ -231,8 +219,12 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
case 2: case 2:
if (nr_node->routes[1].quality > nr_node->routes[0].quality) { if (nr_node->routes[1].quality > nr_node->routes[0].quality) {
switch (nr_node->which) { switch (nr_node->which) {
case 0: nr_node->which = 1; break; case 0: nr_node->which = 1;
case 1: nr_node->which = 0; break; break;
case 1: nr_node->which = 0;
break;
default: break; default: break;
} }
nr_route = nr_node->routes[0]; nr_route = nr_node->routes[0];
...@@ -257,14 +249,11 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 ...@@ -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) static void nr_remove_node(struct nr_node *nr_node)
{ {
struct nr_node *s; struct nr_node *s;
unsigned long flags;
save_flags(flags);
cli();
spin_lock_bh(&nr_node_lock);
if ((s = nr_node_list) == nr_node) { if ((s = nr_node_list) == nr_node) {
nr_node_list = nr_node->next; nr_node_list = nr_node->next;
restore_flags(flags); spin_unlock_bh(&nr_node_lock);
kfree(nr_node); kfree(nr_node);
return; return;
} }
...@@ -272,7 +261,7 @@ static void nr_remove_node(struct nr_node *nr_node) ...@@ -272,7 +261,7 @@ static void nr_remove_node(struct nr_node *nr_node)
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == nr_node) { if (s->next == nr_node) {
s->next = nr_node->next; s->next = nr_node->next;
restore_flags(flags); spin_unlock_bh(&nr_node_lock);
kfree(nr_node); kfree(nr_node);
return; return;
} }
...@@ -280,20 +269,17 @@ static void nr_remove_node(struct nr_node *nr_node) ...@@ -280,20 +269,17 @@ static void nr_remove_node(struct nr_node *nr_node)
s = s->next; s = s->next;
} }
restore_flags(flags); spin_unlock_bh(&nr_node_lock);
} }
static void nr_remove_neigh(struct nr_neigh *nr_neigh) static void nr_remove_neigh(struct nr_neigh *nr_neigh)
{ {
struct nr_neigh *s; struct nr_neigh *s;
unsigned long flags;
save_flags(flags);
cli();
spin_lock_bh(&nr_neigh_lock);
if ((s = nr_neigh_list) == nr_neigh) { if ((s = nr_neigh_list) == nr_neigh) {
nr_neigh_list = nr_neigh->next; nr_neigh_list = nr_neigh->next;
restore_flags(flags); spin_unlock_bh(&nr_neigh_lock);
if (nr_neigh->digipeat != NULL) if (nr_neigh->digipeat != NULL)
kfree(nr_neigh->digipeat); kfree(nr_neigh->digipeat);
kfree(nr_neigh); kfree(nr_neigh);
...@@ -303,7 +289,7 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) ...@@ -303,7 +289,7 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh)
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == nr_neigh) { if (s->next == nr_neigh) {
s->next = nr_neigh->next; s->next = nr_neigh->next;
restore_flags(flags); spin_unlock_bh(&nr_neigh_lock);
if (nr_neigh->digipeat != NULL) if (nr_neigh->digipeat != NULL)
kfree(nr_neigh->digipeat); kfree(nr_neigh->digipeat);
kfree(nr_neigh); kfree(nr_neigh);
...@@ -312,8 +298,7 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) ...@@ -312,8 +298,7 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh)
s = s->next; s = s->next;
} }
spin_unlock_bh(&nr_neigh_lock);
restore_flags(flags);
} }
/* /*
...@@ -330,13 +315,15 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n ...@@ -330,13 +315,15 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
if (ax25cmp(callsign, &nr_node->callsign) == 0) if (ax25cmp(callsign, &nr_node->callsign) == 0)
break; 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) for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
break; break;
if (nr_neigh == NULL) return -EINVAL; if (nr_neigh == NULL)
return -EINVAL;
for (i = 0; i < nr_node->count; i++) { for (i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) { if (nr_node->routes[i].neighbour == nr_neigh) {
...@@ -373,7 +360,6 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n ...@@ -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) static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
{ {
struct nr_neigh *nr_neigh; struct nr_neigh *nr_neigh;
unsigned long flags;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) { if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) {
...@@ -404,13 +390,10 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net ...@@ -404,13 +390,10 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net
memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
} }
save_flags(flags); spin_lock_bh(&nr_neigh_lock);
cli();
nr_neigh->next = nr_neigh_list; nr_neigh->next = nr_neigh_list;
nr_neigh_list = nr_neigh; 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) ...@@ -457,7 +440,6 @@ static int nr_dec_obs(void)
for (i = 0; i < s->count; i++) { for (i = 0; i < s->count; i++) {
switch (s->routes[i].obs_count) { switch (s->routes[i].obs_count) {
case 0: /* A locked entry */ case 0: /* A locked entry */
break; break;
...@@ -622,7 +604,6 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) ...@@ -622,7 +604,6 @@ int nr_rt_ioctl(unsigned int cmd, void *arg)
struct net_device *dev; struct net_device *dev;
switch (cmd) { switch (cmd) {
case SIOCADDRT: case SIOCADDRT:
if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
return -EFAULT; return -EFAULT;
...@@ -758,8 +739,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -758,8 +739,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length)
off_t begin = 0; off_t begin = 0;
int i; 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"); 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) { for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) {
...@@ -788,8 +768,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -788,8 +768,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length)
if (pos > offset + length) if (pos > offset + length)
break; break;
} }
spin_unlock_bh(&nr_node_lock);
sti();
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
...@@ -807,8 +786,7 @@ 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; off_t begin = 0;
int i; int i;
cli(); spin_lock_bh(&nr_neigh_lock);
len += sprintf(buffer, "addr callsign dev qual lock count failed digipeaters\n"); 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) { 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) ...@@ -839,7 +817,7 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length)
break; break;
} }
sti(); spin_unlock_bh(&nr_neigh_lock);
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
......
/* /*
* 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 * Copyright 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.
*
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -165,7 +156,6 @@ void nr_write_internal(struct sock *sk, int frametype) ...@@ -165,7 +156,6 @@ void nr_write_internal(struct sock *sk, int frametype)
dptr = skb_put(skb, skb_tailroom(skb)); dptr = skb_put(skb, skb_tailroom(skb));
switch (frametype & 0x0F) { switch (frametype & 0x0F) {
case NR_CONNREQ: case NR_CONNREQ:
timeout = nr->t1 / HZ; timeout = nr->t1 / HZ;
*dptr++ = nr->my_index; *dptr++ = nr->my_index;
......
/* /*
* 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 * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org)
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -144,8 +136,8 @@ static void nr_heartbeat_expiry(unsigned long param) ...@@ -144,8 +136,8 @@ static void nr_heartbeat_expiry(unsigned long param)
struct sock *sk = (struct sock *)param; struct sock *sk = (struct sock *)param;
nr_cb *nr = nr_sk(sk); nr_cb *nr = nr_sk(sk);
bh_lock_sock(sk);
switch (nr->state) { switch (nr->state) {
case NR_STATE_0: case NR_STATE_0:
/* Magic here: If we listen() and a new link dies before it /* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */ is accepted() it isn't 'dead' so doesn't get removed. */
...@@ -171,6 +163,7 @@ static void nr_heartbeat_expiry(unsigned long param) ...@@ -171,6 +163,7 @@ static void nr_heartbeat_expiry(unsigned long param)
} }
nr_start_heartbeat(sk); nr_start_heartbeat(sk);
bh_unlock_sock(sk);
} }
static void nr_t2timer_expiry(unsigned long param) static void nr_t2timer_expiry(unsigned long param)
...@@ -178,17 +171,21 @@ 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; struct sock *sk = (struct sock *)param;
nr_cb *nr = nr_sk(sk); nr_cb *nr = nr_sk(sk);
bh_lock_sock(sk);
if (nr->condition & NR_COND_ACK_PENDING) { if (nr->condition & NR_COND_ACK_PENDING) {
nr->condition &= ~NR_COND_ACK_PENDING; nr->condition &= ~NR_COND_ACK_PENDING;
nr_enquiry_response(sk); nr_enquiry_response(sk);
} }
bh_unlock_sock(sk);
} }
static void nr_t4timer_expiry(unsigned long param) static void nr_t4timer_expiry(unsigned long param)
{ {
struct sock *sk = (struct sock *)param; struct sock *sk = (struct sock *)param;
bh_lock_sock(sk);
nr_sk(sk)->condition &= ~NR_COND_PEER_RX_BUSY; nr_sk(sk)->condition &= ~NR_COND_PEER_RX_BUSY;
bh_unlock_sock(sk);
} }
static void nr_idletimer_expiry(unsigned long param) static void nr_idletimer_expiry(unsigned long param)
...@@ -196,6 +193,8 @@ 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; struct sock *sk = (struct sock *)param;
nr_cb *nr = nr_sk(sk); nr_cb *nr = nr_sk(sk);
bh_lock_sock(sk);
nr_clear_queues(sk); nr_clear_queues(sk);
nr->n2count = 0; nr->n2count = 0;
...@@ -214,6 +213,7 @@ static void nr_idletimer_expiry(unsigned long param) ...@@ -214,6 +213,7 @@ static void nr_idletimer_expiry(unsigned long param)
sk->state_change(sk); sk->state_change(sk);
sk->dead = 1; sk->dead = 1;
bh_unlock_sock(sk);
} }
static void nr_t1timer_expiry(unsigned long param) static void nr_t1timer_expiry(unsigned long param)
...@@ -221,8 +221,8 @@ static void nr_t1timer_expiry(unsigned long param) ...@@ -221,8 +221,8 @@ static void nr_t1timer_expiry(unsigned long param)
struct sock *sk = (struct sock *)param; struct sock *sk = (struct sock *)param;
nr_cb *nr = nr_sk(sk); nr_cb *nr = nr_sk(sk);
bh_lock_sock(sk);
switch (nr->state) { switch (nr->state) {
case NR_STATE_1: case NR_STATE_1:
if (nr->n2count == nr->n2) { if (nr->n2count == nr->n2) {
nr_disconnect(sk, ETIMEDOUT); nr_disconnect(sk, ETIMEDOUT);
...@@ -255,4 +255,5 @@ static void nr_t1timer_expiry(unsigned long param) ...@@ -255,4 +255,5 @@ static void nr_t1timer_expiry(unsigned long param)
} }
nr_start_t1timer(sk); nr_start_t1timer(sk);
bh_unlock_sock(sk);
} }
/* -*- 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. * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com)
* Added /proc/sys/net/netrom directory entry (empty =) ). [MS]
*/ */
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/init.h> #include <linux/init.h>
......
/* /*
* 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)
* * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* This module: * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net)
* This module is free software; you can redistribute it and/or * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi)
* 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
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -33,6 +18,7 @@ ...@@ -33,6 +18,7 @@
#include <linux/in.h> #include <linux/in.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sockios.h> #include <linux/sockios.h>
...@@ -47,7 +33,7 @@ ...@@ -47,7 +33,7 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/termios.h> /* For TIOCINQ/OUTQ */ #include <linux/termios.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/notifier.h> #include <linux/notifier.h>
...@@ -71,6 +57,7 @@ int sysctl_rose_maximum_vcs = ROSE_DEFAULT_MAXVC; ...@@ -71,6 +57,7 @@ int sysctl_rose_maximum_vcs = ROSE_DEFAULT_MAXVC;
int sysctl_rose_window_size = ROSE_DEFAULT_WINDOW_SIZE; int sysctl_rose_window_size = ROSE_DEFAULT_WINDOW_SIZE;
static struct sock *rose_list; static struct sock *rose_list;
static spinlock_t rose_list_lock = SPIN_LOCK_UNLOCKED;
static struct proto_ops rose_proto_ops; static struct proto_ops rose_proto_ops;
...@@ -173,27 +160,24 @@ decmod: MOD_DEC_USE_COUNT; ...@@ -173,27 +160,24 @@ decmod: MOD_DEC_USE_COUNT;
static void rose_remove_socket(struct sock *sk) static void rose_remove_socket(struct sock *sk)
{ {
struct sock *s; struct sock *s;
unsigned long flags;
save_flags(flags); cli();
spin_lock_bh(&rose_list_lock);
if ((s = rose_list) == sk) { if ((s = rose_list) == sk) {
rose_list = s->next; rose_list = s->next;
restore_flags(flags); spin_unlock_bh(&rose_list_lock);
return; return;
} }
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == sk) { if (s->next == sk) {
s->next = sk->next; s->next = sk->next;
restore_flags(flags); spin_unlock_bh(&rose_list_lock);
return; return;
} }
s = s->next; s = s->next;
} }
spin_unlock_bh(&rose_list_lock);
restore_flags(flags);
} }
/* /*
...@@ -204,6 +188,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) ...@@ -204,6 +188,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh)
{ {
struct sock *s; struct sock *s;
spin_lock_bh(&rose_list_lock);
for (s = rose_list; s != NULL; s = s->next) { for (s = rose_list; s != NULL; s = s->next) {
rose_cb *rose = rose_sk(s); rose_cb *rose = rose_sk(s);
...@@ -213,6 +198,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) ...@@ -213,6 +198,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh)
rose->neighbour = NULL; rose->neighbour = NULL;
} }
} }
spin_unlock_bh(&rose_list_lock);
} }
/* /*
...@@ -222,6 +208,7 @@ static void rose_kill_by_device(struct net_device *dev) ...@@ -222,6 +208,7 @@ static void rose_kill_by_device(struct net_device *dev)
{ {
struct sock *s; struct sock *s;
spin_lock_bh(&rose_list_lock);
for (s = rose_list; s != NULL; s = s->next) { for (s = rose_list; s != NULL; s = s->next) {
rose_cb *rose = rose_sk(s); rose_cb *rose = rose_sk(s);
...@@ -231,12 +218,14 @@ static void rose_kill_by_device(struct net_device *dev) ...@@ -231,12 +218,14 @@ static void rose_kill_by_device(struct net_device *dev)
rose->device = NULL; rose->device = NULL;
} }
} }
spin_unlock_bh(&rose_list_lock);
} }
/* /*
* Handle device status changes. * 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; struct net_device *dev = (struct net_device *)ptr;
...@@ -261,14 +250,11 @@ static int rose_device_event(struct notifier_block *this, unsigned long event, v ...@@ -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) 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; sk->next = rose_list;
rose_list = sk; rose_list = sk;
spin_unlock_bh(&rose_list_lock);
restore_flags(flags);
} }
/* /*
...@@ -277,18 +263,16 @@ static void rose_insert_socket(struct sock *sk) ...@@ -277,18 +263,16 @@ static void rose_insert_socket(struct sock *sk)
*/ */
static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) static struct sock *rose_find_listener(rose_address *addr, ax25_address *call)
{ {
unsigned long flags;
struct sock *s; struct sock *s;
save_flags(flags); cli(); spin_lock_bh(&rose_list_lock);
for (s = rose_list; s != NULL; s = s->next) { for (s = rose_list; s != NULL; s = s->next) {
rose_cb *rose = rose_sk(s); rose_cb *rose = rose_sk(s);
if (!rosecmp(&rose->source_addr, addr) && if (!rosecmp(&rose->source_addr, addr) &&
!ax25cmp(&rose->source_call, call) && !ax25cmp(&rose->source_call, call) &&
!rose->source_ndigis && s->state == TCP_LISTEN) { !rose->source_ndigis && s->state == TCP_LISTEN) {
restore_flags(flags); spin_unlock_bh(&rose_list_lock);
return s; return s;
} }
} }
...@@ -299,12 +283,12 @@ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) ...@@ -299,12 +283,12 @@ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call)
if (!rosecmp(&rose->source_addr, addr) && if (!rosecmp(&rose->source_addr, addr) &&
!ax25cmp(&rose->source_call, &null_ax25_address) && !ax25cmp(&rose->source_call, &null_ax25_address) &&
s->state == TCP_LISTEN) { s->state == TCP_LISTEN) {
restore_flags(flags); spin_unlock_bh(&rose_list_lock);
return s; return s;
} }
} }
spin_unlock_bh(&rose_list_lock);
restore_flags(flags);
return NULL; return NULL;
} }
...@@ -314,20 +298,17 @@ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) ...@@ -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 *rose_find_socket(unsigned int lci, struct rose_neigh *neigh)
{ {
struct sock *s; 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) { for (s = rose_list; s != NULL; s = s->next) {
rose_cb *rose = rose_sk(s); rose_cb *rose = rose_sk(s);
if (rose->lci == lci && rose->neighbour == neigh) { if (rose->lci == lci && rose->neighbour == neigh) {
restore_flags(flags); spin_unlock_bh(&rose_list_lock);
return s; return s;
} }
} }
spin_unlock_bh(&rose_list_lock);
restore_flags(flags);
return NULL; return NULL;
} }
...@@ -366,23 +347,20 @@ static void rose_destroy_timer(unsigned long data) ...@@ -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 * This is called from user mode and the timers. Thus it protects itself
* interrupt users but doesn't worry about being called during work. * against interrupt users but doesn't worry about being called during
* Once it is removed from the queue no interrupt or bottom half will * work. Once it is removed from the queue no interrupt or bottom half
* touch it and we are (fairly 8-) ) safe. * 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; struct sk_buff *skb;
unsigned long flags;
save_flags(flags); cli();
rose_remove_socket(sk);
rose_stop_heartbeat(sk); rose_stop_heartbeat(sk);
rose_stop_idletimer(sk); rose_stop_idletimer(sk);
rose_stop_timer(sk); rose_stop_timer(sk);
rose_remove_socket(sk);
rose_clear_queues(sk); /* Flush the queues */ rose_clear_queues(sk); /* Flush the queues */
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { 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 ...@@ -405,8 +383,6 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time
} else { } else {
rose_free_sock(sk); rose_free_sock(sk);
} }
restore_flags(flags);
} }
/* /*
...@@ -647,7 +623,6 @@ static int rose_release(struct socket *sock) ...@@ -647,7 +623,6 @@ static int rose_release(struct socket *sock)
rose = rose_sk(sk); rose = rose_sk(sk);
switch (rose->state) { switch (rose->state) {
case ROSE_STATE_0: case ROSE_STATE_0:
rose_disconnect(sk, 0, -1, -1); rose_disconnect(sk, 0, -1, -1);
rose_destroy_socket(sk); rose_destroy_socket(sk);
...@@ -842,71 +817,90 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le ...@@ -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)) if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
return -EINPROGRESS; 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) { if (sk->state == TCP_SYN_SENT) {
interruptible_sleep_on(sk->sleep); struct task_struct *tsk = current;
if (signal_pending(current)) { DECLARE_WAITQUEUE(wait, tsk);
sti();
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; return -ERESTARTSYS;
} }
current->state = TASK_RUNNING;
remove_wait_queue(sk->sleep, &wait);
} }
if (sk->state != TCP_ESTABLISHED) { if (sk->state != TCP_ESTABLISHED) {
sti();
sock->state = SS_UNCONNECTED; sock->state = SS_UNCONNECTED;
return sock_error(sk); /* Always set at this point */ return sock_error(sk); /* Always set at this point */
} }
sock->state = SS_CONNECTED; sock->state = SS_CONNECTED;
sti();
return 0; return 0;
} }
static int rose_accept(struct socket *sock, struct socket *newsock, int flags) static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
{ {
struct sock *sk; struct task_struct *tsk = current;
struct sock *newsk; DECLARE_WAITQUEUE(wait, tsk);
struct sk_buff *skb; struct sk_buff *skb;
struct sock *newsk;
struct sock *sk;
int err = 0;
if ((sk = sock->sk) == NULL) if ((sk = sock->sk) == NULL)
return -EINVAL; return -EINVAL;
if (sk->type != SOCK_SEQPACKET) lock_sock(sk);
return -EOPNOTSUPP; if (sk->type != SOCK_SEQPACKET) {
err = -EOPNOTSUPP;
goto out;
}
if (sk->state != TCP_LISTEN) if (sk->state != TCP_LISTEN) {
return -EINVAL; err = -EINVAL;
goto out;
}
/* /*
* The write queue this time is holding sockets ready to use * The write queue this time is holding sockets ready to use
* hooked into the SABM we saved * hooked into the SABM we saved
*/ */
do { add_wait_queue(sk->sleep, &wait);
cli(); for (;;) {
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { skb = skb_dequeue(&sk->receive_queue);
if (flags & O_NONBLOCK) { if (skb)
sti(); break;
current->state = TASK_INTERRUPTIBLE;
release_sock(sk);
if (flags & O_NONBLOCK)
return -EWOULDBLOCK; return -EWOULDBLOCK;
if (!signal_pending(tsk)) {
schedule();
lock_sock(sk);
continue;
} }
interruptible_sleep_on(sk->sleep);
if (signal_pending(current)) {
sti();
return -ERESTARTSYS; return -ERESTARTSYS;
} }
} current->state = TASK_RUNNING;
} while (skb == NULL); remove_wait_queue(sk->sleep, &wait);
newsk = skb->sk; newsk = skb->sk;
newsk->pair = NULL; newsk->pair = NULL;
newsk->socket = newsock; newsk->socket = newsock;
newsk->sleep = &newsock->wait; newsk->sleep = &newsock->wait;
sti();
/* Now attach up the new socket */ /* Now attach up the new socket */
skb->sk = NULL; skb->sk = NULL;
...@@ -914,7 +908,10 @@ static int rose_accept(struct socket *sock, struct socket *newsock, int flags) ...@@ -914,7 +908,10 @@ static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
sk->ack_backlog--; sk->ack_backlog--;
newsock->sk = newsk; newsock->sk = newsk;
return 0; out:
release_sock(sk);
return err;
} }
static int rose_getname(struct socket *sock, struct sockaddr *uaddr, static int rose_getname(struct socket *sock, struct sockaddr *uaddr,
...@@ -1304,7 +1301,8 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -1304,7 +1301,8 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCADDRT: case SIOCADDRT:
case SIOCDELRT: case SIOCDELRT:
case SIOCRSCLRRT: case SIOCRSCLRRT:
if (!capable(CAP_NET_ADMIN)) return -EPERM; if (!capable(CAP_NET_ADMIN))
return -EPERM;
return rose_rt_ioctl(cmd, (void *)arg); return rose_rt_ioctl(cmd, (void *)arg);
case SIOCRSGCAUSE: { case SIOCRSGCAUSE: {
...@@ -1353,7 +1351,6 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -1353,7 +1351,6 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return dev_ioctl(cmd, (void *)arg); return dev_ioctl(cmd, (void *)arg);
} }
/*NOTREACHED*/
return 0; return 0;
} }
...@@ -1366,7 +1363,7 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1366,7 +1363,7 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length)
off_t pos = 0; off_t pos = 0;
off_t begin = 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"); 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");
...@@ -1418,15 +1415,14 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1418,15 +1415,14 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length)
if (pos > offset + length) if (pos > offset + length)
break; break;
} }
spin_unlock_bh(&rose_list_lock);
sti();
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
if (len > length) len = length; if (len > length) len = length;
return(len); return len;
} }
static struct net_proto_family rose_family_ops = { static struct net_proto_family rose_family_ops = {
...@@ -1434,7 +1430,7 @@ static struct net_proto_family rose_family_ops = { ...@@ -1434,7 +1430,7 @@ static struct net_proto_family rose_family_ops = {
.create = rose_create, .create = rose_create,
}; };
static struct proto_ops SOCKOPS_WRAPPED(rose_proto_ops) = { static struct proto_ops rose_proto_ops = {
.family = PF_ROSE, .family = PF_ROSE,
.release = rose_release, .release = rose_release,
...@@ -1455,11 +1451,8 @@ static struct proto_ops SOCKOPS_WRAPPED(rose_proto_ops) = { ...@@ -1455,11 +1451,8 @@ static struct proto_ops SOCKOPS_WRAPPED(rose_proto_ops) = {
.sendpage = sock_no_sendpage, .sendpage = sock_no_sendpage,
}; };
#include <linux/smp_lock.h>
SOCKOPS_WRAP(rose_proto, PF_ROSE);
static struct notifier_block rose_dev_notifier = { static struct notifier_block rose_dev_notifier = {
.notifier_call =rose_device_event, .notifier_call = rose_device_event,
}; };
static struct net_device *dev_rose; static struct net_device *dev_rose;
...@@ -1555,5 +1548,5 @@ static void __exit rose_exit(void) ...@@ -1555,5 +1548,5 @@ static void __exit rose_exit(void)
kfree(dev_rose); kfree(dev_rose);
} }
module_exit(rose_exit);
module_exit(rose_exit);
/* /*
* 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.
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from nr_dev.c.
* Hans(PE1AYX) Fixed interface to IP layer.
*/ */
#include <linux/config.h> #include <linux/config.h>
#define __NO_VERSION__ #define __NO_VERSION__
#include <linux/module.h> #include <linux/module.h>
...@@ -29,7 +21,7 @@ ...@@ -29,7 +21,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/in.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/system.h>
#include <asm/io.h> #include <asm/io.h>
......
/* /*
* 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: * Most of this code is based on the SDL diagrams published in the 7th ARRL
* This module is free software; you can redistribute it and/or * Computer Networking Conference papers. The diagrams have mistakes in them,
* modify it under the terms of the GNU General Public License * but are mostly correct. Before you modify the code could you read the SDL
* as published by the Free Software Foundation; either version * diagrams as the code is not obvious and probably very easy to break.
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -55,7 +44,6 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety ...@@ -55,7 +44,6 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_cb *rose = rose_sk(sk); rose_cb *rose = rose_sk(sk);
switch (frametype) { switch (frametype) {
case ROSE_CALL_ACCEPTED: case ROSE_CALL_ACCEPTED:
rose_stop_timer(sk); rose_stop_timer(sk);
rose_start_idletimer(sk); rose_start_idletimer(sk);
...@@ -93,7 +81,6 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety ...@@ -93,7 +81,6 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_cb *rose = rose_sk(sk); rose_cb *rose = rose_sk(sk);
switch (frametype) { switch (frametype) {
case ROSE_CLEAR_REQUEST: case ROSE_CLEAR_REQUEST:
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
rose_disconnect(sk, 0, skb->data[3], skb->data[4]); rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
...@@ -123,7 +110,6 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety ...@@ -123,7 +110,6 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
int queued = 0; int queued = 0;
switch (frametype) { switch (frametype) {
case ROSE_RESET_REQUEST: case ROSE_RESET_REQUEST:
rose_stop_timer(sk); rose_stop_timer(sk);
rose_start_idletimer(sk); rose_start_idletimer(sk);
...@@ -232,7 +218,6 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety ...@@ -232,7 +218,6 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_cb *rose = rose_sk(sk); rose_cb *rose = rose_sk(sk);
switch (frametype) { switch (frametype) {
case ROSE_RESET_REQUEST: case ROSE_RESET_REQUEST:
rose_write_internal(sk, ROSE_RESET_CONFIRMATION); rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
case ROSE_RESET_CONFIRMATION: case ROSE_RESET_CONFIRMATION:
......
/* /*
* 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.
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from rose_timer.c
* ROSE 003 Jonathan(G4KLX) New timer architecture.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
......
/* /*
* 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
* This code REQUIRES 2.1.15 or higher/ NET3.038 * the Free Software Foundation; either version 2 of the License, or
* * (at your option) any later version.
* 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.
* *
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/timer.h> #include <linux/timer.h>
......
/* /*
* 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.
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from nr_out.c
* ROSE 003 Jonathan(G4KLX) New timer architecture.
* Removed M bit processing.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
......
/* /*
* 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)
* * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net)
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -51,8 +39,11 @@ ...@@ -51,8 +39,11 @@
static unsigned int rose_neigh_no = 1; static unsigned int rose_neigh_no = 1;
static struct rose_node *rose_node_list; 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 struct rose_neigh *rose_neigh_list;
static spinlock_t rose_neigh_list_lock = SPIN_LOCK_UNLOCKED;
static struct rose_route *rose_route_list; static struct rose_route *rose_route_list;
static spinlock_t rose_route_list_lock = SPIN_LOCK_UNLOCKED;
struct rose_neigh *rose_loopback_neigh; struct rose_neigh *rose_loopback_neigh;
...@@ -62,27 +53,44 @@ static void rose_remove_neigh(struct rose_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 * Add a new route to a node, and in the process add the node and the
* neighbour if it is new. * 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_node *rose_node, *rose_tmpn, *rose_tmpp;
struct rose_neigh *rose_neigh; struct rose_neigh *rose_neigh;
unsigned long flags; int i, res = 0;
int i;
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) rose_node = rose_node_list;
if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0)) while (rose_node != NULL) {
if ((rose_node->mask == rose_route->mask) &&
(rosecmpm(&rose_route->address, &rose_node->address,
rose_route->mask) == 0))
break; break;
rose_node = rose_node->next;
}
if (rose_node != NULL && rose_node->loopback) if (rose_node != NULL && rose_node->loopback) {
return -EINVAL; res = -EINVAL;
goto out;
}
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) rose_neigh = rose_neigh_list;
if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) while (rose_neigh != NULL) {
if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0
&& rose_neigh->dev == dev)
break; break;
rose_neigh = rose_neigh->next;
}
if (rose_neigh == NULL) { if (rose_neigh == NULL) {
if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL) rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC);
return -ENOMEM; if (rose_neigh == NULL) {
res = -ENOMEM;
goto out;
}
rose_neigh->callsign = rose_route->neighbour; rose_neigh->callsign = rose_route->neighbour;
rose_neigh->digipeat = NULL; rose_neigh->digipeat = NULL;
...@@ -103,22 +111,22 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device ...@@ -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_route->ndigis != 0) {
if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
kfree(rose_neigh); kfree(rose_neigh);
return -ENOMEM; res = -ENOMEM;
goto out;
} }
rose_neigh->digipeat->ndigi = rose_route->ndigis; rose_neigh->digipeat->ndigi = rose_route->ndigis;
rose_neigh->digipeat->lastrepeat = -1; rose_neigh->digipeat->lastrepeat = -1;
for (i = 0; i < rose_route->ndigis; i++) { 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; rose_neigh->digipeat->repeated[i] = 0;
} }
} }
save_flags(flags); cli();
rose_neigh->next = rose_neigh_list; rose_neigh->next = rose_neigh_list;
rose_neigh_list = rose_neigh; 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 ...@@ -142,8 +150,11 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device
} }
/* create new node */ /* create new node */
if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC);
return -ENOMEM; if (rose_node == NULL) {
res = -ENOMEM;
goto out;
}
rose_node->address = rose_route->address; rose_node->address = rose_route->address;
rose_node->mask = rose_route->mask; rose_node->mask = rose_route->mask;
...@@ -151,8 +162,6 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device ...@@ -151,8 +162,6 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device
rose_node->loopback = 0; rose_node->loopback = 0;
rose_node->neighbour[0] = rose_neigh; rose_node->neighbour[0] = rose_neigh;
save_flags(flags); cli();
if (rose_tmpn == NULL) { if (rose_tmpn == NULL) {
if (rose_tmpp == NULL) { /* Empty list */ if (rose_tmpp == NULL) { /* Empty list */
rose_node_list = rose_node; rose_node_list = rose_node;
...@@ -170,12 +179,9 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device ...@@ -170,12 +179,9 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device
rose_node->next = rose_tmpn; rose_node->next = rose_tmpn;
} }
} }
restore_flags(flags);
rose_neigh->count++; rose_neigh->count++;
return 0; goto out;
} }
/* We have space, slot it in */ /* We have space, slot it in */
...@@ -185,20 +191,23 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device ...@@ -185,20 +191,23 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device
rose_neigh->count++; 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) static void rose_remove_node(struct rose_node *rose_node)
{ {
struct rose_node *s; 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) { if ((s = rose_node_list) == rose_node) {
rose_node_list = rose_node->next; rose_node_list = rose_node->next;
restore_flags(flags);
kfree(rose_node); kfree(rose_node);
return; return;
} }
...@@ -206,32 +215,31 @@ static void rose_remove_node(struct rose_node *rose_node) ...@@ -206,32 +215,31 @@ static void rose_remove_node(struct rose_node *rose_node)
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == rose_node) { if (s->next == rose_node) {
s->next = rose_node->next; s->next = rose_node->next;
restore_flags(flags);
kfree(rose_node); kfree(rose_node);
return; return;
} }
s = s->next; s = s->next;
} }
restore_flags(flags);
} }
/*
* Caller is holding rose_neigh_list_lock.
*/
static void rose_remove_neigh(struct rose_neigh *rose_neigh) static void rose_remove_neigh(struct rose_neigh *rose_neigh)
{ {
struct rose_neigh *s; struct rose_neigh *s;
unsigned long flags;
rose_stop_ftimer(rose_neigh); rose_stop_ftimer(rose_neigh);
rose_stop_t0timer(rose_neigh); rose_stop_t0timer(rose_neigh);
skb_queue_purge(&rose_neigh->queue); skb_queue_purge(&rose_neigh->queue);
save_flags(flags); cli(); spin_lock_bh(&rose_neigh_list_lock);
if ((s = rose_neigh_list) == rose_neigh) { if ((s = rose_neigh_list) == rose_neigh) {
rose_neigh_list = rose_neigh->next; rose_neigh_list = rose_neigh->next;
restore_flags(flags); spin_unlock_bh(&rose_neigh_list_lock);
if (rose_neigh->digipeat != NULL) if (rose_neigh->digipeat != NULL)
kfree(rose_neigh->digipeat); kfree(rose_neigh->digipeat);
kfree(rose_neigh); kfree(rose_neigh);
...@@ -241,7 +249,7 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) ...@@ -241,7 +249,7 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == rose_neigh) { if (s->next == rose_neigh) {
s->next = rose_neigh->next; s->next = rose_neigh->next;
restore_flags(flags); spin_unlock_bh(&rose_neigh_list_lock);
if (rose_neigh->digipeat != NULL) if (rose_neigh->digipeat != NULL)
kfree(rose_neigh->digipeat); kfree(rose_neigh->digipeat);
kfree(rose_neigh); kfree(rose_neigh);
...@@ -250,14 +258,15 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) ...@@ -250,14 +258,15 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
s = s->next; s = s->next;
} }
spin_unlock_bh(&rose_neigh_list_lock);
restore_flags(flags);
} }
/*
* Caller is holding rose_route_list_lock.
*/
static void rose_remove_route(struct rose_route *rose_route) static void rose_remove_route(struct rose_route *rose_route)
{ {
struct rose_route *s; struct rose_route *s;
unsigned long flags;
if (rose_route->neigh1 != NULL) if (rose_route->neigh1 != NULL)
rose_route->neigh1->use--; rose_route->neigh1->use--;
...@@ -265,11 +274,8 @@ static void rose_remove_route(struct rose_route *rose_route) ...@@ -265,11 +274,8 @@ static void rose_remove_route(struct rose_route *rose_route)
if (rose_route->neigh2 != NULL) if (rose_route->neigh2 != NULL)
rose_route->neigh2->use--; rose_route->neigh2->use--;
save_flags(flags); cli();
if ((s = rose_route_list) == rose_route) { if ((s = rose_route_list) == rose_route) {
rose_route_list = rose_route->next; rose_route_list = rose_route->next;
restore_flags(flags);
kfree(rose_route); kfree(rose_route);
return; return;
} }
...@@ -277,40 +283,54 @@ static void rose_remove_route(struct rose_route *rose_route) ...@@ -277,40 +283,54 @@ static void rose_remove_route(struct rose_route *rose_route)
while (s != NULL && s->next != NULL) { while (s != NULL && s->next != NULL) {
if (s->next == rose_route) { if (s->next == rose_route) {
s->next = rose_route->next; s->next = rose_route->next;
restore_flags(flags);
kfree(rose_route); kfree(rose_route);
return; return;
} }
s = s->next; s = s->next;
} }
restore_flags(flags);
} }
/* /*
* "Delete" a node. Strictly speaking remove a route to a node. The node * "Delete" a node. Strictly speaking remove a route to a node. The node
* is only deleted if no routes are left to it. * 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_node *rose_node;
struct rose_neigh *rose_neigh; 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) spin_lock_bh(&rose_node_list_lock);
if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0)) spin_lock_bh(&rose_neigh_list_lock);
break;
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) rose_neigh = rose_neigh_list;
if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) while (rose_neigh != NULL) {
if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0
&& rose_neigh->dev == dev)
break; 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++) { for (i = 0; i < rose_node->count; i++) {
if (rose_node->neighbour[i] == rose_neigh) { if (rose_node->neighbour[i] == rose_neigh) {
...@@ -326,19 +346,25 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device ...@@ -326,19 +346,25 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device
} else { } else {
switch (i) { switch (i) {
case 0: case 0:
rose_node->neighbour[0] = rose_node->neighbour[1]; rose_node->neighbour[0] =
rose_node->neighbour[1];
case 1: case 1:
rose_node->neighbour[1] = rose_node->neighbour[2]; rose_node->neighbour[1] =
rose_node->neighbour[2];
case 2: case 2:
break; break;
} }
} }
goto out;
return 0;
} }
} }
err = -EINVAL;
return -EINVAL; out:
spin_unlock_bh(&rose_neigh_list_lock);
spin_unlock_bh(&rose_node_list_lock);
return err;
} }
/* /*
...@@ -346,8 +372,6 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device ...@@ -346,8 +372,6 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device
*/ */
int rose_add_loopback_neigh(void) int rose_add_loopback_neigh(void)
{ {
unsigned long flags;
if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL) if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -367,10 +391,10 @@ int rose_add_loopback_neigh(void) ...@@ -367,10 +391,10 @@ int rose_add_loopback_neigh(void)
init_timer(&rose_loopback_neigh->ftimer); init_timer(&rose_loopback_neigh->ftimer);
init_timer(&rose_loopback_neigh->t0timer); 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_loopback_neigh->next = rose_neigh_list;
rose_neigh_list = rose_loopback_neigh; rose_neigh_list = rose_loopback_neigh;
restore_flags(flags); spin_unlock_bh(&rose_neigh_list_lock);
return 0; return 0;
} }
...@@ -381,16 +405,26 @@ int rose_add_loopback_neigh(void) ...@@ -381,16 +405,26 @@ int rose_add_loopback_neigh(void)
int rose_add_loopback_node(rose_address *address) int rose_add_loopback_node(rose_address *address)
{ {
struct rose_node *rose_node; 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) spin_lock_bh(&rose_node_list_lock);
if ((rose_node->mask == 10) && (rosecmpm(address, &rose_node->address, 10) == 0) && rose_node->loopback)
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; break;
rose_node = rose_node->next;
}
if (rose_node != NULL) return 0; if (rose_node != NULL)
goto out;
if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL) {
return -ENOMEM; err = -ENOMEM;
goto out;
}
rose_node->address = *address; rose_node->address = *address;
rose_node->mask = 10; rose_node->mask = 10;
...@@ -399,13 +433,14 @@ int rose_add_loopback_node(rose_address *address) ...@@ -399,13 +433,14 @@ int rose_add_loopback_node(rose_address *address)
rose_node->neighbour[0] = rose_loopback_neigh; rose_node->neighbour[0] = rose_loopback_neigh;
/* Insert at the head of list. Address is always mask=10 */ /* Insert at the head of list. Address is always mask=10 */
save_flags(flags); cli();
rose_node->next = rose_node_list; rose_node->next = rose_node_list;
rose_node_list = rose_node; rose_node_list = rose_node;
restore_flags(flags);
rose_loopback_neigh->count++; rose_loopback_neigh->count++;
out:
spin_unlock_bh(&rose_node_list_lock);
return 0; return 0;
} }
...@@ -416,15 +451,26 @@ void rose_del_loopback_node(rose_address *address) ...@@ -416,15 +451,26 @@ void rose_del_loopback_node(rose_address *address)
{ {
struct rose_node *rose_node; struct rose_node *rose_node;
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) spin_lock_bh(&rose_node_list_lock);
if ((rose_node->mask == 10) && (rosecmpm(address, &rose_node->address, 10) == 0) && rose_node->loopback)
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; break;
rose_node = rose_node->next;
}
if (rose_node == NULL) return; if (rose_node == NULL)
goto out;
rose_remove_node(rose_node); rose_remove_node(rose_node);
rose_loopback_neigh->count--; rose_loopback_neigh->count--;
out:
spin_unlock_bh(&rose_node_list_lock);
} }
/* /*
...@@ -432,15 +478,20 @@ void rose_del_loopback_node(rose_address *address) ...@@ -432,15 +478,20 @@ void rose_del_loopback_node(rose_address *address)
*/ */
void rose_rt_device_down(struct net_device *dev) 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; struct rose_node *t, *rose_node;
int i; 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) { while (rose_neigh != NULL) {
s = rose_neigh; s = rose_neigh;
rose_neigh = rose_neigh->next; rose_neigh = rose_neigh->next;
if (s->dev == dev) { if (s->dev != dev)
continue;
rose_node = rose_node_list; rose_node = rose_node_list;
while (rose_node != NULL) { while (rose_node != NULL) {
...@@ -448,7 +499,9 @@ void rose_rt_device_down(struct net_device *dev) ...@@ -448,7 +499,9 @@ void rose_rt_device_down(struct net_device *dev)
rose_node = rose_node->next; rose_node = rose_node->next;
for (i = 0; i < t->count; i++) { for (i = 0; i < t->count; i++) {
if (t->neighbour[i] == s) { if (t->neighbour[i] != s)
continue;
t->count--; t->count--;
switch (i) { switch (i) {
...@@ -460,7 +513,6 @@ void rose_rt_device_down(struct net_device *dev) ...@@ -460,7 +513,6 @@ void rose_rt_device_down(struct net_device *dev)
break; break;
} }
} }
}
if (t->count <= 0) if (t->count <= 0)
rose_remove_node(t); rose_remove_node(t);
...@@ -468,16 +520,20 @@ void rose_rt_device_down(struct net_device *dev) ...@@ -468,16 +520,20 @@ void rose_rt_device_down(struct net_device *dev)
rose_remove_neigh(s); 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. * A device has been removed. Remove its links.
*/ */
void rose_route_device_down(struct net_device *dev) 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) { while (rose_route != NULL) {
s = rose_route; s = rose_route;
rose_route = rose_route->next; rose_route = rose_route->next;
...@@ -485,7 +541,9 @@ void rose_route_device_down(struct net_device *dev) ...@@ -485,7 +541,9 @@ void rose_route_device_down(struct net_device *dev)
if (s->neigh1->dev == dev || s->neigh2->dev == dev) if (s->neigh1->dev == dev || s->neigh2->dev == dev)
rose_remove_route(s); rose_remove_route(s);
} }
spin_unlock_bh(&rose_route_list_lock);
} }
#endif
/* /*
* Clear all nodes and neighbours out, except for neighbours with * Clear all nodes and neighbours out, except for neighbours with
...@@ -494,8 +552,14 @@ void rose_route_device_down(struct net_device *dev) ...@@ -494,8 +552,14 @@ void rose_route_device_down(struct net_device *dev)
*/ */
static int rose_clear_routes(void) static int rose_clear_routes(void)
{ {
struct rose_neigh *s, *rose_neigh = rose_neigh_list; struct rose_neigh *s, *rose_neigh;
struct rose_node *t, *rose_node = rose_node_list; 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) { while (rose_node != NULL) {
t = rose_node; t = rose_node;
...@@ -514,6 +578,9 @@ static int rose_clear_routes(void) ...@@ -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; return 0;
} }
...@@ -603,18 +670,22 @@ struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neig ...@@ -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. * 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; struct rose_node *node;
int failed = 0; int failed = 0;
int i; int i;
spin_lock_bh(&rose_node_list_lock);
for (node = rose_node_list; node != NULL; node = node->next) { for (node = rose_node_list; node != NULL; node = node->next) {
if (rosecmpm(addr, &node->address, node->mask) == 0) { if (rosecmpm(addr, &node->address, node->mask) == 0) {
for (i = 0; i < node->count; i++) { for (i = 0; i < node->count; i++) {
if (!rose_ftimer_running(node->neighbour[i])) { if (!rose_ftimer_running(node->neighbour[i])) {
return node->neighbour[i]; } res = node->neighbour[i];
else goto out;
} else
failed = 1; failed = 1;
} }
break; break;
...@@ -629,7 +700,10 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsi ...@@ -629,7 +700,10 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsi
*diagnostic = 0; *diagnostic = 0;
} }
return NULL; out:
spin_unlock_bh(&rose_node_list_lock);
return res;
} }
/* /*
...@@ -642,7 +716,6 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) ...@@ -642,7 +716,6 @@ int rose_rt_ioctl(unsigned int cmd, void *arg)
int err; int err;
switch (cmd) { switch (cmd) {
case SIOCADDRT: case SIOCADDRT:
if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)))
return -EFAULT; return -EFAULT;
...@@ -668,7 +741,6 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) ...@@ -668,7 +741,6 @@ int rose_rt_ioctl(unsigned int cmd, void *arg)
dev_put(dev); dev_put(dev);
return err; return err;
case SIOCRSCLRRT: case SIOCRSCLRRT:
return rose_clear_routes(); return rose_clear_routes();
...@@ -690,6 +762,8 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) ...@@ -690,6 +762,8 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
skb_queue_purge(&rose_neigh->queue); skb_queue_purge(&rose_neigh->queue);
spin_lock_bh(&rose_route_list_lock);
rose_route = rose_route_list; rose_route = rose_route_list;
while (rose_route != NULL) { while (rose_route != NULL) {
...@@ -716,6 +790,7 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) ...@@ -716,6 +790,7 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
rose_route = rose_route->next; 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) ...@@ -727,16 +802,21 @@ void rose_link_failed(ax25_cb *ax25, int reason)
{ {
struct rose_neigh *rose_neigh; 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) if (rose_neigh->ax25 == ax25)
break; break;
rose_neigh = rose_neigh->next;
}
if (rose_neigh == NULL) return; if (rose_neigh != NULL) {
rose_neigh->ax25 = NULL; rose_neigh->ax25 = NULL;
rose_del_route_by_neigh(rose_neigh); rose_del_route_by_neigh(rose_neigh);
rose_kill_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) ...@@ -769,12 +849,11 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
unsigned int lci, new_lci; unsigned int lci, new_lci;
unsigned char cause, diagnostic; unsigned char cause, diagnostic;
struct net_device *dev; struct net_device *dev;
unsigned long flags; int len, res = 0;
int len;
#if 0 #if 0
if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
return 0; return res;
#endif #endif
frametype = skb->data[2]; frametype = skb->data[2];
...@@ -782,13 +861,22 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ...@@ -782,13 +861,22 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
src_addr = (rose_address *)(skb->data + 9); src_addr = (rose_address *)(skb->data + 9);
dest_addr = (rose_address *)(skb->data + 4); dest_addr = (rose_address *)(skb->data + 4);
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) spin_lock_bh(&rose_node_list_lock);
if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev) 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; break;
rose_neigh = rose_neigh->next;
}
if (rose_neigh == NULL) { if (rose_neigh == NULL) {
printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr)); printk("rose_route : unknown neighbour or device %s\n",
return 0; ax2asc(&ax25->dest_addr));
goto out;
} }
/* /*
...@@ -802,7 +890,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ...@@ -802,7 +890,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
*/ */
if (lci == 0) { if (lci == 0) {
rose_link_rx_restart(skb, rose_neigh, frametype); 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) ...@@ -828,7 +916,8 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
} }
else { else {
skb->h.raw = skb->data; 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) ...@@ -837,21 +926,23 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
*/ */
if (frametype == ROSE_CALL_REQUEST) if (frametype == ROSE_CALL_REQUEST)
if ((dev = rose_dev_get(dest_addr)) != NULL) { 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); dev_put(dev);
return err; goto out;
} }
if (!sysctl_rose_routing_control) { if (!sysctl_rose_routing_control) {
rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); 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. * 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) { rose_route = rose_route_list;
if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) { while (rose_route != NULL) {
if (rose_route->lci1 == lci &&
rose_route->neigh1 == rose_neigh) {
if (frametype == ROSE_CALL_REQUEST) { if (frametype == ROSE_CALL_REQUEST) {
/* F6FBB - Remove an existing unused route */ /* F6FBB - Remove an existing unused route */
rose_remove_route(rose_route); rose_remove_route(rose_route);
...@@ -863,14 +954,16 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ...@@ -863,14 +954,16 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
rose_transmit_link(skb, rose_route->neigh2); rose_transmit_link(skb, rose_route->neigh2);
if (frametype == ROSE_CLEAR_CONFIRMATION) if (frametype == ROSE_CLEAR_CONFIRMATION)
rose_remove_route(rose_route); rose_remove_route(rose_route);
return 1; res = 1;
goto out;
} else { } else {
if (frametype == ROSE_CLEAR_CONFIRMATION) if (frametype == ROSE_CLEAR_CONFIRMATION)
rose_remove_route(rose_route); 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) { if (frametype == ROSE_CALL_REQUEST) {
/* F6FBB - Remove an existing unused route */ /* F6FBB - Remove an existing unused route */
rose_remove_route(rose_route); rose_remove_route(rose_route);
...@@ -882,13 +975,15 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ...@@ -882,13 +975,15 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
rose_transmit_link(skb, rose_route->neigh1); rose_transmit_link(skb, rose_route->neigh1);
if (frametype == ROSE_CLEAR_CONFIRMATION) if (frametype == ROSE_CLEAR_CONFIRMATION)
rose_remove_route(rose_route); rose_remove_route(rose_route);
return 1; res = 1;
goto out;
} else { } else {
if (frametype == ROSE_CLEAR_CONFIRMATION) if (frametype == ROSE_CLEAR_CONFIRMATION)
rose_remove_route(rose_route); rose_remove_route(rose_route);
return 0; goto out;
} }
} }
rose_route = rose_route->next;
} }
/* /*
...@@ -906,35 +1001,37 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ...@@ -906,35 +1001,37 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { if (!rose_parse_facilities(skb->data + len + 4, &facilities)) {
rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
return 0; goto out;
} }
/* /*
* Check for routing loops. * 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 && if (rose_route->rand == facilities.rand &&
rosecmp(src_addr, &rose_route->src_addr) == 0 && rosecmp(src_addr, &rose_route->src_addr) == 0 &&
ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 && ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 &&
ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) {
rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); 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) { if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) {
rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic);
return 0; goto out;
} }
if ((new_lci = rose_new_lci(new_neigh)) == 0) { if ((new_lci = rose_new_lci(new_neigh)) == 0) {
rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); 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) { if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120);
return 0; goto out;
} }
rose_route->lci1 = lci; rose_route->lci1 = lci;
...@@ -950,18 +1047,22 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ...@@ -950,18 +1047,22 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
rose_route->neigh1->use++; rose_route->neigh1->use++;
rose_route->neigh2->use++; rose_route->neigh2->use++;
save_flags(flags); cli();
rose_route->next = rose_route_list; rose_route->next = rose_route_list;
rose_route_list = rose_route; rose_route_list = rose_route;
restore_flags(flags);
skb->data[0] &= 0xF0; skb->data[0] &= 0xF0;
skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; skb->data[1] = (rose_route->lci2 >> 0) & 0xFF;
rose_transmit_link(skb, rose_route->neigh2); 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) 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) ...@@ -972,7 +1073,7 @@ int rose_nodes_get_info(char *buffer, char **start, off_t offset, int length)
off_t begin = 0; off_t begin = 0;
int i; int i;
cli(); spin_lock_bh(&rose_neigh_list_lock);
len += sprintf(buffer, "address mask n neigh neigh neigh\n"); len += sprintf(buffer, "address mask n neigh neigh neigh\n");
...@@ -1004,13 +1105,13 @@ int rose_nodes_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1004,13 +1105,13 @@ int rose_nodes_get_info(char *buffer, char **start, off_t offset, int length)
if (pos > offset + length) if (pos > offset + length)
break; break;
} }
spin_unlock_bh(&rose_neigh_list_lock);
sti();
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
if (len > length) len = length; if (len > length)
len = length;
return len; return len;
} }
...@@ -1023,7 +1124,7 @@ 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; off_t begin = 0;
int i; int i;
cli(); spin_lock_bh(&rose_neigh_list_lock);
len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n"); len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n");
...@@ -1059,12 +1160,13 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1059,12 +1160,13 @@ 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); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
if (len > length) len = length; if (len > length)
len = length;
return len; return len;
} }
...@@ -1076,7 +1178,7 @@ 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 pos = 0;
off_t begin = 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"); len += sprintf(buffer, "lci address callsign neigh <-> lci address callsign neigh\n");
...@@ -1112,12 +1214,13 @@ int rose_routes_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1112,12 +1214,13 @@ int rose_routes_get_info(char *buffer, char **start, off_t offset, int length)
break; break;
} }
sti(); spin_unlock_bh(&rose_route_list_lock);
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
if (len > length) len = length; if (len > length)
len = length;
return len; return len;
} }
......
/* /*
* 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.
*
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -145,7 +136,6 @@ void rose_write_internal(struct sock *sk, int frametype) ...@@ -145,7 +136,6 @@ void rose_write_internal(struct sock *sk, int frametype)
lci2 = (rose->lci >> 0) & 0xFF; lci2 = (rose->lci >> 0) & 0xFF;
switch (frametype) { switch (frametype) {
case ROSE_CALL_REQUEST: case ROSE_CALL_REQUEST:
*dptr++ = ROSE_GFI | lci1; *dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2; *dptr++ = lci2;
...@@ -363,7 +353,8 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac ...@@ -363,7 +353,8 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac
return n; 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; int facilities_len, len;
...@@ -396,8 +387,8 @@ int rose_parse_facilities(unsigned char *p, struct rose_facilities_struct *facil ...@@ -396,8 +387,8 @@ int rose_parse_facilities(unsigned char *p, struct rose_facilities_struct *facil
p++; p++;
break; break;
} }
} } else
else break; /* Error in facilities format */ break; /* Error in facilities format */
} }
return 1; return 1;
......
/* /*
* 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)
* * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org)
* 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.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
...@@ -139,8 +131,8 @@ static void rose_heartbeat_expiry(unsigned long param) ...@@ -139,8 +131,8 @@ static void rose_heartbeat_expiry(unsigned long param)
struct sock *sk = (struct sock *)param; struct sock *sk = (struct sock *)param;
rose_cb *rose = rose_sk(sk); rose_cb *rose = rose_sk(sk);
bh_lock_sock(sk);
switch (rose->state) { switch (rose->state) {
case ROSE_STATE_0: case ROSE_STATE_0:
/* Magic here: If we listen() and a new link dies before it /* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */ is accepted() it isn't 'dead' so doesn't get removed. */
...@@ -167,6 +159,7 @@ static void rose_heartbeat_expiry(unsigned long param) ...@@ -167,6 +159,7 @@ static void rose_heartbeat_expiry(unsigned long param)
} }
rose_start_heartbeat(sk); rose_start_heartbeat(sk);
bh_unlock_sock(sk);
} }
static void rose_timer_expiry(unsigned long param) static void rose_timer_expiry(unsigned long param)
...@@ -174,8 +167,8 @@ static void rose_timer_expiry(unsigned long param) ...@@ -174,8 +167,8 @@ static void rose_timer_expiry(unsigned long param)
struct sock *sk = (struct sock *)param; struct sock *sk = (struct sock *)param;
rose_cb *rose = rose_sk(sk); rose_cb *rose = rose_sk(sk);
bh_lock_sock(sk);
switch (rose->state) { switch (rose->state) {
case ROSE_STATE_1: /* T1 */ case ROSE_STATE_1: /* T1 */
case ROSE_STATE_4: /* T2 */ case ROSE_STATE_4: /* T2 */
rose_write_internal(sk, ROSE_CLEAR_REQUEST); rose_write_internal(sk, ROSE_CLEAR_REQUEST);
...@@ -195,12 +188,14 @@ static void rose_timer_expiry(unsigned long param) ...@@ -195,12 +188,14 @@ static void rose_timer_expiry(unsigned long param)
} }
break; break;
} }
bh_unlock_sock(sk);
} }
static void rose_idletimer_expiry(unsigned long param) static void rose_idletimer_expiry(unsigned long param)
{ {
struct sock *sk = (struct sock *)param; struct sock *sk = (struct sock *)param;
bh_lock_sock(sk);
rose_clear_queues(sk); rose_clear_queues(sk);
rose_write_internal(sk, ROSE_CLEAR_REQUEST); rose_write_internal(sk, ROSE_CLEAR_REQUEST);
...@@ -216,4 +211,5 @@ static void rose_idletimer_expiry(unsigned long param) ...@@ -216,4 +211,5 @@ static void rose_idletimer_expiry(unsigned long param)
sk->state_change(sk); sk->state_change(sk);
sk->dead = 1; sk->dead = 1;
bh_unlock_sock(sk);
} }
/* -*- 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. * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com)
* Added /proc/sys/net/rose directory entry (empty =) ). [MS]
*/ */
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/init.h> #include <linux/init.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment